| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 8765afd commit 8b0bdd9
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,48 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + // Measures symbol resolution rather than call throughput. Creating a callable | ||
| 4 | + // for a fast-eligible signature emits a native trampoline, so this benchmark | ||
| 5 | + // covers the trampoline allocation path that the call benchmarks never reach. | ||
| 6 | + // | ||
| 7 | + // The `fast` variant is eligible for a generated trampoline; `slow` exceeds the | ||
| 8 | + // x86_64 register budget and falls back, so it resolves without allocating one. | ||
| 9 | + // Comparing the two isolates trampoline creation cost from the rest of symbol | ||
| 10 | + // resolution. | ||
| 11 | + | ||
| 12 | + const common = require('../common.js'); | ||
| 13 | + const { DynamicLibrary } = require('node:ffi'); | ||
| 14 | + const { libraryPath, ensureFixtureLibrary } = require('./common.js'); | ||
| 15 | + | ||
| 16 | + const bench = common.createBenchmark(main, { | ||
| 17 | + signature: ['fast', 'slow'], | ||
| 18 | + n: [1e3], | ||
| 19 | + }, { | ||
| 20 | + flags: ['--experimental-ffi'], | ||
| 21 | + }); | ||
| 22 | + | ||
| 23 | + ensureFixtureLibrary(); | ||
| 24 | + | ||
| 25 | + const signatures = { | ||
| 26 | + fast: { name: 'add_i32', return: 'i32', arguments: ['i32', 'i32'] }, | ||
| 27 | + slow: { | ||
| 28 | + name: 'sum_8_i32', | ||
| 29 | + return: 'i32', | ||
| 30 | + arguments: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32', 'i32', 'i32'], | ||
| 31 | + }, | ||
| 32 | + }; | ||
| 33 | + | ||
| 34 | + function main({ n, signature }) { | ||
| 35 | + const { name, ...definition } = signatures[signature]; | ||
| 36 | + const lib = new DynamicLibrary(libraryPath); | ||
| 37 | + | ||
| 38 | + // Warm up one-time initialization (libffi setup, executable memory probe) so | ||
| 39 | + // it is not attributed to the measured resolutions. | ||
| 40 | + lib.getFunction(name, definition); | ||
| 41 | + | ||
| 42 | + bench.start(); | ||
| 43 | + for (let i = 0; i < n; ++i) | ||
| 44 | + lib.getFunction(name, definition); | ||
| 45 | + bench.end(n); | ||
| 46 | + | ||
| 47 | + lib.close(); | ||
| 48 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -294,7 +294,16 @@ void* AllocateCodeNear(uintptr_t target_address, size_t code_size) { | |||
| 294 | 294 | const uintptr_t base = target_address & ~(page_size - 1); | |
| 295 | 295 | // Search a small window around the target first. Shared libraries usually | |
| 296 | 296 | // leave nearby holes, and keeping the trampoline close enables jmp rel32. | |
| 297 | - constexpr uintptr_t kMaxPages = 1024; | ||
| 297 | + // | ||
| 298 | + // The window doubles as the capacity of the near-text region: every | ||
| 299 | + // trampoline keeps one page in it, so about 2 * kMaxPages trampolines per | ||
| 300 | + // library can use jmp rel32 before the window is full and later ones take | ||
| 301 | + // the far placement below. Each candidate costs an mmap syscall that is | ||
| 302 | + // expected to fail, and a layout with no hole within a few pages of the | ||
| 303 | + // text rarely has one further out either, so a wide window mostly buys | ||
| 304 | + // failed probes. Keep it small enough that an exhausted window costs a few | ||
| 305 | + // microseconds while still covering typical per-library symbol counts. | ||
| 306 | + constexpr uintptr_t kMaxPages = 16; | ||
| 298 | 307 | for (uintptr_t i = 1; i <= kMaxPages; i++) { | |
| 299 | 308 | const uintptr_t delta = i * page_size; | |
| 300 | 309 | const uintptr_t candidates[] = { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments