Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

benchmark: add initial support for benchmark coverage #54333

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

RafaelGSS
Copy link
Member

@RafaelGSS RafaelGSS commented Aug 12, 2024

Hey,

I'm opening it as a draft as there are a lot of loose ends to solve before considering such usage. This is an experiment related to research I'm conducting.

The idea is to generate a benchmark coverage for all of our exported modules. For instance, check if the functions exposed by require('node:fs') are covered in our benchmark suite (benchmark/fs/*.js). To achieve an intermediary goal I had to monkey-patch the Module.prototype.require before executing each benchmark (we run each one in a separate process) to return a singleton that basically manages the state of how many times that function was called.

I have tried to use our test_runner coverage for that, but it couldn't identify the location of built-in modules (require('node:*')) which is expected by the nature of a coverage tool. The other reason it didn't fit was the need to cover only the exported modules, not the lib/internal/* functions. The result I wanted to have was: "I need to know which functions/classes that are exposed to users do not contain a benchmark".

It's also worth it to mention that, a nested call is ignored in the benchmark report. Example:

  • benchmark/foo/foo.js
const bench = common.createBenchmark(main, { ... }});
const fs = require('node:fs')

function main({ ... }) {
  bench.start()
  fs.exists(__filename, () => {
    bench.stop(n)
  });
}
  • fs.exists calls fs.access behind the scenes

    node/lib/fs.js

    Line 260 in b8a2550

    fs.access(path, F_OK, suppressedCallback);
  • I didn't want this benchmark to consider both fs.exists and fs.access as "covered" in the benchmark report. It should only count fs.exists as covered.
  • To achieve that I'm getting the last callSite and checking if the path is benchmark/**/*.js

Results

Please note that this is not accurate (in terms of Classes) for the reason described in the limitations section

┌─────────┬──────────────────┬────────┐
│ (index) │ node:assert      │ Values │
├─────────┼──────────────────┼────────┤
│ 0       │ 'fail'           │ 0      │
│ 1       │ 'AssertionError' │ 0      │
│ 2       │ 'equal'          │ 0      │
│ 3       │ 'notEqual'       │ 0      │
│ 4       │ 'notStrictEqual' │ 0      │
│ 5       │ 'throws'         │ 0      │
│ 6       │ 'rejects'        │ 0      │
│ 7       │ 'doesNotThrow'   │ 0      │
│ 8       │ 'doesNotReject'  │ 0      │
│ 9       │ 'match'          │ 0      │
│ 10      │ 'doesNotMatch'   │ 0      │
│ 11      │ 'CallTracker'    │ 0      │
│ 12      │ 'strict'         │ 0      │
└─────────┴──────────────────┴────────┘
┌─────────┬─────────────┬────────┐
│ (index) │ node:buffer │ Values │
├─────────┼─────────────┼────────┤
│ 0       │ 'Buffer'    │ 0      │
│ 1       │ 'transcode' │ 0      │
│ 2       │ 'isUtf8'    │ 0      │
│ 3       │ 'isAscii'   │ 0      │
│ 4       │ 'btoa'      │ 0      │
│ 5       │ 'atob'      │ 0      │
│ 6       │ 'Blob'      │ 0      │
│ 7       │ 'File'      │ 0      │
└─────────┴─────────────┴────────┘
┌─────────┬────────────────────┬────────┐
│ (index) │ node:child_process │ Values │
├─────────┼────────────────────┼────────┤
│ 0       │ 'ChildProcess'     │ 0      │
│ 1       │ 'fork'             │ 0      │
└─────────┴────────────────────┴────────┘
┌─────────┬──────────────────┬────────┐
│ (index) │ node:console     │ Values │
├─────────┼──────────────────┼────────┤
│ 0       │ 'log'            │ 0      │
│ 1       │ 'warn'           │ 0      │
│ 2       │ 'error'          │ 0      │
│ 3       │ 'dir'            │ 0      │
│ 4       │ 'time'           │ 0      │
│ 5       │ 'timeEnd'        │ 0      │
│ 6       │ 'timeLog'        │ 0      │
│ 7       │ 'trace'          │ 0      │
│ 8       │ 'assert'         │ 0      │
│ 9       │ 'clear'          │ 0      │
│ 10      │ 'count'          │ 0      │
│ 11      │ 'countReset'     │ 0      │
│ 12      │ 'group'          │ 0      │
│ 13      │ 'groupEnd'       │ 0      │
│ 14      │ 'table'          │ 0      │
│ 15      │ 'debug'          │ 0      │
│ 16      │ 'info'           │ 0      │
│ 17      │ 'dirxml'         │ 0      │
│ 18      │ 'groupCollapsed' │ 0      │
│ 19      │ 'Console'        │ 0      │
│ 20      │ 'profile'        │ 0      │
│ 21      │ 'profileEnd'     │ 0      │
│ 22      │ 'timeStamp'      │ 0      │
│ 23      │ 'context'        │ 0      │
│ 24      │ 'createTask'     │ 0      │
└─────────┴──────────────────┴────────┘
┌─────────┬────────────────────────────┬────────┐
│ (index) │ node:crypto                │ Values │
├─────────┼────────────────────────────┼────────┤
│ 0       │ 'checkPrime'               │ 0      │
│ 1       │ 'checkPrimeSync'           │ 0      │
│ 2       │ 'createDiffieHellman'      │ 0      │
│ 3       │ 'createDiffieHellmanGroup' │ 0      │
│ 4       │ 'createECDH'               │ 0      │
│ 5       │ 'createHmac'               │ 0      │
│ 6       │ 'createSecretKey'          │ 0      │
│ 7       │ 'diffieHellman'            │ 0      │
│ 8       │ 'generatePrime'            │ 0      │
│ 9       │ 'generatePrimeSync'        │ 0      │
│ 10      │ 'getCipherInfo'            │ 0      │
│ 11      │ 'getCurves'                │ 0      │
│ 12      │ 'getDiffieHellman'         │ 0      │
│ 13      │ 'getHashes'                │ 0      │
│ 14      │ 'pbkdf2'                   │ 0      │
│ 15      │ 'pbkdf2Sync'               │ 0      │
│ 16      │ 'generateKey'              │ 0      │
│ 17      │ 'generateKeySync'          │ 0      │
│ 18      │ 'privateDecrypt'           │ 0      │
│ 19      │ 'publicEncrypt'            │ 0      │
│ 20      │ 'randomFill'               │ 0      │
│ 21      │ 'randomFillSync'           │ 0      │
│ 22      │ 'scrypt'                   │ 0      │
│ 23      │ 'scryptSync'               │ 0      │
│ 24      │ 'setEngine'                │ 0      │
│ 25      │ 'getFips'                  │ 0      │
│ 26      │ 'setFips'                  │ 0      │
│ 27      │ 'Certificate'              │ 0      │
│ 28      │ 'Cipheriv'                 │ 0      │
│ 29      │ 'Decipheriv'               │ 0      │
│ 30      │ 'DiffieHellman'            │ 0      │
│ 31      │ 'DiffieHellmanGroup'       │ 0      │
│ 32      │ 'ECDH'                     │ 0      │
│ 33      │ 'Hash'                     │ 0      │
│ 34      │ 'Hmac'                     │ 0      │
│ 35      │ 'KeyObject'                │ 0      │
│ 36      │ 'Sign'                     │ 0      │
│ 37      │ 'Verify'                   │ 0      │
│ 38      │ 'X509Certificate'          │ 0      │
│ 39      │ 'secureHeapUsed'           │ 0      │
│ 40      │ 'getRandomValues'          │ 0      │
└─────────┴────────────────────────────┴────────┘
┌─────────┬────────────┬────────┐
│ (index) │ node:dgram │ Values │
├─────────┼────────────┼────────┤
│ 0       │ 'Socket'   │ 0      │
└─────────┴────────────┴────────┘
┌─────────┬──────────────────────────┬────────┐
│ (index) │ node:diagnostics_channel │ Values │
├─────────┼──────────────────────────┼────────┤
│ 0       │ 'hasSubscribers'         │ 0      │
│ 1       │ 'subscribe'              │ 0      │
│ 2       │ 'tracingChannel'         │ 0      │
│ 3       │ 'unsubscribe'            │ 0      │
│ 4       │ 'Channel'                │ 0      │
└─────────┴──────────────────────────┴────────┘
┌─────────┬─────────────────────────┬────────┐
│ (index) │ node:dns                │ Values │
├─────────┼─────────────────────────┼────────┤
│ 0       │ 'lookupService'         │ 0      │
│ 1       │ 'Resolver'              │ 0      │
│ 2       │ 'getDefaultResultOrder' │ 0      │
│ 3       │ 'setDefaultResultOrder' │ 0      │
│ 4       │ 'setServers'            │ 0      │
│ 5       │ 'getServers'            │ 0      │
│ 6       │ 'resolve'               │ 0      │
│ 7       │ 'resolve4'              │ 0      │
│ 8       │ 'resolve6'              │ 0      │
│ 9       │ 'resolveAny'            │ 0      │
│ 10      │ 'resolveCaa'            │ 0      │
│ 11      │ 'resolveCname'          │ 0      │
│ 12      │ 'resolveMx'             │ 0      │
│ 13      │ 'resolveNaptr'          │ 0      │
│ 14      │ 'resolveNs'             │ 0      │
│ 15      │ 'resolvePtr'            │ 0      │
│ 16      │ 'resolveSoa'            │ 0      │
│ 17      │ 'resolveSrv'            │ 0      │
│ 18      │ 'resolveTxt'            │ 0      │
│ 19      │ 'reverse'               │ 0      │
└─────────┴─────────────────────────┴────────┘
┌─────────┬────────────────┬────────┐
│ (index) │ node:domain    │ Values │
├─────────┼────────────────┼────────┤
│ 0       │ 'Domain'       │ 0      │
│ 1       │ 'createDomain' │ 0      │
└─────────┴────────────────┴────────┘
┌─────────┬─────────────────────────────┬────────┐
│ (index) │ node:events                 │ Values │
├─────────┼─────────────────────────────┼────────┤
│ 0       │ 'addAbortListener'          │ 0      │
│ 1       │ 'once'                      │ 0      │
│ 2       │ 'on'                        │ 0      │
│ 3       │ 'getEventListeners'         │ 0      │
│ 4       │ 'getMaxListeners'           │ 0      │
│ 5       │ 'EventEmitterAsyncResource' │ 0      │
│ 6       │ 'setMaxListeners'           │ 0      │
│ 7       │ 'init'                      │ 0      │
│ 8       │ 'listenerCount'             │ 0      │
└─────────┴─────────────────────────────┴────────┘
┌─────────┬───────────────────┬────────┐
│ (index) │ node:fs           │ Values │
├─────────┼───────────────────┼────────┤
│ 0       │ 'appendFile'      │ 0      │
│ 1       │ 'access'          │ 0      │
│ 2       │ 'chown'           │ 0      │
│ 3       │ 'chmod'           │ 0      │
│ 4       │ 'close'           │ 0      │
│ 5       │ 'copyFile'        │ 0      │
│ 6       │ 'cp'              │ 0      │
│ 7       │ 'exists'          │ 0      │
│ 8       │ 'fchown'          │ 0      │
│ 9       │ 'fchownSync'      │ 0      │
│ 10      │ 'fchmod'          │ 0      │
│ 11      │ 'fdatasync'       │ 0      │
│ 12      │ 'fsync'           │ 0      │
│ 13      │ 'ftruncate'       │ 0      │
│ 14      │ 'futimes'         │ 0      │
│ 15      │ 'glob'            │ 0      │
│ 16      │ 'globSync'        │ 0      │
│ 17      │ 'lchown'          │ 0      │
│ 18      │ 'link'            │ 0      │
│ 19      │ 'lutimes'         │ 0      │
│ 20      │ 'mkdtemp'         │ 0      │
│ 21      │ 'openAsBlob'      │ 0      │
│ 22      │ 'read'            │ 0      │
│ 23      │ 'readv'           │ 0      │
│ 24      │ 'readlink'        │ 0      │
│ 25      │ 'rename'          │ 0      │
│ 26      │ 'rm'              │ 0      │
│ 27      │ 'rmdir'           │ 0      │
│ 28      │ 'statfs'          │ 0      │
│ 29      │ 'statfsSync'      │ 0      │
│ 30      │ 'symlink'         │ 0      │
│ 31      │ 'truncate'        │ 0      │
│ 32      │ 'truncateSync'    │ 0      │
│ 33      │ 'unwatchFile'     │ 0      │
│ 34      │ 'unlink'          │ 0      │
│ 35      │ 'utimes'          │ 0      │
│ 36      │ 'watch'           │ 0      │
│ 37      │ 'watchFile'       │ 0      │
│ 38      │ 'writeFile'       │ 0      │
│ 39      │ 'write'           │ 0      │
│ 40      │ 'writeSync'       │ 0      │
│ 41      │ 'writev'          │ 0      │
│ 42      │ 'Dirent'          │ 0      │
│ 43      │ 'Stats'           │ 0      │
│ 44      │ 'ReadStream'      │ 0      │
│ 45      │ 'WriteStream'     │ 0      │
│ 46      │ 'FileReadStream'  │ 0      │
│ 47      │ 'FileWriteStream' │ 0      │
│ 48      │ 'Dir'             │ 0      │
│ 49      │ 'opendir'         │ 0      │
└─────────┴───────────────────┴────────┘
┌─────────┬─────────────────────────┬────────┐
│ (index) │ node:http               │ Values │
├─────────┼─────────────────────────┼────────┤
│ 0       │ 'Agent'                 │ 0      │
│ 1       │ 'IncomingMessage'       │ 0      │
│ 2       │ 'OutgoingMessage'       │ 0      │
│ 3       │ 'Server'                │ 0      │
│ 4       │ 'ServerResponse'        │ 0      │
│ 5       │ 'validateHeaderName'    │ 0      │
│ 6       │ 'validateHeaderValue'   │ 0      │
│ 7       │ 'get'                   │ 0      │
│ 8       │ 'request'               │ 0      │
│ 9       │ 'setMaxIdleHTTPParsers' │ 0      │
│ 10      │ 'WebSocket'             │ 0      │
│ 11      │ 'CloseEvent'            │ 0      │
│ 12      │ 'MessageEvent'          │ 0      │
└─────────┴─────────────────────────┴────────┘
┌─────────┬──────────────────────────┬────────┐
│ (index) │ node:http2               │ Values │
├─────────┼──────────────────────────┼────────┤
│ 0       │ 'createSecureServer'     │ 0      │
│ 1       │ 'getDefaultSettings'     │ 0      │
│ 2       │ 'getPackedSettings'      │ 0      │
│ 3       │ 'getUnpackedSettings'    │ 0      │
│ 4       │ 'performServerHandshake' │ 0      │
│ 5       │ 'Http2ServerRequest'     │ 0      │
│ 6       │ 'Http2ServerResponse'    │ 0      │
└─────────┴──────────────────────────┴────────┘
┌─────────┬────────────┬────────┐
│ (index) │ node:https │ Values │
├─────────┼────────────┼────────┤
│ 0       │ 'Agent'    │ 0      │
│ 1       │ 'Server'   │ 0      │
│ 2       │ 'get'      │ 0      │
│ 3       │ 'request'  │ 0      │
└─────────┴────────────┴────────┘
┌─────────┬───────────────────┬────────┐
│ (index) │ node:inspector    │ Values │
├─────────┼───────────────────┼────────┤
│ 0       │ 'open'            │ 0      │
│ 1       │ 'close'           │ 0      │
│ 2       │ 'url'             │ 0      │
│ 3       │ 'waitForDebugger' │ 0      │
│ 4       │ 'Session'         │ 0      │
└─────────┴───────────────────┴────────┘
┌─────────┬─────────────────────────┬────────┐
│ (index) │ node:module             │ Values │
├─────────┼─────────────────────────┼────────┤
│ 0       │ 'isBuiltin'             │ 0      │
│ 1       │ 'createRequire'         │ 0      │
│ 2       │ 'syncBuiltinESMExports' │ 0      │
│ 3       │ 'Module'                │ 0      │
│ 4       │ 'runMain'               │ 0      │
│ 5       │ 'findSourceMap'         │ 0      │
│ 6       │ 'register'              │ 0      │
│ 7       │ 'SourceMap'             │ 0      │
└─────────┴─────────────────────────┴────────┘
┌─────────┬────────────────────────────────────────────┬────────┐
│ (index) │ node:net                                   │ Values │
├─────────┼────────────────────────────────────────────┼────────┤
│ 0       │ 'BlockList'                                │ 0      │
│ 1       │ 'SocketAddress'                            │ 0      │
│ 2       │ 'createConnection'                         │ 0      │
│ 3       │ 'isIP'                                     │ 0      │
│ 4       │ 'Server'                                   │ 0      │
│ 5       │ 'Socket'                                   │ 0      │
│ 6       │ 'Stream'                                   │ 0      │
│ 7       │ 'getDefaultAutoSelectFamily'               │ 0      │
│ 8       │ 'setDefaultAutoSelectFamily'               │ 0      │
│ 9       │ 'getDefaultAutoSelectFamilyAttemptTimeout' │ 0      │
│ 10      │ 'setDefaultAutoSelectFamilyAttemptTimeout' │ 0      │
└─────────┴────────────────────────────────────────────┴────────┘
┌─────────┬────────────────────────┬────────┐
│ (index) │ node:os                │ Values │
├─────────┼────────────────────────┼────────┤
│ 0       │ 'arch'                 │ 0      │
│ 1       │ 'availableParallelism' │ 0      │
│ 2       │ 'endianness'           │ 0      │
│ 3       │ 'freemem'              │ 0      │
│ 4       │ 'getPriority'          │ 0      │
│ 5       │ 'release'              │ 0      │
│ 6       │ 'setPriority'          │ 0      │
│ 7       │ 'tmpdir'               │ 0      │
│ 8       │ 'totalmem'             │ 0      │
│ 9       │ 'userInfo'             │ 0      │
│ 10      │ 'version'              │ 0      │
│ 11      │ 'machine'              │ 0      │
└─────────┴────────────────────────┴────────┘
┌─────────┬────────────────────┬────────┐
│ (index) │ node:path          │ Values │
├─────────┼────────────────────┼────────┤
│ 0       │ 'normalize'        │ 0      │
│ 1       │ 'isAbsolute'       │ 0      │
│ 2       │ 'toNamespacedPath' │ 0      │
│ 3       │ 'dirname'          │ 0      │
│ 4       │ 'basename'         │ 0      │
│ 5       │ 'extname'          │ 0      │
│ 6       │ 'format'           │ 0      │
│ 7       │ 'parse'            │ 0      │
│ 8       │ 'matchesGlob'      │ 0      │
└─────────┴────────────────────┴────────┘
┌─────────┬────────────────────────────────┬────────┐
│ (index) │ node:perf_hooks                │ Values │
├─────────┼────────────────────────────────┼────────┤
│ 0       │ 'Performance'                  │ 0      │
│ 1       │ 'PerformanceEntry'             │ 0      │
│ 2       │ 'PerformanceMark'              │ 0      │
│ 3       │ 'PerformanceMeasure'           │ 0      │
│ 4       │ 'PerformanceObserver'          │ 0      │
│ 5       │ 'PerformanceObserverEntryList' │ 0      │
│ 6       │ 'PerformanceResourceTiming'    │ 0      │
│ 7       │ 'monitorEventLoopDelay'        │ 0      │
└─────────┴────────────────────────────────┴────────┘
┌─────────┬───────────────────────────────────────┬────────┐
│ (index) │ node:process                          │ Values │
├─────────┼───────────────────────────────────────┼────────┤
│ 0       │ 'binding'                             │ 0      │
│ 1       │ 'dlopen'                              │ 0      │
│ 2       │ 'uptime'                              │ 0      │
│ 3       │ 'getActiveResourcesInfo'              │ 0      │
│ 4       │ 'reallyExit'                          │ 0      │
│ 5       │ 'loadEnvFile'                         │ 0      │
│ 6       │ 'cpuUsage'                            │ 0      │
│ 7       │ 'resourceUsage'                       │ 0      │
│ 8       │ 'memoryUsage'                         │ 0      │
│ 9       │ 'constrainedMemory'                   │ 0      │
│ 10      │ 'availableMemory'                     │ 0      │
│ 11      │ 'kill'                                │ 0      │
│ 12      │ 'exit'                                │ 0      │
│ 13      │ 'hrtime'                              │ 0      │
│ 14      │ 'openStdin'                           │ 0      │
│ 15      │ 'getuid'                              │ 0      │
│ 16      │ 'geteuid'                             │ 0      │
│ 17      │ 'getgid'                              │ 0      │
│ 18      │ 'getegid'                             │ 0      │
│ 19      │ 'getgroups'                           │ 0      │
│ 20      │ 'assert'                              │ 0      │
│ 21      │ 'setUncaughtExceptionCaptureCallback' │ 0      │
│ 22      │ 'hasUncaughtExceptionCaptureCallback' │ 0      │
│ 23      │ 'emitWarning'                         │ 0      │
│ 24      │ 'nextTick'                            │ 0      │
│ 25      │ 'setSourceMapsEnabled'                │ 0      │
│ 26      │ 'getBuiltinModule'                    │ 0      │
│ 27      │ 'abort'                               │ 0      │
│ 28      │ 'umask'                               │ 0      │
│ 29      │ 'chdir'                               │ 0      │
│ 30      │ 'cwd'                                 │ 0      │
│ 31      │ 'initgroups'                          │ 0      │
│ 32      │ 'setgroups'                           │ 0      │
│ 33      │ 'setegid'                             │ 0      │
│ 34      │ 'seteuid'                             │ 0      │
│ 35      │ 'setgid'                              │ 0      │
│ 36      │ 'setuid'                              │ 0      │
└─────────┴───────────────────────────────────────┴────────┘
┌─────────┬───────────────┬────────┐
│ (index) │ node:punycode │ Values │
├─────────┼───────────────┼────────┤
│ 0       │ 'decode'      │ 0      │
│ 1       │ 'encode'      │ 0      │
└─────────┴───────────────┴────────┘
┌─────────┬──────────────────┬────────┐
│ (index) │ node:querystring │ Values │
├─────────┼──────────────────┼────────┤
│ 0       │ 'unescape'       │ 0      │
│ 1       │ 'escape'         │ 0      │
│ 2       │ 'encode'         │ 0      │
│ 3       │ 'decode'         │ 0      │
└─────────┴──────────────────┴────────┘
┌─────────┬──────────────────────┬────────┐
│ (index) │ node:readline        │ Values │
├─────────┼──────────────────────┼────────┤
│ 0       │ 'Interface'          │ 0      │
│ 1       │ 'clearLine'          │ 0      │
│ 2       │ 'clearScreenDown'    │ 0      │
│ 3       │ 'cursorTo'           │ 0      │
│ 4       │ 'emitKeypressEvents' │ 0      │
│ 5       │ 'moveCursor'         │ 0      │
└─────────┴──────────────────────┴────────┘
┌─────────┬───────────────┬────────┐
│ (index) │ node:repl     │ Values │
├─────────┼───────────────┼────────┤
│ 0       │ 'start'       │ 0      │
│ 1       │ 'writer'      │ 0      │
│ 2       │ 'REPLServer'  │ 0      │
│ 3       │ 'Recoverable' │ 0      │
└─────────┴───────────────┴────────┘
┌─────────┬──────────────────┬────────┐
│ (index) │ node:sea         │ Values │
├─────────┼──────────────────┼────────┤
│ 0       │ 'isSea'          │ 0      │
│ 1       │ 'getAsset'       │ 0      │
│ 2       │ 'getRawAsset'    │ 0      │
│ 3       │ 'getAssetAsBlob' │ 0      │
└─────────┴──────────────────┴────────┘
┌─────────┬─────────────────┬────────┐
│ (index) │ node:sqlite     │ Values │
├─────────┼─────────────────┼────────┤
│ 0       │ 'DatabaseSync'  │ 0      │
│ 1       │ 'StatementSync' │ 0      │
└─────────┴─────────────────┴────────┘
┌─────────┬───────────────────────────┬────────┐
│ (index) │ node:stream               │ Values │
├─────────┼───────────────────────────┼────────┤
│ 0       │ 'isDestroyed'             │ 0      │
│ 1       │ 'isDisturbed'             │ 0      │
│ 2       │ 'isErrored'               │ 0      │
│ 3       │ 'isReadable'              │ 0      │
│ 4       │ 'isWritable'              │ 0      │
│ 5       │ 'duplexPair'              │ 0      │
│ 6       │ 'pipeline'                │ 0      │
│ 7       │ 'addAbortSignal'          │ 0      │
│ 8       │ 'finished'                │ 0      │
│ 9       │ 'destroy'                 │ 0      │
│ 10      │ 'compose'                 │ 0      │
│ 11      │ 'setDefaultHighWaterMark' │ 0      │
│ 12      │ 'getDefaultHighWaterMark' │ 0      │
│ 13      │ 'Stream'                  │ 0      │
└─────────┴───────────────────────────┴────────┘
┌─────────┬──────────────┬────────┐
│ (index) │ node:test    │ Values │
├─────────┼──────────────┼────────┤
│ 0       │ 'skip'       │ 0      │
│ 1       │ 'todo'       │ 0      │
│ 2       │ 'only'       │ 0      │
│ 3       │ 'after'      │ 0      │
│ 4       │ 'afterEach'  │ 0      │
│ 5       │ 'before'     │ 0      │
│ 6       │ 'beforeEach' │ 0      │
│ 7       │ 'run'        │ 0      │
│ 8       │ 'suite'      │ 0      │
│ 9       │ 'test'       │ 0      │
└─────────┴──────────────┴────────┘
┌─────────┬──────────────────┬────────┐
│ (index) │ node:timers      │ Values │
├─────────┼──────────────────┼────────┤
│ 0       │ 'setTimeout'     │ 0      │
│ 1       │ 'clearTimeout'   │ 0      │
│ 2       │ 'setImmediate'   │ 0      │
│ 3       │ 'clearImmediate' │ 0      │
│ 4       │ 'setInterval'    │ 0      │
│ 5       │ 'clearInterval'  │ 0      │
│ 6       │ 'active'         │ 0      │
│ 7       │ 'unenroll'       │ 0      │
│ 8       │ 'enroll'         │ 0      │
└─────────┴──────────────────┴────────┘
┌─────────┬───────────────────────┬────────┐
│ (index) │ node:tls              │ Values │
├─────────┼───────────────────────┼────────┤
│ 0       │ 'checkServerIdentity' │ 0      │
│ 1       │ 'SecureContext'       │ 0      │
│ 2       │ 'Server'              │ 0      │
└─────────┴───────────────────────┴────────┘
┌─────────┬───────────────┬────────┐
│ (index) │ node:tty      │ Values │
├─────────┼───────────────┼────────┤
│ 0       │ 'isatty'      │ 0      │
│ 1       │ 'ReadStream'  │ 0      │
│ 2       │ 'WriteStream' │ 0      │
└─────────┴───────────────┴────────┘
┌─────────┬────────────────────┬────────┐
│ (index) │ node:url           │ Values │
├─────────┼────────────────────┼────────┤
│ 0       │ 'Url'              │ 0      │
│ 1       │ 'resolveObject'    │ 0      │
│ 2       │ 'URL'              │ 0      │
│ 3       │ 'URLSearchParams'  │ 0      │
│ 4       │ 'urlToHttpOptions' │ 0      │
└─────────┴────────────────────┴────────┘
┌─────────┬───────────────────────────────┬────────┐
│ (index) │ node:util                     │ Values │
├─────────┼───────────────────────────────┼────────┤
│ 0       │ 'callbackify'                 │ 0      │
│ 1       │ 'debug'                       │ 0      │
│ 2       │ 'debuglog'                    │ 0      │
│ 3       │ 'deprecate'                   │ 0      │
│ 4       │ 'formatWithOptions'           │ 0      │
│ 5       │ 'getSystemErrorMap'           │ 0      │
│ 6       │ 'getSystemErrorName'          │ 0      │
│ 7       │ 'inherits'                    │ 0      │
│ 8       │ 'isArray'                     │ 0      │
│ 9       │ 'isBoolean'                   │ 0      │
│ 10      │ 'isBuffer'                    │ 0      │
│ 11      │ 'isDeepStrictEqual'           │ 0      │
│ 12      │ 'isNull'                      │ 0      │
│ 13      │ 'isNullOrUndefined'           │ 0      │
│ 14      │ 'isNumber'                    │ 0      │
│ 15      │ 'isString'                    │ 0      │
│ 16      │ 'isSymbol'                    │ 0      │
│ 17      │ 'isUndefined'                 │ 0      │
│ 18      │ 'isRegExp'                    │ 0      │
│ 19      │ 'isObject'                    │ 0      │
│ 20      │ 'isDate'                      │ 0      │
│ 21      │ 'isError'                     │ 0      │
│ 22      │ 'isFunction'                  │ 0      │
│ 23      │ 'isPrimitive'                 │ 0      │
│ 24      │ 'log'                         │ 0      │
│ 25      │ 'stripVTControlCharacters'    │ 0      │
│ 26      │ 'toUSVString'                 │ 0      │
│ 27      │ 'transferableAbortSignal'     │ 0      │
│ 28      │ 'transferableAbortController' │ 0      │
│ 29      │ 'aborted'                     │ 0      │
│ 30      │ 'parseEnv'                    │ 0      │
│ 31      │ 'parseArgs'                   │ 0      │
│ 32      │ 'TextDecoder'                 │ 0      │
│ 33      │ 'TextEncoder'                 │ 0      │
│ 34      │ 'MIMEType'                    │ 0      │
│ 35      │ 'MIMEParams'                  │ 0      │
└─────────┴───────────────────────────────┴────────┘
┌─────────┬────────────────────────────────┬────────┐
│ (index) │ node:v8                        │ Values │
├─────────┼────────────────────────────────┼────────┤
│ 0       │ 'cachedDataVersionTag'         │ 0      │
│ 1       │ 'getHeapSnapshot'              │ 0      │
│ 2       │ 'getHeapCodeStatistics'        │ 0      │
│ 3       │ 'setFlagsFromString'           │ 0      │
│ 4       │ 'Serializer'                   │ 0      │
│ 5       │ 'Deserializer'                 │ 0      │
│ 6       │ 'DefaultSerializer'            │ 0      │
│ 7       │ 'DefaultDeserializer'          │ 0      │
│ 8       │ 'deserialize'                  │ 0      │
│ 9       │ 'takeCoverage'                 │ 0      │
│ 10      │ 'stopCoverage'                 │ 0      │
│ 11      │ 'writeHeapSnapshot'            │ 0      │
│ 12      │ 'queryObjects'                 │ 0      │
│ 13      │ 'setHeapSnapshotNearHeapLimit' │ 0      │
│ 14      │ 'GCProfiler'                   │ 0      │
└─────────┴────────────────────────────────┴────────┘
┌─────────┬───────────────────┬────────┐
│ (index) │ node:vm           │ Values │
├─────────┼───────────────────┼────────┤
│ 0       │ 'Script'          │ 0      │
│ 1       │ 'createScript'    │ 0      │
│ 2       │ 'runInNewContext' │ 0      │
│ 3       │ 'isContext'       │ 0      │
│ 4       │ 'compileFunction' │ 0      │
│ 5       │ 'measureMemory'   │ 0      │
└─────────┴───────────────────┴────────┘
┌─────────┬───────────┬────────┐
│ (index) │ node:wasi │ Values │
├─────────┼───────────┼────────┤
│ 0       │ 'WASI'    │ 0      │
└─────────┴───────────┴────────┘
┌─────────┬────────────────────────────┬────────┐
│ (index) │ node:worker_threads        │ Values │
├─────────┼────────────────────────────┼────────┤
│ 0       │ 'MessagePort'              │ 0      │
│ 1       │ 'markAsUntransferable'     │ 0      │
│ 2       │ 'isMarkedAsUntransferable' │ 0      │
│ 3       │ 'moveMessagePortToContext' │ 0      │
│ 4       │ 'receiveMessageOnPort'     │ 0      │
│ 5       │ 'postMessageToThread'      │ 0      │
│ 6       │ 'BroadcastChannel'         │ 0      │
│ 7       │ 'setEnvironmentData'       │ 0      │
│ 8       │ 'getEnvironmentData'       │ 0      │
└─────────┴────────────────────────────┴────────┘
┌─────────┬────────────────────────┬────────┐
│ (index) │ node:zlib              │ Values │
├─────────┼────────────────────────┼────────┤
│ 0       │ 'crc32'                │ 0      │
│ 1       │ 'Deflate'              │ 0      │
│ 2       │ 'Inflate'              │ 0      │
│ 3       │ 'Gzip'                 │ 0      │
│ 4       │ 'Gunzip'               │ 0      │
│ 5       │ 'DeflateRaw'           │ 0      │
│ 6       │ 'InflateRaw'           │ 0      │
│ 7       │ 'Unzip'                │ 0      │
│ 8       │ 'BrotliCompress'       │ 0      │
│ 9       │ 'BrotliDecompress'     │ 0      │
│ 10      │ 'gzip'                 │ 0      │
│ 11      │ 'gzipSync'             │ 0      │
│ 12      │ 'deflateRaw'           │ 0      │
│ 13      │ 'deflateRawSync'       │ 0      │
│ 14      │ 'unzip'                │ 0      │
│ 15      │ 'unzipSync'            │ 0      │
│ 16      │ 'gunzip'               │ 0      │
│ 17      │ 'gunzipSync'           │ 0      │
│ 18      │ 'inflateRaw'           │ 0      │
│ 19      │ 'inflateRawSync'       │ 0      │
│ 20      │ 'brotliCompress'       │ 0      │
│ 21      │ 'brotliCompressSync'   │ 0      │
│ 22      │ 'brotliDecompress'     │ 0      │
│ 23      │ 'brotliDecompressSync' │ 0      │
└─────────┴────────────────────────┴────────┘

Full result: https://gist.github.com/RafaelGSS/66d33091560d61932b26d74af2fa8b82

Current limitations

  1. For each benchmark file I require coverage.js to set up the monkey patch of Module.require (-r ./coverage.js). However, any benchmark setup would impact the report. For instance:
const bench = common.createBenchmark(main, { ... }});
const fs = require('node:fs')

