Description
This paper serves as a whitepaper for feasible performance optimizations proposals which can be performed on ApiDOM monorepo. Paper is divided into sections (single top level section represents one monorepo package) and each of these sections is further divided into sub-sections describing the actual performance optimization proposal.
Paper also contains references to various revisions. Keep in mind that code can change between specific revision and master branch as time goes on. This can render certain performance optimization proposal invalid.
apidom
Transcluder
For every use of transclude function all the parent edges needs to be computed.
Single transclution
It is more optimal to use single traversal run for this usecase. As soon as search
element is found, it is transcluded with replace
element and traversal is immediately stopped.
Multi transclution
This is a usecase where multiple search
elements needs to be replaced by multiple replace
elements. Again it is more optimal to use traversal to replace/transclude the nodes.
Repeated transclution
This is more a corner case - we want to transclude one or multiple elements during different points in time on the same ApiDOM structure. It's possible to use current implementation of transclution to cover this usecase.
All the fore-mentioned usecases are currently embedded in one single implementation which makes it less then optimal.
apidom-ast
Parallel visiting
Parallel visiting or megring of the visitors is implemented in a way where all the "merged" visitors are iterated in every enter
or leave
visitor function and appropriate visitFn
is obtained and executed. This can be implemented in more performant way where static chain of visitor functions is constructed by merging the visitors before they're executed.
Parallel visiting (merging) is used in refractor layers in all the namespace packages.
apidom-ns-asyncapi-2-0
Refracting primitive fields
During refracting phase traversal is executed on every field of every Object Element or item of Array Element. This has potentially significant performance implications. We could device a more performant mechanism (let's call it visitor-shortcut) that would not run traversal on any field that we expect is going to be a primitive or generic element (String Element, Number Element, Object Element, etc...). Instead fields like those are going to be directly translated from generic form to semantic form by simple cloning.
Refracting primitive fields - utilize specification object
Currently every primitive field has it's own file even though it's using generic FallbackVisitor
. We can eliminate those files and attach visitors directly in specification objects. By this we will save a lot of code and file imports and lower the overall size of resulting UMD bundles.
Refractor plugins
If refractor plugins are utilized, they require additional full traversal after the refracting phase has finished. It would be convenient to run refractor plugins without additional full traversal. Running plugins inside refracting phase is not feasible as refraction transforms generic ApiDOM into semantic progressively. If we would run refractor plugins inside refracting phase it would mean that plugins will receive either generic ApiDOM elements or semantic ones depending on the state of refracting at the point of refractor plugin intercepting it. One feasible solution would be to fold our build-in defaults plugins into refracting phase directly and if there would be need for additional ad-hoc plugins, person that attaches them to refractor must expect that there will be performance implication.
apidom-ns-openapi-3-1
Refracting primitive fields
During refracting phase traversal is executed on every field of every Object Element or item of Array Element. This has potentially significant performance implications. We could device a more performant mechanism (let's call it visitor-shortcut) that would not run traversal on any field that we expect is going to be a primitive or generic element (String Element, Number Element, Object Element, etc...). Instead fields like those are going to be directly translated from generic form to semantic form by simple cloning.
Refracting primitive fields - utilize specification object
Currently every primitive field has it's own file even though it's using generic FallbackVisitor
. We can eliminate those files and attach visitors directly in specification objects. By this we will save a lot of code and file imports and lower the overall size of resulting UMD bundles.
Refractor plugins
If refractor plugins are utilized, they require additional full traversal after the refracting phase has finished. It would be convenient to run refractor plugins without additional full traversal. Running plugins inside refracting phase is not feasible as refraction transforms generic ApiDOM into semantic progressively. If we would run refractor plugins inside refracting phase it would mean that plugins will receive either generic ApiDOM elements or semantic ones depending on the state of refracting at the point of refractor plugin intercepting it. One feasible solution would be to fold our build-in defaults plugins into refracting phase directly and if there would be need for additional ad-hoc plugins, person that attaches them to refractor must expect that there will be performance implication.
apidom-parser
No feasible performance optimization are currently proposed for this package.
apidom-parser-adapter-json
Syntactical analysis
In order to parse source JSON string into generic ApiDOM following parsing phases are peformed:
- Lexical analysis (tree-sitter)
Source JSON string is parsed and tokenized. The result of these operations is a structure that technically categorizes as CST, but contains further APIs for AST like manipulation.
- Syntactical analysis
CST is transformed into JSON AST.
- Transform
During this phase JSON AST is transformed into generic ApiDOM.
We could possibly fold phase 2. and 3. into a single phase. By doing this we would avoid one full traversal and maintaining additional JSON AST structure in memory. Syntactical analysis phase
doesn't contain any additional logic that couldn't be folded easily into Transform
phase because JSON is very simple format to process.
apidom-parser-adapter-yaml-1-2
Syntactical analysis
In order to parse source YAML 1.2 string into generic ApiDOM following parsing phases are peformed:
- Lexical analysis (tree-sitter)
Source YAML string is parsed and tokenized. The result of these operations is a structure that technically categorizes as CST, but contains further APIs for AST like manipulation.
- Syntactical analysis
CST is transformed into YAML AST. These transformations includes resolving YAML anchors, processing YAML Schemas and creating surrogate AST nodes wherever they're missing.
- Transform
During this phase YAML AST is transformed into generic ApiDOM.
We could possibly fold phase 2. and 3. into a single phase. By doing this we would avoid one full traversal and maintaining additional YAML AST structure in memory. Unfortunately YAML 1.2 is pretty complex and folding the two phases would create complex code that would not be easy to maintain. IMHO in this case code readability and maintenance wins over performance. It is always possible to create additional YAML 1.2 adapter with folded phases in future.
apidom-parser-adapter-asyncapi-json-2-0
This is a hollow adapter composed of two main packages: apidom-parser-adapter-json and apidom-ns-asyncapi-2-0. All the performance optimizations that will be performed on these two main packages will inevitably increase performance of this adapter.
This adapter goes through following phases:
- Lexical analysis
- Syntactical analysis
- Transform
- Refracting
It's possible to create a super performant version of this adapter folding all the aforementioned phases into single one.
Transclution
We could avoid transclusion here completely or use one of the already optimized transcluder described in this paper.
apidom-parser-adapter-asyncapi-yaml-2-0
This is a hollow adapter composed of two main packages: apidom-parser-adapter-yaml-1-2 and apidom-ns-asyncapi-2-0. All the performance optimizations that will be performed on these two main packages will inevitably increase performance of this adapter.
This adapter goes through following phases:
- Lexical analysis
- Syntactical analysis
- Transform
- Refracting
It's possible to create a super performant version of this adapter folding all the aforementioned phases into single one.
Transclution
We could avoid transclusion here completely or use one of the already optimized transcluder described in this paper.
apidom-parser-adapter-openapi-json-3-1
This is a hollow adapter composed of two main packages: apidom-parser-adapter-json and apidom-ns-openapi-3-1. All the performance optimizations that will be performed on these two main packages will inevitably increase performance of this adapter.
This adapter goes through following phases:
- Lexical analysis
- Syntactical analysis
- Transform
- Refracting
It's possible to create a super performant version of this adapter folding all the aforementioned phases into single one.
Transclution
We could avoid transclusion here completely or use one of the already optimized transcluder described in this paper.
apidom-parser-adapter-openapi-yaml-3-1
This is a hollow adapter composed of two main packages: apidom-parser-adapter-yaml-1-2 and apidom-ns-openapi-3-1. All the performance optimizations that will be performed on these two main packages will inevitably increase performance of this adapter.
This adapter goes through following phases:
- Lexical analysis
- Syntactical analysis
- Transform
- Refracting
It's possible to create a super performant version of this adapter folding all the aforementioned phases into single one.
Transclution
We could avoid transclusion here completely or use one of the already optimized transcluder described in this paper.
apidom-reference
No feasible performance optimization are currently proposed for this package. The performance and optimal memory management were kept in check during all implementations.
Performance analytics
All performance analytics are performed using benchmark.js library. This library contains proper statistical model for performing analysis.
Resources
- https://benchmarkjs.com/docs
- https://mathiasbynens.be/notes/javascript-benchmarking
- https://thomaspoignant.medium.com/ci-build-performance-testing-with-github-action-e6b227097c83
- https://learning.oreilly.com/library/view/you-dont-know/9781491905197/ch06.html
- https://stackoverflow.com/questions/59971998/how-to-get-benchmark-js-to-do-setup-teardown-each-time-my-benchmarked-code-is-ru
- quick async example in readme bestiejs/benchmark.js#55
Performance optimizations
- Bootstrap performance suite for Refracting primitive types for OpenApi 3.1 namespace
- Introduce visitor shortcuts for AsyncApi 2.0 namespace
- Fold OpenApi 3.1 default refractor plugins into refracting phase
- Fold JSON parser syntactic analysis with transform phase
- Analyze performance profile of YAML parser adapter
- Consolidate code of YAML parser adapter
- Remove transcluder from all semantic parsers
- Second round of performance profiling #691
- Create performance tests on GitHub Api
- performance regressions tests