The VScode Deopt explorer showed that .createReadOnly inside the class model register function was getting deoptimized because it closes over too-wide a variety of klass variable references. We're closing over it so it is correct, but to v8, the callsite is still the same I guess, so it has to deoptimize to pull the right value for the closure! Same for .is and .instantiate -- these dynamically produced functions make a huge difference if they are re-eval'd for each class instead of defined dynamically.
Removing this deoptimization recovered a lot of the node 18 => node 21 performance gap for me! Not all of it. It also makes node 18 faster, but not by nearly as much.
On main on node 18
instantiating a small root x 3,105,456 ops/sec ±0.15% (100 runs sampled)
instantiating a large root x 6,289 ops/sec ±0.68% (99 runs sampled)
instantiating a large union x 803,839 ops/sec ±1.33% (98 runs sampled)
instantiating a diverse root x 881,149 ops/sec ±1.35% (99 runs sampled)
On main on node 21
instantiating a small root x 1,942,360 ops/sec ±0.75% (94 runs sampled)
instantiating a large root x 3,725 ops/sec ±1.61% (93 runs sampled)
instantiating a large union x 468,432 ops/sec ±1.57% (95 runs sampled)
instantiating a diverse root x 604,151 ops/sec ±0.19% (96 runs sampled)
On branch on node 18
instantiating a small root x 3,047,781 ops/sec ±0.30% (95 runs sampled)
instantiating a large root x 6,842 ops/sec ±2.48% (96 runs sampled)
instantiating a large union x 1,085,365 ops/sec ±1.45% (99 runs sampled)
instantiating a diverse root x 983,455 ops/sec ±1.39% (100 runs sampled)
On branch on node 21
instantiating a small root x 1,904,508 ops/sec ±0.52% (99 runs sampled)
instantiating a large root x 5,247 ops/sec ±0.29% (98 runs sampled)
instantiating a large union x 523,979 ops/sec ±1.31% (95 runs sampled)
instantiating a diverse root x 1,040,574 ops/sec ±1.07% (101 runs sampled)
The VScode Deopt explorer showed that
.createReadOnly
inside the class modelregister
function was getting deoptimized because it closes over too-wide a variety ofklass
variable references. We're closing over it so it is correct, but to v8, the callsite is still the same I guess, so it has to deoptimize to pull the right value for the closure! Same for .is and .instantiate -- these dynamically produced functions make a huge difference if they are re-eval'd for each class instead of defined dynamically.Removing this deoptimization recovered a lot of the node 18 => node 21 performance gap for me! Not all of it. It also makes node 18 faster, but not by nearly as much.
On main on node 18
On main on node 21
On branch on node 18
On branch on node 21