const files = fs.readdir(...) // readdir and collect files

function main({ ... }) {
  bench.start()
  for (const file of files) {
    fs.existsSync(file)
  }
  bench.end(files.length)
}

This will result in:

  • fs.existsSync status "covered"
  • fs.readdir status "covered"

This is unfortunate, as in reality only fs.existsSync is being measured. One theory that came to my mind to fix that is to intercept the bench.start calls and only then return the patched modules. I might try it later.

  1. For simplicity, I'm assuming that all modules export an object containing functions. However, some modules default exports a function, for instance, typeof require('node:assert') === 'function'. This also means that classes such as AsyncLocalStorage (or function classes) aren't covered as the constructor (called with new ..) isn't patched. I think using Proxy might solve it, but I haven't tested yet.

I'm certain there's a better way to approach this goal and fix the limitations. Hence, I'm opening it as a draft.

cc: @nodejs/benchmarking @nodejs/test_runner @lemire

@nodejs-github-bot nodejs-github-bot added benchmark Issues and PRs related to the benchmark subsystem. performance Issues and PRs related to the performance of Node.js. url Issues and PRs related to the legacy built-in url module. whatwg-url Issues and PRs related to the WHATWG URL implementation. labels Aug 12, 2024
@anonrig
Copy link
Member

anonrig commented Aug 12, 2024

