11cb0ef41Sopenharmony_ci# How to write and run benchmarks in Node.js core 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ci## Table of contents 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ci* [Prerequisites](#prerequisites) 61cb0ef41Sopenharmony_ci * [HTTP benchmark requirements](#http-benchmark-requirements) 71cb0ef41Sopenharmony_ci * [HTTPS benchmark requirements](#https-benchmark-requirements) 81cb0ef41Sopenharmony_ci * [HTTP/2 benchmark requirements](#http2-benchmark-requirements) 91cb0ef41Sopenharmony_ci * [Benchmark analysis requirements](#benchmark-analysis-requirements) 101cb0ef41Sopenharmony_ci* [Running benchmarks](#running-benchmarks) 111cb0ef41Sopenharmony_ci * [Running individual benchmarks](#running-individual-benchmarks) 121cb0ef41Sopenharmony_ci * [Running all benchmarks](#running-all-benchmarks) 131cb0ef41Sopenharmony_ci * [Filtering benchmarks](#filtering-benchmarks) 141cb0ef41Sopenharmony_ci * [Comparing Node.js versions](#comparing-nodejs-versions) 151cb0ef41Sopenharmony_ci * [Comparing parameters](#comparing-parameters) 161cb0ef41Sopenharmony_ci * [Running benchmarks on the CI](#running-benchmarks-on-the-ci) 171cb0ef41Sopenharmony_ci* [Creating a benchmark](#creating-a-benchmark) 181cb0ef41Sopenharmony_ci * [Basics of a benchmark](#basics-of-a-benchmark) 191cb0ef41Sopenharmony_ci * [Creating an HTTP benchmark](#creating-an-http-benchmark) 201cb0ef41Sopenharmony_ci 211cb0ef41Sopenharmony_ci## Prerequisites 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_ciBasic Unix tools are required for some benchmarks. 241cb0ef41Sopenharmony_ci[Git for Windows][git-for-windows] includes Git Bash and the necessary tools, 251cb0ef41Sopenharmony_ciwhich need to be included in the global Windows `PATH`. 261cb0ef41Sopenharmony_ci 271cb0ef41Sopenharmony_ci### HTTP benchmark requirements 281cb0ef41Sopenharmony_ci 291cb0ef41Sopenharmony_ciMost of the HTTP benchmarks require a benchmarker to be installed. This can be 301cb0ef41Sopenharmony_cieither [`wrk`][wrk] or [`autocannon`][autocannon]. 311cb0ef41Sopenharmony_ci 321cb0ef41Sopenharmony_ci`Autocannon` is a Node.js script that can be installed using 331cb0ef41Sopenharmony_ci`npm install -g autocannon`. It will use the Node.js executable that is in the 341cb0ef41Sopenharmony_cipath. In order to compare two HTTP benchmark runs, make sure that the 351cb0ef41Sopenharmony_ciNode.js version in the path is not altered. 361cb0ef41Sopenharmony_ci 371cb0ef41Sopenharmony_ci`wrk` may be available through one of the available package managers. If not, 381cb0ef41Sopenharmony_ciit can be easily built [from source][wrk] via `make`. 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_ciBy default, `wrk` will be used as the benchmarker. If it is not available, 411cb0ef41Sopenharmony_ci`autocannon` will be used in its place. When creating an HTTP benchmark, the 421cb0ef41Sopenharmony_cibenchmarker to be used should be specified by providing it as an argument: 431cb0ef41Sopenharmony_ci 441cb0ef41Sopenharmony_ci`node benchmark/run.js --set benchmarker=autocannon http` 451cb0ef41Sopenharmony_ci 461cb0ef41Sopenharmony_ci`node benchmark/http/simple.js benchmarker=autocannon` 471cb0ef41Sopenharmony_ci 481cb0ef41Sopenharmony_ci#### HTTPS benchmark requirements 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_ciTo run the `https` benchmarks, one of `autocannon` or `wrk` benchmarkers must 511cb0ef41Sopenharmony_cibe used. 521cb0ef41Sopenharmony_ci 531cb0ef41Sopenharmony_ci`node benchmark/https/simple.js benchmarker=autocannon` 541cb0ef41Sopenharmony_ci 551cb0ef41Sopenharmony_ci#### HTTP/2 benchmark requirements 561cb0ef41Sopenharmony_ci 571cb0ef41Sopenharmony_ciTo run the `http2` benchmarks, the `h2load` benchmarker must be used. The 581cb0ef41Sopenharmony_ci`h2load` tool is a component of the `nghttp2` project and may be installed 591cb0ef41Sopenharmony_cifrom [nghttp2.org][] or built from source. 601cb0ef41Sopenharmony_ci 611cb0ef41Sopenharmony_ci`node benchmark/http2/simple.js benchmarker=h2load` 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_ci### Benchmark analysis requirements 641cb0ef41Sopenharmony_ci 651cb0ef41Sopenharmony_ciTo analyze the results statistically, you can use either the 661cb0ef41Sopenharmony_ci[node-benchmark-compare][] tool or the R script `benchmark/compare.R`. 671cb0ef41Sopenharmony_ci 681cb0ef41Sopenharmony_ci[node-benchmark-compare][] is a Node.js script that can be installed with 691cb0ef41Sopenharmony_ci`npm install -g node-benchmark-compare`. 701cb0ef41Sopenharmony_ci 711cb0ef41Sopenharmony_ciTo draw comparison plots when analyzing the results, `R` must be installed. 721cb0ef41Sopenharmony_ciUse one of the available package managers or download it from 731cb0ef41Sopenharmony_ci<https://www.r-project.org/>. 741cb0ef41Sopenharmony_ci 751cb0ef41Sopenharmony_ciThe R packages `ggplot2` and `plyr` are also used and can be installed using 761cb0ef41Sopenharmony_cithe R REPL. 771cb0ef41Sopenharmony_ci 781cb0ef41Sopenharmony_ci```console 791cb0ef41Sopenharmony_ci$ R 801cb0ef41Sopenharmony_ciinstall.packages("ggplot2") 811cb0ef41Sopenharmony_ciinstall.packages("plyr") 821cb0ef41Sopenharmony_ci``` 831cb0ef41Sopenharmony_ci 841cb0ef41Sopenharmony_ciIf a message states that a CRAN mirror must be selected first, specify a mirror 851cb0ef41Sopenharmony_ciwith the `repo` parameter. 861cb0ef41Sopenharmony_ci 871cb0ef41Sopenharmony_ci```r 881cb0ef41Sopenharmony_ciinstall.packages("ggplot2", repo="http://cran.us.r-project.org") 891cb0ef41Sopenharmony_ci``` 901cb0ef41Sopenharmony_ci 911cb0ef41Sopenharmony_ciOf course, use an appropriate mirror based on location. 921cb0ef41Sopenharmony_ciA list of mirrors is [located here](https://cran.r-project.org/mirrors.html). 931cb0ef41Sopenharmony_ci 941cb0ef41Sopenharmony_ci## Running benchmarks 951cb0ef41Sopenharmony_ci 961cb0ef41Sopenharmony_ci### Running individual benchmarks 971cb0ef41Sopenharmony_ci 981cb0ef41Sopenharmony_ciThis can be useful for debugging a benchmark or doing a quick performance 991cb0ef41Sopenharmony_cimeasure. But it does not provide the statistical information to make any 1001cb0ef41Sopenharmony_ciconclusions about the performance. 1011cb0ef41Sopenharmony_ci 1021cb0ef41Sopenharmony_ciIndividual benchmarks can be executed by simply executing the benchmark script 1031cb0ef41Sopenharmony_ciwith node. 1041cb0ef41Sopenharmony_ci 1051cb0ef41Sopenharmony_ci```console 1061cb0ef41Sopenharmony_ci$ node benchmark/buffers/buffer-tostring.js 1071cb0ef41Sopenharmony_ci 1081cb0ef41Sopenharmony_cibuffers/buffer-tostring.js n=10000000 len=0 arg=true: 62710590.393305704 1091cb0ef41Sopenharmony_cibuffers/buffer-tostring.js n=10000000 len=1 arg=true: 9178624.591787899 1101cb0ef41Sopenharmony_cibuffers/buffer-tostring.js n=10000000 len=64 arg=true: 7658962.8891432695 1111cb0ef41Sopenharmony_cibuffers/buffer-tostring.js n=10000000 len=1024 arg=true: 4136904.4060201733 1121cb0ef41Sopenharmony_cibuffers/buffer-tostring.js n=10000000 len=0 arg=false: 22974354.231509723 1131cb0ef41Sopenharmony_cibuffers/buffer-tostring.js n=10000000 len=1 arg=false: 11485945.656765845 1141cb0ef41Sopenharmony_cibuffers/buffer-tostring.js n=10000000 len=64 arg=false: 8718280.70650129 1151cb0ef41Sopenharmony_cibuffers/buffer-tostring.js n=10000000 len=1024 arg=false: 4103857.0726124765 1161cb0ef41Sopenharmony_ci``` 1171cb0ef41Sopenharmony_ci 1181cb0ef41Sopenharmony_ciEach line represents a single benchmark with parameters specified as 1191cb0ef41Sopenharmony_ci`${variable}=${value}`. Each configuration combination is executed in a separate 1201cb0ef41Sopenharmony_ciprocess. This ensures that benchmark results aren't affected by the execution 1211cb0ef41Sopenharmony_ciorder due to V8 optimizations. **The last number is the rate of operations 1221cb0ef41Sopenharmony_cimeasured in ops/sec (higher is better).** 1231cb0ef41Sopenharmony_ci 1241cb0ef41Sopenharmony_ciFurthermore a subset of the configurations can be specified, by setting them in 1251cb0ef41Sopenharmony_cithe process arguments: 1261cb0ef41Sopenharmony_ci 1271cb0ef41Sopenharmony_ci```console 1281cb0ef41Sopenharmony_ci$ node benchmark/buffers/buffer-tostring.js len=1024 1291cb0ef41Sopenharmony_ci 1301cb0ef41Sopenharmony_cibuffers/buffer-tostring.js n=10000000 len=1024 arg=true: 3498295.68561504 1311cb0ef41Sopenharmony_cibuffers/buffer-tostring.js n=10000000 len=1024 arg=false: 3783071.1678948295 1321cb0ef41Sopenharmony_ci``` 1331cb0ef41Sopenharmony_ci 1341cb0ef41Sopenharmony_ci### Running all benchmarks 1351cb0ef41Sopenharmony_ci 1361cb0ef41Sopenharmony_ciSimilar to running individual benchmarks, a group of benchmarks can be executed 1371cb0ef41Sopenharmony_ciby using the `run.js` tool. To see how to use this script, 1381cb0ef41Sopenharmony_cirun `node benchmark/run.js`. Again this does not provide the statistical 1391cb0ef41Sopenharmony_ciinformation to make any conclusions. 1401cb0ef41Sopenharmony_ci 1411cb0ef41Sopenharmony_ci```console 1421cb0ef41Sopenharmony_ci$ node benchmark/run.js assert 1431cb0ef41Sopenharmony_ci 1441cb0ef41Sopenharmony_ciassert/deepequal-buffer.js 1451cb0ef41Sopenharmony_ciassert/deepequal-buffer.js method="deepEqual" strict=0 len=100 n=20000: 773,200.4995493788 1461cb0ef41Sopenharmony_ciassert/deepequal-buffer.js method="notDeepEqual" strict=0 len=100 n=20000: 964,411.712953848 1471cb0ef41Sopenharmony_ci... 1481cb0ef41Sopenharmony_ci 1491cb0ef41Sopenharmony_ciassert/deepequal-map.js 1501cb0ef41Sopenharmony_ciassert/deepequal-map.js method="deepEqual_primitiveOnly" strict=0 len=500 n=500: 20,445.06368453332 1511cb0ef41Sopenharmony_ciassert/deepequal-map.js method="deepEqual_objectOnly" strict=0 len=500 n=500: 1,393.3481642240833 1521cb0ef41Sopenharmony_ci... 1531cb0ef41Sopenharmony_ci 1541cb0ef41Sopenharmony_ciassert/deepequal-object.js 1551cb0ef41Sopenharmony_ciassert/deepequal-object.js method="deepEqual" strict=0 size=100 n=5000: 1,053.1950937538475 1561cb0ef41Sopenharmony_ciassert/deepequal-object.js method="notDeepEqual" strict=0 size=100 n=5000: 9,734.193251965213 1571cb0ef41Sopenharmony_ci... 1581cb0ef41Sopenharmony_ci``` 1591cb0ef41Sopenharmony_ci 1601cb0ef41Sopenharmony_ciIt is possible to execute more groups by adding extra process arguments. 1611cb0ef41Sopenharmony_ci 1621cb0ef41Sopenharmony_ci```console 1631cb0ef41Sopenharmony_ci$ node benchmark/run.js assert async_hooks 1641cb0ef41Sopenharmony_ci``` 1651cb0ef41Sopenharmony_ci 1661cb0ef41Sopenharmony_ci#### Filtering benchmarks 1671cb0ef41Sopenharmony_ci 1681cb0ef41Sopenharmony_ci`benchmark/run.js` and `benchmark/compare.js` have `--filter pattern` and 1691cb0ef41Sopenharmony_ci`--exclude pattern` options, which can be used to run a subset of benchmarks or 1701cb0ef41Sopenharmony_cito exclude specific benchmarks from the execution, respectively. 1711cb0ef41Sopenharmony_ci 1721cb0ef41Sopenharmony_ci```console 1731cb0ef41Sopenharmony_ci$ node benchmark/run.js --filter "deepequal-b" assert 1741cb0ef41Sopenharmony_ci 1751cb0ef41Sopenharmony_ciassert/deepequal-buffer.js 1761cb0ef41Sopenharmony_ciassert/deepequal-buffer.js method="deepEqual" strict=0 len=100 n=20000: 773,200.4995493788 1771cb0ef41Sopenharmony_ciassert/deepequal-buffer.js method="notDeepEqual" strict=0 len=100 n=20000: 964,411.712953848 1781cb0ef41Sopenharmony_ci 1791cb0ef41Sopenharmony_ci$ node benchmark/run.js --exclude "deepequal-b" assert 1801cb0ef41Sopenharmony_ci 1811cb0ef41Sopenharmony_ciassert/deepequal-map.js 1821cb0ef41Sopenharmony_ciassert/deepequal-map.js method="deepEqual_primitiveOnly" strict=0 len=500 n=500: 20,445.06368453332 1831cb0ef41Sopenharmony_ciassert/deepequal-map.js method="deepEqual_objectOnly" strict=0 len=500 n=500: 1,393.3481642240833 1841cb0ef41Sopenharmony_ci... 1851cb0ef41Sopenharmony_ci 1861cb0ef41Sopenharmony_ciassert/deepequal-object.js 1871cb0ef41Sopenharmony_ciassert/deepequal-object.js method="deepEqual" strict=0 size=100 n=5000: 1,053.1950937538475 1881cb0ef41Sopenharmony_ciassert/deepequal-object.js method="notDeepEqual" strict=0 size=100 n=5000: 9,734.193251965213 1891cb0ef41Sopenharmony_ci... 1901cb0ef41Sopenharmony_ci``` 1911cb0ef41Sopenharmony_ci 1921cb0ef41Sopenharmony_ci`--filter` and `--exclude` can be repeated to provide multiple patterns. 1931cb0ef41Sopenharmony_ci 1941cb0ef41Sopenharmony_ci```console 1951cb0ef41Sopenharmony_ci$ node benchmark/run.js --filter "deepequal-b" --filter "deepequal-m" assert 1961cb0ef41Sopenharmony_ci 1971cb0ef41Sopenharmony_ciassert/deepequal-buffer.js 1981cb0ef41Sopenharmony_ciassert/deepequal-buffer.js method="deepEqual" strict=0 len=100 n=20000: 773,200.4995493788 1991cb0ef41Sopenharmony_ciassert/deepequal-buffer.js method="notDeepEqual" strict=0 len=100 n=20000: 964,411.712953848 2001cb0ef41Sopenharmony_ci 2011cb0ef41Sopenharmony_ciassert/deepequal-map.js 2021cb0ef41Sopenharmony_ciassert/deepequal-map.js method="deepEqual_primitiveOnly" strict=0 len=500 n=500: 20,445.06368453332 2031cb0ef41Sopenharmony_ciassert/deepequal-map.js method="deepEqual_objectOnly" strict=0 len=500 n=500: 1,393.3481642240833 2041cb0ef41Sopenharmony_ci 2051cb0ef41Sopenharmony_ci$ node benchmark/run.js --exclude "deepequal-b" --exclude "deepequal-m" assert 2061cb0ef41Sopenharmony_ci 2071cb0ef41Sopenharmony_ciassert/deepequal-object.js 2081cb0ef41Sopenharmony_ciassert/deepequal-object.js method="deepEqual" strict=0 size=100 n=5000: 1,053.1950937538475 2091cb0ef41Sopenharmony_ciassert/deepequal-object.js method="notDeepEqual" strict=0 size=100 n=5000: 9,734.193251965213 2101cb0ef41Sopenharmony_ci... 2111cb0ef41Sopenharmony_ci 2121cb0ef41Sopenharmony_ciassert/deepequal-prims-and-objs-big-array-set.js 2131cb0ef41Sopenharmony_ciassert/deepequal-prims-and-objs-big-array-set.js method="deepEqual_Array" strict=0 len=20000 n=25 primitive="string": 865.2977195251661 2141cb0ef41Sopenharmony_ciassert/deepequal-prims-and-objs-big-array-set.js method="notDeepEqual_Array" strict=0 len=20000 n=25 primitive="string": 827.8297281403861 2151cb0ef41Sopenharmony_ciassert/deepequal-prims-and-objs-big-array-set.js method="deepEqual_Set" strict=0 len=20000 n=25 primitive="string": 28,826.618268696366 2161cb0ef41Sopenharmony_ci... 2171cb0ef41Sopenharmony_ci``` 2181cb0ef41Sopenharmony_ci 2191cb0ef41Sopenharmony_ciIf `--filter` and `--exclude` are used together, `--filter` is applied first, 2201cb0ef41Sopenharmony_ciand `--exclude` is applied on the result of `--filter`: 2211cb0ef41Sopenharmony_ci 2221cb0ef41Sopenharmony_ci```console 2231cb0ef41Sopenharmony_ci$ node benchmark/run.js --filter "bench-" process 2241cb0ef41Sopenharmony_ci 2251cb0ef41Sopenharmony_ciprocess/bench-env.js 2261cb0ef41Sopenharmony_ciprocess/bench-env.js operation="get" n=1000000: 2,356,946.0770617095 2271cb0ef41Sopenharmony_ciprocess/bench-env.js operation="set" n=1000000: 1,295,176.3266261867 2281cb0ef41Sopenharmony_ciprocess/bench-env.js operation="enumerate" n=1000000: 24,592.32231990992 2291cb0ef41Sopenharmony_ciprocess/bench-env.js operation="query" n=1000000: 3,625,787.2150573144 2301cb0ef41Sopenharmony_ciprocess/bench-env.js operation="delete" n=1000000: 1,521,131.5742806569 2311cb0ef41Sopenharmony_ci 2321cb0ef41Sopenharmony_ciprocess/bench-hrtime.js 2331cb0ef41Sopenharmony_ciprocess/bench-hrtime.js type="raw" n=1000000: 13,178,002.113936031 2341cb0ef41Sopenharmony_ciprocess/bench-hrtime.js type="diff" n=1000000: 11,585,435.712423025 2351cb0ef41Sopenharmony_ciprocess/bench-hrtime.js type="bigint" n=1000000: 13,342,884.703919787 2361cb0ef41Sopenharmony_ci 2371cb0ef41Sopenharmony_ci$ node benchmark/run.js --filter "bench-" --exclude "hrtime" process 2381cb0ef41Sopenharmony_ci 2391cb0ef41Sopenharmony_ciprocess/bench-env.js 2401cb0ef41Sopenharmony_ciprocess/bench-env.js operation="get" n=1000000: 2,356,946.0770617095 2411cb0ef41Sopenharmony_ciprocess/bench-env.js operation="set" n=1000000: 1,295,176.3266261867 2421cb0ef41Sopenharmony_ciprocess/bench-env.js operation="enumerate" n=1000000: 24,592.32231990992 2431cb0ef41Sopenharmony_ciprocess/bench-env.js operation="query" n=1000000: 3,625,787.2150573144 2441cb0ef41Sopenharmony_ciprocess/bench-env.js operation="delete" n=1000000: 1,521,131.5742806569 2451cb0ef41Sopenharmony_ci``` 2461cb0ef41Sopenharmony_ci 2471cb0ef41Sopenharmony_ci### Comparing Node.js versions 2481cb0ef41Sopenharmony_ci 2491cb0ef41Sopenharmony_ciTo compare the effect of a new Node.js version use the `compare.js` tool. This 2501cb0ef41Sopenharmony_ciwill run each benchmark multiple times, making it possible to calculate 2511cb0ef41Sopenharmony_cistatistics on the performance measures. To see how to use this script, 2521cb0ef41Sopenharmony_cirun `node benchmark/compare.js`. 2531cb0ef41Sopenharmony_ci 2541cb0ef41Sopenharmony_ciAs an example on how to check for a possible performance improvement, the 2551cb0ef41Sopenharmony_ci[#5134](https://github.com/nodejs/node/pull/5134) pull request will be used as 2561cb0ef41Sopenharmony_cian example. This pull request _claims_ to improve the performance of the 2571cb0ef41Sopenharmony_ci`node:string_decoder` module. 2581cb0ef41Sopenharmony_ci 2591cb0ef41Sopenharmony_ciFirst build two versions of Node.js, one from the `main` branch (here called 2601cb0ef41Sopenharmony_ci`./node-main`) and another with the pull request applied (here called 2611cb0ef41Sopenharmony_ci`./node-pr-5134`). 2621cb0ef41Sopenharmony_ci 2631cb0ef41Sopenharmony_ciTo run multiple compiled versions in parallel you need to copy the output of the 2641cb0ef41Sopenharmony_cibuild: `cp ./out/Release/node ./node-main`. Check out the following example: 2651cb0ef41Sopenharmony_ci 2661cb0ef41Sopenharmony_ci```console 2671cb0ef41Sopenharmony_ci$ git checkout main 2681cb0ef41Sopenharmony_ci$ ./configure && make -j4 2691cb0ef41Sopenharmony_ci$ cp ./out/Release/node ./node-main 2701cb0ef41Sopenharmony_ci 2711cb0ef41Sopenharmony_ci$ git checkout pr-5134 2721cb0ef41Sopenharmony_ci$ ./configure && make -j4 2731cb0ef41Sopenharmony_ci$ cp ./out/Release/node ./node-pr-5134 2741cb0ef41Sopenharmony_ci``` 2751cb0ef41Sopenharmony_ci 2761cb0ef41Sopenharmony_ciThe `compare.js` tool will then produce a csv file with the benchmark results. 2771cb0ef41Sopenharmony_ci 2781cb0ef41Sopenharmony_ci```console 2791cb0ef41Sopenharmony_ci$ node benchmark/compare.js --old ./node-main --new ./node-pr-5134 string_decoder > compare-pr-5134.csv 2801cb0ef41Sopenharmony_ci``` 2811cb0ef41Sopenharmony_ci 2821cb0ef41Sopenharmony_ci_Tips: there are some useful options of `benchmark/compare.js`. For example, 2831cb0ef41Sopenharmony_ciif you want to compare the benchmark of a single script instead of a whole 2841cb0ef41Sopenharmony_cimodule, you can use the `--filter` option:_ 2851cb0ef41Sopenharmony_ci 2861cb0ef41Sopenharmony_ci```console 2871cb0ef41Sopenharmony_ci --new ./new-node-binary new node binary (required) 2881cb0ef41Sopenharmony_ci --old ./old-node-binary old node binary (required) 2891cb0ef41Sopenharmony_ci --runs 30 number of samples 2901cb0ef41Sopenharmony_ci --filter pattern string to filter benchmark scripts 2911cb0ef41Sopenharmony_ci --set variable=value set benchmark variable (can be repeated) 2921cb0ef41Sopenharmony_ci --no-progress don't show benchmark progress indicator 2931cb0ef41Sopenharmony_ci``` 2941cb0ef41Sopenharmony_ci 2951cb0ef41Sopenharmony_ciFor analyzing the benchmark results, use [node-benchmark-compare][] or the R 2961cb0ef41Sopenharmony_ciscripts: 2971cb0ef41Sopenharmony_ci 2981cb0ef41Sopenharmony_ci* `benchmark/compare.R` 2991cb0ef41Sopenharmony_ci* `benchmark/bar.R` 3001cb0ef41Sopenharmony_ci 3011cb0ef41Sopenharmony_ci```console 3021cb0ef41Sopenharmony_ci$ node-benchmark-compare compare-pr-5134.csv # or cat compare-pr-5134.csv | Rscript benchmark/compare.R 3031cb0ef41Sopenharmony_ci 3041cb0ef41Sopenharmony_ci confidence improvement accuracy (*) (**) (***) 3051cb0ef41Sopenharmony_ci string_decoder/string-decoder.js n=2500000 chunkLen=16 inLen=128 encoding='ascii' *** -3.76 % ±1.36% ±1.82% ±2.40% 3061cb0ef41Sopenharmony_ci string_decoder/string-decoder.js n=2500000 chunkLen=16 inLen=128 encoding='utf8' ** -0.81 % ±0.53% ±0.71% ±0.93% 3071cb0ef41Sopenharmony_ci string_decoder/string-decoder.js n=2500000 chunkLen=16 inLen=32 encoding='ascii' *** -2.70 % ±0.83% ±1.11% ±1.45% 3081cb0ef41Sopenharmony_ci string_decoder/string-decoder.js n=2500000 chunkLen=16 inLen=32 encoding='base64-ascii' *** -1.57 % ±0.83% ±1.11% ±1.46% 3091cb0ef41Sopenharmony_ci... 3101cb0ef41Sopenharmony_ci``` 3111cb0ef41Sopenharmony_ci 3121cb0ef41Sopenharmony_ciIn the output, _improvement_ is the relative improvement of the new version, 3131cb0ef41Sopenharmony_cihopefully this is positive. _confidence_ tells if there is enough 3141cb0ef41Sopenharmony_cistatistical evidence to validate the _improvement_. If there is enough evidence 3151cb0ef41Sopenharmony_cithen there will be at least one star (`*`), more stars is just better. **However 3161cb0ef41Sopenharmony_ciif there are no stars, then don't make any conclusions based on the 3171cb0ef41Sopenharmony_ci_improvement_.** Sometimes this is fine, for example if no improvements are 3181cb0ef41Sopenharmony_ciexpected, then there shouldn't be any stars. 3191cb0ef41Sopenharmony_ci 3201cb0ef41Sopenharmony_ci**A word of caution:** Statistics is not a foolproof tool. If a benchmark shows 3211cb0ef41Sopenharmony_cia statistical significant difference, there is a 5% risk that this 3221cb0ef41Sopenharmony_cidifference doesn't actually exist. For a single benchmark this is not an 3231cb0ef41Sopenharmony_ciissue. But when considering 20 benchmarks it's normal that one of them 3241cb0ef41Sopenharmony_ciwill show significance, when it shouldn't. A possible solution is to instead 3251cb0ef41Sopenharmony_ciconsider at least two stars (`**`) as the threshold, in that case the risk 3261cb0ef41Sopenharmony_ciis 1%. If three stars (`***`) is considered the risk is 0.1%. However this 3271cb0ef41Sopenharmony_cimay require more runs to obtain (can be set with `--runs`). 3281cb0ef41Sopenharmony_ci 3291cb0ef41Sopenharmony_ci_For the statistically minded, the script performs an [independent/unpaired 3301cb0ef41Sopenharmony_ci2-group t-test][t-test], with the null hypothesis that the performance is the 3311cb0ef41Sopenharmony_cisame for both versions. The confidence field will show a star if the p-value 3321cb0ef41Sopenharmony_ciis less than `0.05`._ 3331cb0ef41Sopenharmony_ci 3341cb0ef41Sopenharmony_ciThe `compare.R` tool can additionally produce a box plot by using the 3351cb0ef41Sopenharmony_ci`--plot filename` option. In this case there are 48 different benchmark 3361cb0ef41Sopenharmony_cicombinations, and there may be a need to filter the csv file. This can be done 3371cb0ef41Sopenharmony_ciwhile benchmarking using the `--set` parameter (e.g. `--set encoding=ascii`) or 3381cb0ef41Sopenharmony_ciby filtering results afterwards using tools such as `sed` or `grep`. In the 3391cb0ef41Sopenharmony_ci`sed` case be sure to keep the first line since that contains the header 3401cb0ef41Sopenharmony_ciinformation. 3411cb0ef41Sopenharmony_ci 3421cb0ef41Sopenharmony_ci```console 3431cb0ef41Sopenharmony_ci$ cat compare-pr-5134.csv | sed '1p;/encoding='"'"ascii"'"'/!d' | Rscript benchmark/compare.R --plot compare-plot.png 3441cb0ef41Sopenharmony_ci 3451cb0ef41Sopenharmony_ci confidence improvement accuracy (*) (**) (***) 3461cb0ef41Sopenharmony_ci string_decoder/string-decoder.js n=2500000 chunkLen=16 inLen=128 encoding='ascii' *** -3.76 % ±1.36% ±1.82% ±2.40% 3471cb0ef41Sopenharmony_ci string_decoder/string-decoder.js n=2500000 chunkLen=16 inLen=32 encoding='ascii' *** -2.70 % ±0.83% ±1.11% ±1.45% 3481cb0ef41Sopenharmony_ci string_decoder/string-decoder.js n=2500000 chunkLen=16 inLen=4096 encoding='ascii' *** -4.06 % ±0.31% ±0.41% ±0.54% 3491cb0ef41Sopenharmony_ci string_decoder/string-decoder.js n=2500000 chunkLen=256 inLen=1024 encoding='ascii' *** -1.42 % ±0.58% ±0.77% ±1.01% 3501cb0ef41Sopenharmony_ci... 3511cb0ef41Sopenharmony_ci``` 3521cb0ef41Sopenharmony_ci 3531cb0ef41Sopenharmony_ci 3541cb0ef41Sopenharmony_ci 3551cb0ef41Sopenharmony_ci### Comparing parameters 3561cb0ef41Sopenharmony_ci 3571cb0ef41Sopenharmony_ciIt can be useful to compare the performance for different parameters, for 3581cb0ef41Sopenharmony_ciexample to analyze the time complexity. 3591cb0ef41Sopenharmony_ci 3601cb0ef41Sopenharmony_ciTo do this use the `scatter.js` tool, this will run a benchmark multiple times 3611cb0ef41Sopenharmony_ciand generate a csv with the results. To see how to use this script, 3621cb0ef41Sopenharmony_cirun `node benchmark/scatter.js`. 3631cb0ef41Sopenharmony_ci 3641cb0ef41Sopenharmony_ci```console 3651cb0ef41Sopenharmony_ci$ node benchmark/scatter.js benchmark/string_decoder/string-decoder.js > scatter.csv 3661cb0ef41Sopenharmony_ci``` 3671cb0ef41Sopenharmony_ci 3681cb0ef41Sopenharmony_ciAfter generating the csv, a comparison table can be created using the 3691cb0ef41Sopenharmony_ci`scatter.R` tool. Even more useful it creates an actual scatter plot when using 3701cb0ef41Sopenharmony_cithe `--plot filename` option. 3711cb0ef41Sopenharmony_ci 3721cb0ef41Sopenharmony_ci```console 3731cb0ef41Sopenharmony_ci$ cat scatter.csv | Rscript benchmark/scatter.R --xaxis chunkLen --category encoding --plot scatter-plot.png --log 3741cb0ef41Sopenharmony_ci 3751cb0ef41Sopenharmony_ciaggregating variable: inLen 3761cb0ef41Sopenharmony_ci 3771cb0ef41Sopenharmony_cichunkLen encoding rate confidence.interval 3781cb0ef41Sopenharmony_ci 16 ascii 1515855.1 334492.68 3791cb0ef41Sopenharmony_ci 16 base64-ascii 403527.2 89677.70 3801cb0ef41Sopenharmony_ci 16 base64-utf8 322352.8 70792.93 3811cb0ef41Sopenharmony_ci 16 utf16le 1714567.5 388439.81 3821cb0ef41Sopenharmony_ci 16 utf8 1100181.6 254141.32 3831cb0ef41Sopenharmony_ci 64 ascii 3550402.0 661277.65 3841cb0ef41Sopenharmony_ci 64 base64-ascii 1093660.3 229976.34 3851cb0ef41Sopenharmony_ci 64 base64-utf8 997804.8 227238.04 3861cb0ef41Sopenharmony_ci 64 utf16le 3372234.0 647274.88 3871cb0ef41Sopenharmony_ci 64 utf8 1731941.2 360854.04 3881cb0ef41Sopenharmony_ci 256 ascii 5033793.9 723354.30 3891cb0ef41Sopenharmony_ci 256 base64-ascii 1447962.1 236625.96 3901cb0ef41Sopenharmony_ci 256 base64-utf8 1357269.2 231045.70 3911cb0ef41Sopenharmony_ci 256 utf16le 4039581.5 655483.16 3921cb0ef41Sopenharmony_ci 256 utf8 1828672.9 360311.55 3931cb0ef41Sopenharmony_ci 1024 ascii 5677592.7 624771.56 3941cb0ef41Sopenharmony_ci 1024 base64-ascii 1494171.7 227302.34 3951cb0ef41Sopenharmony_ci 1024 base64-utf8 1399218.9 224584.79 3961cb0ef41Sopenharmony_ci 1024 utf16le 4157452.0 630416.28 3971cb0ef41Sopenharmony_ci 1024 utf8 1824266.6 359628.52 3981cb0ef41Sopenharmony_ci``` 3991cb0ef41Sopenharmony_ci 4001cb0ef41Sopenharmony_ciBecause the scatter plot can only show two variables (in this case _chunkLen_ 4011cb0ef41Sopenharmony_ciand _encoding_) the rest is aggregated. Sometimes aggregating is a problem, this 4021cb0ef41Sopenharmony_cican be solved by filtering. This can be done while benchmarking using the 4031cb0ef41Sopenharmony_ci`--set` parameter (e.g. `--set encoding=ascii`) or by filtering results 4041cb0ef41Sopenharmony_ciafterwards using tools such as `sed` or `grep`. In the `sed` case be 4051cb0ef41Sopenharmony_cisure to keep the first line since that contains the header information. 4061cb0ef41Sopenharmony_ci 4071cb0ef41Sopenharmony_ci```console 4081cb0ef41Sopenharmony_ci$ cat scatter.csv | sed -E '1p;/([^,]+, ){3}128,/!d' | Rscript benchmark/scatter.R --xaxis chunkLen --category encoding --plot scatter-plot.png --log 4091cb0ef41Sopenharmony_ci 4101cb0ef41Sopenharmony_cichunkLen encoding rate confidence.interval 4111cb0ef41Sopenharmony_ci 16 ascii 1302078.5 71692.27 4121cb0ef41Sopenharmony_ci 16 base64-ascii 338669.1 15159.54 4131cb0ef41Sopenharmony_ci 16 base64-utf8 281904.2 20326.75 4141cb0ef41Sopenharmony_ci 16 utf16le 1381515.5 58533.61 4151cb0ef41Sopenharmony_ci 16 utf8 831183.2 33631.01 4161cb0ef41Sopenharmony_ci 64 ascii 4363402.8 224030.00 4171cb0ef41Sopenharmony_ci 64 base64-ascii 1036825.9 48644.72 4181cb0ef41Sopenharmony_ci 64 base64-utf8 780059.3 60994.98 4191cb0ef41Sopenharmony_ci 64 utf16le 3900749.5 158366.84 4201cb0ef41Sopenharmony_ci 64 utf8 1723710.6 80665.65 4211cb0ef41Sopenharmony_ci 256 ascii 8472896.1 511822.51 4221cb0ef41Sopenharmony_ci 256 base64-ascii 2215884.6 104347.53 4231cb0ef41Sopenharmony_ci 256 base64-utf8 1996230.3 131778.47 4241cb0ef41Sopenharmony_ci 256 utf16le 5824147.6 234550.82 4251cb0ef41Sopenharmony_ci 256 utf8 2019428.8 100913.36 4261cb0ef41Sopenharmony_ci 1024 ascii 8340189.4 598855.08 4271cb0ef41Sopenharmony_ci 1024 base64-ascii 2201316.2 111777.68 4281cb0ef41Sopenharmony_ci 1024 base64-utf8 2002272.9 128843.11 4291cb0ef41Sopenharmony_ci 1024 utf16le 5789281.7 240642.77 4301cb0ef41Sopenharmony_ci 1024 utf8 2025551.2 81770.69 4311cb0ef41Sopenharmony_ci``` 4321cb0ef41Sopenharmony_ci 4331cb0ef41Sopenharmony_ci 4341cb0ef41Sopenharmony_ci 4351cb0ef41Sopenharmony_ci### Running benchmarks on the CI 4361cb0ef41Sopenharmony_ci 4371cb0ef41Sopenharmony_ciTo see the performance impact of a pull request by running benchmarks on 4381cb0ef41Sopenharmony_cithe CI, check out [How to: Running core benchmarks on Node.js CI][benchmark-ci]. 4391cb0ef41Sopenharmony_ci 4401cb0ef41Sopenharmony_ci## Creating a benchmark 4411cb0ef41Sopenharmony_ci 4421cb0ef41Sopenharmony_ci### Basics of a benchmark 4431cb0ef41Sopenharmony_ci 4441cb0ef41Sopenharmony_ciAll benchmarks use the `require('../common.js')` module. This contains the 4451cb0ef41Sopenharmony_ci`createBenchmark(main, configs[, options])` method which will setup the 4461cb0ef41Sopenharmony_cibenchmark. 4471cb0ef41Sopenharmony_ci 4481cb0ef41Sopenharmony_ciThe arguments of `createBenchmark` are: 4491cb0ef41Sopenharmony_ci 4501cb0ef41Sopenharmony_ci* `main` {Function} The benchmark function, 4511cb0ef41Sopenharmony_ci where the code running operations and controlling timers should go 4521cb0ef41Sopenharmony_ci* `configs` {Object} The benchmark parameters. `createBenchmark` will run all 4531cb0ef41Sopenharmony_ci possible combinations of these parameters, unless specified otherwise. 4541cb0ef41Sopenharmony_ci Each configuration is a property with an array of possible values. 4551cb0ef41Sopenharmony_ci The configuration values can only be strings or numbers. 4561cb0ef41Sopenharmony_ci* `options` {Object} The benchmark options. Supported options: 4571cb0ef41Sopenharmony_ci * `flags` {Array} Contains node-specific command line flags to pass to 4581cb0ef41Sopenharmony_ci the child process. 4591cb0ef41Sopenharmony_ci * `combinationFilter` {Function} Has a single parameter which is an object 4601cb0ef41Sopenharmony_ci containing a combination of benchmark parameters. It should return `true` 4611cb0ef41Sopenharmony_ci or `false` to indicate whether the combination should be included or not. 4621cb0ef41Sopenharmony_ci 4631cb0ef41Sopenharmony_ci`createBenchmark` returns a `bench` object, which is used for timing 4641cb0ef41Sopenharmony_cithe runtime of the benchmark. Run `bench.start()` after the initialization 4651cb0ef41Sopenharmony_ciand `bench.end(n)` when the benchmark is done. `n` is the number of operations 4661cb0ef41Sopenharmony_ciperformed in the benchmark. 4671cb0ef41Sopenharmony_ci 4681cb0ef41Sopenharmony_ciThe benchmark script will be run twice: 4691cb0ef41Sopenharmony_ci 4701cb0ef41Sopenharmony_ciThe first pass will configure the benchmark with the combination of 4711cb0ef41Sopenharmony_ciparameters specified in `configs`, and WILL NOT run the `main` function. 4721cb0ef41Sopenharmony_ciIn this pass, no flags except the ones directly passed via commands 4731cb0ef41Sopenharmony_ciwhen running the benchmarks will be used. 4741cb0ef41Sopenharmony_ci 4751cb0ef41Sopenharmony_ciIn the second pass, the `main` function will be run, and the process 4761cb0ef41Sopenharmony_ciwill be launched with: 4771cb0ef41Sopenharmony_ci 4781cb0ef41Sopenharmony_ci* The flags passed into `createBenchmark` (the third argument) 4791cb0ef41Sopenharmony_ci* The flags in the command passed when the benchmark was run 4801cb0ef41Sopenharmony_ci 4811cb0ef41Sopenharmony_ciBeware that any code outside the `main` function will be run twice 4821cb0ef41Sopenharmony_ciin different processes. This could be troublesome if the code 4831cb0ef41Sopenharmony_cioutside the `main` function has side effects. In general, prefer putting 4841cb0ef41Sopenharmony_cithe code inside the `main` function if it's more than just declaration. 4851cb0ef41Sopenharmony_ci 4861cb0ef41Sopenharmony_ci```js 4871cb0ef41Sopenharmony_ci'use strict'; 4881cb0ef41Sopenharmony_ciconst common = require('../common.js'); 4891cb0ef41Sopenharmony_ciconst { SlowBuffer } = require('node:buffer'); 4901cb0ef41Sopenharmony_ci 4911cb0ef41Sopenharmony_ciconst configs = { 4921cb0ef41Sopenharmony_ci // Number of operations, specified here so they show up in the report. 4931cb0ef41Sopenharmony_ci // Most benchmarks just use one value for all runs. 4941cb0ef41Sopenharmony_ci n: [1024], 4951cb0ef41Sopenharmony_ci type: ['fast', 'slow'], // Custom configurations 4961cb0ef41Sopenharmony_ci size: [16, 128, 1024], // Custom configurations 4971cb0ef41Sopenharmony_ci}; 4981cb0ef41Sopenharmony_ci 4991cb0ef41Sopenharmony_ciconst options = { 5001cb0ef41Sopenharmony_ci // Add --expose-internals in order to require internal modules in main 5011cb0ef41Sopenharmony_ci flags: ['--zero-fill-buffers'], 5021cb0ef41Sopenharmony_ci}; 5031cb0ef41Sopenharmony_ci 5041cb0ef41Sopenharmony_ci// `main` and `configs` are required, `options` is optional. 5051cb0ef41Sopenharmony_ciconst bench = common.createBenchmark(main, configs, options); 5061cb0ef41Sopenharmony_ci 5071cb0ef41Sopenharmony_ci// Any code outside main will be run twice, 5081cb0ef41Sopenharmony_ci// in different processes, with different command line arguments. 5091cb0ef41Sopenharmony_ci 5101cb0ef41Sopenharmony_cifunction main(conf) { 5111cb0ef41Sopenharmony_ci // Only flags that have been passed to createBenchmark 5121cb0ef41Sopenharmony_ci // earlier when main is run will be in effect. 5131cb0ef41Sopenharmony_ci // In order to benchmark the internal modules, require them here. For example: 5141cb0ef41Sopenharmony_ci // const URL = require('internal/url').URL 5151cb0ef41Sopenharmony_ci 5161cb0ef41Sopenharmony_ci // Start the timer 5171cb0ef41Sopenharmony_ci bench.start(); 5181cb0ef41Sopenharmony_ci 5191cb0ef41Sopenharmony_ci // Do operations here 5201cb0ef41Sopenharmony_ci const BufferConstructor = conf.type === 'fast' ? Buffer : SlowBuffer; 5211cb0ef41Sopenharmony_ci 5221cb0ef41Sopenharmony_ci for (let i = 0; i < conf.n; i++) { 5231cb0ef41Sopenharmony_ci new BufferConstructor(conf.size); 5241cb0ef41Sopenharmony_ci } 5251cb0ef41Sopenharmony_ci 5261cb0ef41Sopenharmony_ci // End the timer, pass in the number of operations 5271cb0ef41Sopenharmony_ci bench.end(conf.n); 5281cb0ef41Sopenharmony_ci} 5291cb0ef41Sopenharmony_ci``` 5301cb0ef41Sopenharmony_ci 5311cb0ef41Sopenharmony_ci### Creating an HTTP benchmark 5321cb0ef41Sopenharmony_ci 5331cb0ef41Sopenharmony_ciThe `bench` object returned by `createBenchmark` implements 5341cb0ef41Sopenharmony_ci`http(options, callback)` method. It can be used to run external tool to 5351cb0ef41Sopenharmony_cibenchmark HTTP servers. 5361cb0ef41Sopenharmony_ci 5371cb0ef41Sopenharmony_ci```js 5381cb0ef41Sopenharmony_ci'use strict'; 5391cb0ef41Sopenharmony_ci 5401cb0ef41Sopenharmony_ciconst common = require('../common.js'); 5411cb0ef41Sopenharmony_ci 5421cb0ef41Sopenharmony_ciconst bench = common.createBenchmark(main, { 5431cb0ef41Sopenharmony_ci kb: [64, 128, 256, 1024], 5441cb0ef41Sopenharmony_ci connections: [100, 500], 5451cb0ef41Sopenharmony_ci duration: 5, 5461cb0ef41Sopenharmony_ci}); 5471cb0ef41Sopenharmony_ci 5481cb0ef41Sopenharmony_cifunction main(conf) { 5491cb0ef41Sopenharmony_ci const http = require('node:http'); 5501cb0ef41Sopenharmony_ci const len = conf.kb * 1024; 5511cb0ef41Sopenharmony_ci const chunk = Buffer.alloc(len, 'x'); 5521cb0ef41Sopenharmony_ci const server = http.createServer((req, res) => { 5531cb0ef41Sopenharmony_ci res.end(chunk); 5541cb0ef41Sopenharmony_ci }); 5551cb0ef41Sopenharmony_ci 5561cb0ef41Sopenharmony_ci server.listen(common.PORT, () => { 5571cb0ef41Sopenharmony_ci bench.http({ 5581cb0ef41Sopenharmony_ci connections: conf.connections, 5591cb0ef41Sopenharmony_ci }, () => { 5601cb0ef41Sopenharmony_ci server.close(); 5611cb0ef41Sopenharmony_ci }); 5621cb0ef41Sopenharmony_ci }); 5631cb0ef41Sopenharmony_ci} 5641cb0ef41Sopenharmony_ci``` 5651cb0ef41Sopenharmony_ci 5661cb0ef41Sopenharmony_ciSupported options keys are: 5671cb0ef41Sopenharmony_ci 5681cb0ef41Sopenharmony_ci* `port` - defaults to `common.PORT` 5691cb0ef41Sopenharmony_ci* `path` - defaults to `/` 5701cb0ef41Sopenharmony_ci* `connections` - number of concurrent connections to use, defaults to 100 5711cb0ef41Sopenharmony_ci* `duration` - duration of the benchmark in seconds, defaults to 10 5721cb0ef41Sopenharmony_ci* `benchmarker` - benchmarker to use, defaults to the first available http 5731cb0ef41Sopenharmony_ci benchmarker 5741cb0ef41Sopenharmony_ci 5751cb0ef41Sopenharmony_ci[autocannon]: https://github.com/mcollina/autocannon 5761cb0ef41Sopenharmony_ci[benchmark-ci]: https://github.com/nodejs/benchmarking/blob/HEAD/docs/core_benchmarks.md 5771cb0ef41Sopenharmony_ci[git-for-windows]: https://git-scm.com/download/win 5781cb0ef41Sopenharmony_ci[nghttp2.org]: https://nghttp2.org 5791cb0ef41Sopenharmony_ci[node-benchmark-compare]: https://github.com/targos/node-benchmark-compare 5801cb0ef41Sopenharmony_ci[t-test]: https://en.wikipedia.org/wiki/Student%27s_t-test#Equal_or_unequal_sample_sizes%2C_unequal_variances_%28sX1_%3E_2sX2_or_sX2_%3E_2sX1%29 5811cb0ef41Sopenharmony_ci[wrk]: https://github.com/wg/wrk 582