Skip to content
Grant Carthew edited this page Oct 17, 2024 · 73 revisions

perj Performance and Benchmark Tests

One of the goals of perj was to ensure the logger worked as fast as possible. This document gives a rundown on the benchmarking process.

perj vs pino

To start with, it is worth noting that perj is designed to be used as a building block and cross-platform out of the box. In comparison, pino is a fully mature feature rich Node.js logger. You should really check it out.

Testing the performance of perj has been a simple process thanks to the logging benchmark being set by pino. By running benchmarks that compare perj to pino we can see if perj can stand up to the tried and true standard.

If you review the primary goals of the perj logger, you will see that Fast is not on the top of the list. It is still a goal of this package, however not at the expense of the API or browser support. With perj you can log any number of strings or objects in any order. This was a design decision from the start to enable rich content in log entries. Also, perj can be used in the browser as well as it can be used in Node.js.

When logging Error objects with perj, the error object is serialized recursively, looking for any extra data added to the object. This was also a design decision with the knowledge that logging Error objects would be slow compared to pino. Keeping in mind that exceptions should be at a minimum, priority on error content trumped performance. You can change this serialization function using the serializeErrorFunction option.

It is worth noting that pino has an advanced option being extreme mode. If you enable this option, pino will outperform perj. Checkout the pino benchmarks for more detail.

Benchmark Operations

To determine the performance of perj, a benchmark file performs different logging operations. The benchmark jobs are also performed against pino as a comparison.

For the benchmark tests, both logging packages are configured to have a similar payload when logging a simple message.

log.info('hello world')

/*

perj will log the following for the benchmark tests:
{"level":30,"pid":238226,"hostname":"dev","v":1,"time":1528345978602,"msg":"hello world","data":""}

pino will log the following for the benchmark tests:
{"level":30,"time":1526111032989,"msg":"hello world","pid":239309,"hostname":"Dev","v":1}

*/

Benchmark Results

Environment

  • Lenovo Legion Pro 7i Intel Core i9-14900HX
  • Node.js v22.9.0
  • perj v4.0.0
  • pino v9.5.0
  • Date: 2024-10-17

Results

================================================================================
 perj vs pino Benchmark
================================================================================
NOTE: pino is not using extreme mode.
perj Common Log Operations x 204,700 ops/sec ±1.31% (90 runs sampled)
pino Common Log Operations x 277,287 ops/sec ±0.25% (97 runs sampled)
perj Single String Message x 3,052,397 ops/sec ±2.20% (81 runs sampled)
pino Single String Message x 2,097,915 ops/sec ±1.97% (84 runs sampled)
perj Single Long String x 52,440 ops/sec ±2.37% (89 runs sampled)
pino Single Long String x 8,638 ops/sec ±0.45% (97 runs sampled)
perj Flat Object Data x 1,609,021 ops/sec ±2.84% (85 runs sampled)
pino Flat Object Data x 1,488,158 ops/sec ±1.65% (94 runs sampled)
perj Simple Object Data x 797,010 ops/sec ±0.74% (97 runs sampled)
pino Simple Object Data x 812,983 ops/sec ±0.25% (96 runs sampled)
perj Complex Object Data x 224,077 ops/sec ±1.28% (95 runs sampled)
pino Complex Object Data x 328,022 ops/sec ±0.41% (100 runs sampled)
perj Deep Object Data x 37,589 ops/sec ±0.37% (99 runs sampled)
pino Deep Object Data x 61,721 ops/sec ±0.25% (97 runs sampled)
perj Single String And Object Data x 1,726,538 ops/sec ±2.69% (91 runs sampled)
pino Single String And Object Data x 2,058,570 ops/sec ±1.62% (92 runs sampled)
perj Logging Error Objects x 138,373 ops/sec ±0.63% (96 runs sampled)
pino Logging Error Objects x 121,419 ops/sec ±1.10% (95 runs sampled)
perj Create Single Child x 817,463 ops/sec ±0.25% (95 runs sampled)
pino Create Single Child x 1,166,109 ops/sec ±2.38% (95 runs sampled)
perj Create Two Children x 139,502 ops/sec ±0.22% (95 runs sampled)
pino Create Two Children x 106,129 ops/sec ±0.26% (98 runs sampled)
perj Create Three Children x 79,573 ops/sec ±0.25% (97 runs sampled)
pino Create Three Children x 53,808 ops/sec ±0.27% (96 runs sampled)
╔═══════════════════════════════╤════════════╤════════════╤═════════════════════╗
║ Benchmark Ops/Sec             │ perj       │ pino       │ Result              ║
╟───────────────────────────────┼────────────┼────────────┼─────────────────────╢
║ Common Log Operations         │ 204699.67  │ 277287.37  │ pino: 26.18% faster ║
╟───────────────────────────────┼────────────┼────────────┼─────────────────────╢
║ Single String Message         │ 3052397.05 │ 2097915.25 │ perj: 31.27% faster ║
╟───────────────────────────────┼────────────┼────────────┼─────────────────────╢
║ Single Long String            │ 52440.26   │ 8638.30    │ perj: 83.53% faster ║
╟───────────────────────────────┼────────────┼────────────┼─────────────────────╢
║ Flat Object Data              │ 1609021.08 │ 1488158.14 │ perj: 7.51% faster  ║
╟───────────────────────────────┼────────────┼────────────┼─────────────────────╢
║ Simple Object Data            │ 797010.26  │ 812982.92  │ pino: 1.96% faster  ║
╟───────────────────────────────┼────────────┼────────────┼─────────────────────╢
║ Complex Object Data           │ 224077.01  │ 328021.97  │ pino: 31.69% faster ║
╟───────────────────────────────┼────────────┼────────────┼─────────────────────╢
║ Deep Object Data              │ 37588.52   │ 61721.49   │ pino: 39.10% faster ║
╟───────────────────────────────┼────────────┼────────────┼─────────────────────╢
║ Single String And Object Data │ 1726538.34 │ 2058570.15 │ pino: 16.13% faster ║
╟───────────────────────────────┼────────────┼────────────┼─────────────────────╢
║ Logging Error Objects         │ 138372.70  │ 121419.21  │ perj: 12.25% faster ║
╟───────────────────────────────┼────────────┼────────────┼─────────────────────╢
║ Create Single Child           │ 817462.78  │ 1166109.36 │ pino: 29.90% faster ║
╟───────────────────────────────┼────────────┼────────────┼─────────────────────╢
║ Create Two Children           │ 139502.33  │ 106129.32  │ perj: 23.92% faster ║
╟───────────────────────────────┼────────────┼────────────┼─────────────────────╢
║ Create Three Children         │ 79572.90   │ 53808.27   │ perj: 32.38% faster ║
╚═══════════════════════════════╧════════════╧════════════╧═════════════════════╝

Run The Test

If you would like to try out the benchmark yourself, follow these steps:

  1. Clone the perj repository:

git clone https://github.com/grantcarthew/node-perj

  1. Change directory into node-perj and install the package dependencies:

npm install

  1. The pino logger is not a dependency of the perj package. You will need to install pino:

npm install pino --no-save.

  1. Run the benchmark:

npm run benchmark.