Why don't we pass the function to benchmakr as a parameter to createBenchmark() like

const bench = common.createBenchmark(main, {
  n: [2e4],
  benchmark_method: ['deepEqual', 'notDeepEqual', 'unequal_length'],
  benchmark_module: 'assert',
}, {
  combinationFilter: (p) => {
    return p.strict === 1 || p.method === 'deepEqual';
  },
});

and later access it as

function run({ fn })

or something which resolves into node:assert 's function

@RafaelGSS
Copy link
Member Author

RafaelGSS commented Aug 12, 2024

That requires changing all benchmarks and this is subject to developer errors. I tried to do it harmlessly (funny saying that while I'm monkey patching Module.require). But, possibly simpler than my approach.

@anonrig
Copy link
Member

anonrig commented Aug 12, 2024

That requires changing all benchmarks and this is subject to developer errors. I tried to do it harmlessly (funny saying that while I'm monkey patching Module.require). But, possibly simpler than my approach.

We can add an eslint rule (possibly @aduh95) to call that parameter inside bench.run() and bench.end() scope.

@Felipe1731

This comment was marked as spam.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
benchmark Issues and PRs related to the benchmark subsystem. performance Issues and PRs related to the performance of Node.js. url Issues and PRs related to the legacy built-in url module. whatwg-url Issues and PRs related to the WHATWG URL implementation.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants