Does JSON.stringify be fast enough? Can we improve the speed of it? The first idea comes into my mind is whether I can use C/C++ rewrite it? Cause there may be many redundant checks in V8 of it, whether I can discard them. But it may be wrong, stringify JSON object needs read it to judge the type of the JSON primitive type, may string, object or array etc.
Yeah, we must do that to analyze its typed to transform it to JSON string. But if we know the type, in another way that we have known the structure of the object. That means we don't need to analyze the type one by one anymore. Assume that we have a schema like below
so we must know the JSON object of this schema is an object having a property of string named firstName. so when we stringify it, we only add "{" at the start and "}" at the end, and also can add ' \"fisrtName\": ' follow the "{" according to the schema. Analyze the schema is cheap. Analyzing the object without knowing anything is difficult. Oh yeah, that's amazing.
1. the first Engine accelerator of the fastify is the fast-json-stringify 2x than JSON.stringify
The main principle of this package is the seem as we are talking above.
Yeah every route path is mapped to a branch of the tree.
Insert 'water' at the root
Insert 'slower' while keeping 'slow'
insert 'tester' which is a prefix of 'tester'
Insert 'team' while splitting 'test' and creating a new edge label 'st'
Insert 'toast' while splitting 'te' and moving previous strings a level lower
Search for 'toasting'
Clearly radix tree is not a balanced trees so the cost is O(k) rather then O(log n), an K >= log n and K <= M(total of words or routes)
Compared to Array[M] (koa-router & express-router) it will be more efficient. Cause it needs lower comparison.
Compared to hash router it will be efficient too, even though hash time is O(1). Cause we need compute hash of the routes when use it. The most drawbacks of hash are that it does not support params and any (*) matches.
have you ever thought about JSON.stringify?
Does JSON.stringify be fast enough? Can we improve the speed of it? The first idea comes into my mind is whether I can use C/C++ rewrite it? Cause there may be many redundant checks in V8 of it, whether I can discard them. But it may be wrong, stringify JSON object needs read it to judge the type of the JSON primitive type, may string, object or array etc.
Yeah, we must do that to analyze its typed to transform it to JSON string. But if we know the type, in another way that we have known the structure of the object. That means we don't need to analyze the type one by one anymore. Assume that we have a schema like below
so we must know the JSON object of this schema is an object having a property of string named firstName. so when we stringify it, we only add "{" at the start and "}" at the end, and also can add ' \"fisrtName\": ' follow the "{" according to the schema. Analyze the schema is cheap. Analyzing the object without knowing anything is difficult. Oh yeah, that's amazing.
1. the first Engine accelerator of the fastify is the fast-json-stringify 2x than JSON.stringify
The main principle of this package is the seem as we are talking above.
we can dig into the generated ‘stringify’ function
As shown above, that's so simple. There is a benchmark on Node 10.4.0:
It's extremely fast. The last question is where fastify use it. Obviously using it in validation and reply.
2. about how v8 stores string: the second acceleration engine
flatstr is a package that makes some string's operation so fast. The homepage's section 'how it works' has explained it very clear in English.
Next is Chinese translation:
v8里面有两种处理字符串的方式
当我们处理字符串连接的时候,v8使用的是树结构。对于连接操作(concat), 构造一棵树明显比重新分配一块大的内存来的划算(大家可以思考一下为什么)。但是有一些其他操作树结构反而会带来更多的损耗(比如大量的字符串连接操作,大家又可以思考一下为什么了)。
V8里面有个内置函数叫String::Flatten, 它能够把树状的字符串结构再转化为C的数组形式。这个方法通常会在遍历字符串这个操作之前被调用(比如:测试一个正则表达式)。 在多次使用一个字符串时也会调用这个方法来作为优化。但这个方法并不是在所有的字符串操作时都会调用,比如我们传递一个字符串给WriteStream这个方法,此时字符串会被转为buffer, 但如果字符串的底层结构是树的话,这个转换操作就会很昂贵(至于为什么昂贵?留待下次探查,或者你们谁来追查一下?)。
关键在于String::Flatten并不是js的内置方法其实是v8独有的,但我们还是有办法触发这个方法的。
在(alt-benchmark.js)里面列举了一些可以触发这个方法的调用。其中转换成Number是代价最小的了。
但是自从Node10开始刚才我们所说的那些能触发Flatten的调用的操作V8都不会再去调用Flatten了。但没关系我们还可以通过'--allow-natives-syntax'这个flag来手动调用
如上就是Flatten的调用方式以及Node10以下的触发方式。其实这也是这个包的所有代码(不含测试等其他)。。。(求解释上面那个v8的引用方式。。。)
benchmark (fs.WriteStream)
可以看出基本都是flatstr 胜出的
最后需要注意的就是,物极必反,也不要太过于频繁的调用Flatten。毕竟他也是有性能损耗的。V8已经替我们做了很多的优化,我们就不要随便插手了。因此Flatten的正确使用方式应该是在传递字符串给非V8的代码:比如fs.WriteStream, xhr, DOM api等等。
V8内置函数列表
So where fastify use flatstr? in the http reply, cause the data will be passed to WriteSteam
3. the third Engine accelerator: radix tree--router matching
Fastify's router package is find-my-way based on radix tree akka compact Prefix Tree
Three picture explains radix clearly
Yeah every route path is mapped to a branch of the tree.
Insert 'water' at the root
Insert 'slower' while keeping 'slow'
insert 'tester' which is a prefix of 'tester'
Insert 'team' while splitting 'test' and creating a new edge label 'st'
Insert 'toast' while splitting 'te' and moving previous strings a level lower
Search for 'toasting'
Clearly radix tree is not a balanced trees so the cost is O(k) rather then O(log n), an K >= log n and K <= M(total of words or routes)
Compared to Array[M] (koa-router & express-router) it will be more efficient. Cause it needs lower comparison.
Compared to hash router it will be efficient too, even though hash time is O(1). Cause we need compute hash of the routes when use it. The most drawbacks of hash are that it does not support params and any (*) matches.
Another radix-router impletion in go echo
Conclusion
That's all? no
Far from enough. We still need to dig into fastify deeper
References
radix tree wiki
take-your-http-server-to-ludicrous-speed