New version
Here comes the new version:
- Stories and bugs implemented
- Sources
- Documentation
Interfaces
A new entity type can now be defined: interface.
Being able to define interfaces and to check if an object conforms with their specification will normalize and improve the way encapsulations are handled within the library but also when using it.
Release optimization
When the library is built, some source manipulations occur:
- Each module is individually transformed into an AST structure using esprima
- These structures are concatenated inside an Universal Module Definition loader
- Eventually, the result is serialized using escodegen
Before this release, the debug and release versions were almost the same. The main differences were:
- The release version is serialized without the comments (see rewriteOptions)
- Also, the release version is minified using UglifyJS
Because I am planning to implement some optimization patterns on the release version, I started to redesign the build process. After converting all the relevant sources to ES6, I realized that the former AST manipulation was not working properly. Indeed, it was trying to rename all variables to shorten them but... it didn't work. Not a big deal because UglifyJS was already doing the job.
Finally, I rewrote the AST manipulation to start optimizing the concatenated AST structure.
Estimating functions usage
I implemented a detection of unused functions which revealed that _gpfIsUnsignedByte was not used! Hence, it is removed from the output.
NOP
The build process includes a pre-processor capable of conditioning some JavaScript lines using C-like defines. However, as a lazy developer, I don't want to wrap all DEBUG specific functions inside a #ifdef / #endif pair.
That's why some variables are tagged with a comment containing only gpf:nop. This is a way to signal that the variable represents a no operation.
For instance: javascript
/*#ifdef(DEBUG)*/
// DEBUG specifics
_gpfAssertAttributeClassOnly = function (value) {
_gpfAsserts({
"Expected a class parameter": "function" === typeof value,
"Expected an Attribute-like class parameter": value.prototype instanceof _gpfAttribute
});
};
_gpfAssertAttributeOnly = function (value) {
_gpfAssert(value instanceof _gpfAttribute, "Expected an Attribute-like parameter");
};
/* istanbul ignore if */ // Because tested in DEBUG
if (!_gpfAssertAttributeClassOnly) {
/*#else*/
/*gpf:nop*/ _gpfAssertAttributeClassOnly = _gpfEmptyFunc;
/*gpf:nop*/ _gpfAssertAttributeOnly = _gpfEmptyFunc;
/*#endif*/
/*#ifdef(DEBUG)*/
}
The optimizer is now capable of locating all variables flagged with gpf:nop and safely remove them from the output.
Automated release (final)
In the last release, I forgot one last step: closing the milestone. This is now completed.
Lessons learned
Performance measurement
When dealing with performances, one of the biggest challenge is to establish the point of reference. Indeed, if you want to quantify how much you gain, you need to make sure that the measurement environment is stable enough so that you can compare your results.
In my case, this is tough: the JavaScript hosts are evolving very fast and, since I started this project, I already changed my workstation twice. Furthermore, I don't have any performance specific tests.
So, I decided to take a pragmatic approach.
I reuse the test cases (that cover almost all the library) and compare the debug version with the release one. By quantifying the difference of execution between the two versions, this would give me a good indication on how much the release is optimized.
Today, both version demonstrates similar performances, even after implementing some of the optimizations.
jsdoc plugin
In the article My own jsdoc plugin, I explained how I tweaked jsdoc to facilitate generation of the documentation.
However, I noticed that the defineTags could be a better alternative to define custom tags. After trying to experiment it (knowing that jsdoc plugins are badly documented), it appeared that it is extremely limited:
- No lookup function to access other existing doclets
- Very limited information on the current doclet (for instance: I tried to implement @gpf:chainable but the memberOf property was not set). Indeed, we can't know when the onTagged is triggered (no control on the order).
Next release
The next release will introduce some features that are required for a side project I created.
No comments:
Post a Comment