diff --git a/packages/benchmark.js-plugin/src/index.ts b/packages/benchmark.js-plugin/src/index.ts index 0ffe5a7..97947dd 100644 --- a/packages/benchmark.js-plugin/src/index.ts +++ b/packages/benchmark.js-plugin/src/index.ts @@ -196,6 +196,7 @@ async function runBenchmarks({ if (isAsync) { await optimizeFunction(benchPayload); await mongoMeasurement.start(uri); + global.gc?.(); await (async function __codspeed_root_frame__() { Measurement.startInstrumentation(); await benchPayload(); diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index e536008..d477064 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,7 +1,6 @@ import { checkV8Flags } from "./introspection"; import { MongoMeasurement } from "./mongoMeasurement"; import native_core from "./native_core"; -import { initOptimization } from "./optimization"; declare const __VERSION__: string; @@ -12,7 +11,6 @@ export const isBound = native_core.isBound; export const mongoMeasurement = new MongoMeasurement(); export const setupCore = () => { - initOptimization(); native_core.Measurement.stopInstrumentation( `Metadata: codspeed-node ${__VERSION__}` ); diff --git a/packages/core/src/introspection.ts b/packages/core/src/introspection.ts index 89842d7..1be58ab 100644 --- a/packages/core/src/introspection.ts +++ b/packages/core/src/introspection.ts @@ -12,6 +12,10 @@ export const getV8Flags = () => { "--predictable", "--predictable-gc-schedule", "--interpreted-frames-native-stack", + "--allow-natives-syntax", + "--expose-gc", + "--no-concurrent-sweeping", + "--max-old-space-size=4096", ]; if (nodeVersionMajor < 18) { flags.push("--no-randomize-hashes"); diff --git a/packages/core/src/optimization.ts b/packages/core/src/optimization.ts index f646ddb..f39d36f 100644 --- a/packages/core/src/optimization.ts +++ b/packages/core/src/optimization.ts @@ -1,34 +1,25 @@ -const skipOptimization = process.env.CODSPEED_FORCE_OPTIMIZATION !== "true"; - -export const initOptimization = () => { - if (!skipOptimization) { - // eslint-disable-next-line @typescript-eslint/no-var-requires - require("v8").setFlagsFromString("--allow-natives-syntax"); - } -}; - export const optimizeFunction = async (fn: CallableFunction) => { - if (skipOptimization) { - // warmup V8 symbols generation of the performance map - await fn(); - return; - } // Source: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#optimization-killers - await fn(); //Fill type-info - await fn(); // 2 calls are needed to go from uninitialized -> pre-monomorphic -> monomorphic + // a total of 7 calls seems to be the sweet spot + await fn(); + await fn(); + await fn(); + await fn(); + await fn(); + await fn(); eval("%OptimizeFunctionOnNextCall(fn)"); await fn(); // optimize }; export const optimizeFunctionSync = (fn: CallableFunction) => { - if (skipOptimization) { - // warmup V8 symbols generation of the performance map - fn(); - return; - } // Source: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#optimization-killers - fn(); //Fill type-info - fn(); // 2 calls are needed to go from uninitialized -> pre-monomorphic -> monomorphic + // a total of 7 calls seems to be the sweet spot + fn(); + fn(); + fn(); + fn(); + fn(); + fn(); eval("%OptimizeFunctionOnNextCall(fn)"); fn(); // optimize }; diff --git a/packages/tinybench-plugin/src/index.ts b/packages/tinybench-plugin/src/index.ts index e573bd4..dba8212 100644 --- a/packages/tinybench-plugin/src/index.ts +++ b/packages/tinybench-plugin/src/index.ts @@ -70,6 +70,7 @@ export function withCodSpeed(bench: Bench): Bench { await task.opts.beforeEach?.call(task); await mongoMeasurement.start(uri); + global.gc?.(); await (async function __codspeed_root_frame__() { Measurement.startInstrumentation(); await task.fn(); diff --git a/packages/vitest-plugin/src/__tests__/index.test.ts b/packages/vitest-plugin/src/__tests__/index.test.ts index a1147b3..6c513d6 100644 --- a/packages/vitest-plugin/src/__tests__/index.test.ts +++ b/packages/vitest-plugin/src/__tests__/index.test.ts @@ -89,6 +89,10 @@ describe("codSpeedPlugin", () => { "--predictable", "--predictable-gc-schedule", "--interpreted-frames-native-stack", + "--allow-natives-syntax", + "--expose-gc", + "--no-concurrent-sweeping", + "--max-old-space-size=4096", "--no-scavenge-task", ], }, diff --git a/packages/vitest-plugin/src/__tests__/runner.test.ts b/packages/vitest-plugin/src/__tests__/runner.test.ts index 2130e7a..32c0471 100644 --- a/packages/vitest-plugin/src/__tests__/runner.test.ts +++ b/packages/vitest-plugin/src/__tests__/runner.test.ts @@ -18,6 +18,8 @@ const coreMocks = vi.hoisted(() => { }; }); +global.eval = vi.fn(); + vi.mock("@codspeed/core", async (importOriginal) => { const mod = await importOriginal(); return { ...mod, ...coreMocks }; @@ -25,16 +27,13 @@ vi.mock("@codspeed/core", async (importOriginal) => { console.log = vi.fn(); -vi.mock("vitest/suite", () => ({ - getBenchFn: vi.fn(), - // wrapping the value in vi.fn(...) here will not work for some reason - getHooks: () => ({ - beforeAll: [], - beforeEach: [], - afterEach: [], - afterAll: [], - }), -})); +vi.mock("vitest/suite", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + getBenchFn: vi.fn(), + }; +}); const mockedGetBenchFn = vi.mocked(getBenchFn); describe("CodSpeedRunner", () => { @@ -62,7 +61,7 @@ describe("CodSpeedRunner", () => { "packages/vitest-plugin/src/__tests__/runner.test.ts::test bench" ); expect(coreMocks.Measurement.startInstrumentation).toHaveBeenCalledTimes(1); - expect(benchFn).toHaveBeenCalledTimes(2); + expect(benchFn).toHaveBeenCalledTimes(8); expect(coreMocks.Measurement.stopInstrumentation).toHaveBeenCalledTimes(1); expect(coreMocks.mongoMeasurement.stop).toHaveBeenCalledTimes(1); expect(console.log).toHaveBeenCalledWith( @@ -116,7 +115,7 @@ describe("CodSpeedRunner", () => { "packages/vitest-plugin/src/__tests__/runner.test.ts::nested suite::test bench" ); expect(coreMocks.Measurement.startInstrumentation).toHaveBeenCalledTimes(1); - expect(benchFn).toHaveBeenCalledTimes(2); + expect(benchFn).toHaveBeenCalledTimes(8); expect(coreMocks.Measurement.stopInstrumentation).toHaveBeenCalledTimes(1); expect(coreMocks.mongoMeasurement.stop).toHaveBeenCalledTimes(1); expect(console.log).toHaveBeenCalledWith( diff --git a/packages/vitest-plugin/src/runner.ts b/packages/vitest-plugin/src/runner.ts index 2aeba7b..780b348 100644 --- a/packages/vitest-plugin/src/runner.ts +++ b/packages/vitest-plugin/src/runner.ts @@ -15,7 +15,7 @@ import { getBenchFn, getHooks } from "vitest/suite"; type SuiteHooks = ReturnType; function getSuiteHooks(suite: Suite, name: keyof SuiteHooks) { - return getHooks(suite)[name]; + return getHooks(suite)?.[name] ?? []; } export async function callSuiteHook( @@ -23,7 +23,7 @@ export async function callSuiteHook( currentTask: Task, name: T ): Promise { - if (name === "beforeEach" && suite.suite) { + if (name === "beforeEach" && suite?.suite) { await callSuiteHook(suite.suite, currentTask, name); } @@ -31,7 +31,7 @@ export async function callSuiteHook( await Promise.all(hooks.map((fn) => fn())); - if (name === "afterEach" && suite.suite) { + if (name === "afterEach" && suite?.suite) { await callSuiteHook(suite.suite, currentTask, name); } } @@ -69,6 +69,7 @@ async function runBench(benchmark: Benchmark, currentSuiteName: string) { await callSuiteHook(benchmark.suite, benchmark, "beforeEach"); await mongoMeasurement.start(uri); + global.gc?.(); await (async function __codspeed_root_frame__() { Measurement.startInstrumentation(); // @ts-expect-error we do not need to bind the function to an instance of tinybench's Bench