Skip to content

Commit c591e88

Browse files
committed
feat: restructure the native core and setup basic perf-map generation
1 parent 3630e0e commit c591e88

File tree

21 files changed

+365
-141
lines changed

21 files changed

+365
-141
lines changed

.vscode/c_cpp_properties.json

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
{
2-
"configurations": [
3-
{
4-
"name": "Linux",
5-
"includePath": ["${workspaceFolder}/**"],
6-
"defines": [],
7-
"compilerPath": "/usr/bin/clang",
8-
"cStandard": "c17",
9-
"cppStandard": "c++14",
10-
"intelliSenseMode": "linux-clang-x64",
11-
"configurationProvider": "ms-vscode.makefile-tools"
12-
}
13-
],
14-
"version": 4
15-
}
2+
"configurations": [
3+
{
4+
"name": "Linux",
5+
"includePath": [
6+
"${workspaceFolder}/**",
7+
"/usr/include/node"
8+
],
9+
"defines": [],
10+
"compilerPath": "/usr/bin/clang",
11+
"cStandard": "c17",
12+
"cppStandard": "c++14",
13+
"intelliSenseMode": "linux-clang-x64",
14+
"configurationProvider": "ms-vscode.makefile-tools"
15+
}
16+
],
17+
"version": 4
18+
}

packages/benchmark.js-plugin/src/index.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import {
2-
initCore,
3-
measurement,
2+
Measurement,
43
optimizeFunction,
54
optimizeFunctionSync,
5+
setupCore,
6+
teardownCore,
67
} from "@codspeed/core";
78
import Benchmark from "benchmark";
89
import buildSuiteAdd from "./buildSuiteAdd";
@@ -83,7 +84,7 @@ export function withCodSpeed(item: unknown): unknown {
8384
}
8485

8586
function withCodSpeedBenchmark(bench: Benchmark): WithCodSpeedBenchmark {
86-
if (!measurement.isInstrumented()) {
87+
if (!Measurement.isInstrumented()) {
8788
const rawRun = bench.run;
8889
bench.run = (options?: Benchmark.Options) => {
8990
console.warn(
@@ -113,7 +114,7 @@ function withCodSpeedBenchmark(bench: Benchmark): WithCodSpeedBenchmark {
113114
}
114115

115116
function withCodSpeedSuite(suite: Benchmark.Suite): WithCodSpeedSuite {
116-
if (!measurement.isInstrumented()) {
117+
if (!Measurement.isInstrumented()) {
117118
const rawRun = suite.run;
118119
suite.run = (options?: Benchmark.Options) => {
119120
console.warn(
@@ -162,7 +163,7 @@ async function runBenchmarks({
162163
benchmarkCompletedListeners,
163164
}: RunBenchmarksOptions): Promise<void> {
164165
console.log(`[CodSpeed] running with @codspeed/benchmark.js v${__VERSION__}`);
165-
initCore();
166+
setupCore();
166167
for (let i = 0; i < benches.length; i++) {
167168
const bench = benches[i];
168169
const uri = bench.uri ?? `${baseUri}::unknown_${i}`;
@@ -185,20 +186,21 @@ async function runBenchmarks({
185186
if (isAsync) {
186187
await optimizeFunction(benchPayload);
187188
await (async function __codspeed_root_frame__() {
188-
measurement.startInstrumentation();
189+
Measurement.startInstrumentation();
189190
await benchPayload();
190-
measurement.stopInstrumentation(uri);
191+
Measurement.stopInstrumentation(uri);
191192
})();
192193
} else {
193194
optimizeFunctionSync(benchPayload);
194195
(function __codspeed_root_frame__() {
195-
measurement.startInstrumentation();
196+
Measurement.startInstrumentation();
196197
benchPayload();
197-
measurement.stopInstrumentation(uri);
198+
Measurement.stopInstrumentation(uri);
198199
})();
199200
}
200201
console.log(` ✔ Measured ${uri}`);
201202
benchmarkCompletedListeners.forEach((listener) => listener());
203+
teardownCore();
202204
}
203205
console.log(`[CodSpeed] Done running ${benches.length} benches.`);
204206
}

packages/benchmark.js-plugin/tests/index.integ.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { mockDeep, mockReset } from "jest-mock-extended";
2-
const mockCore = mockDeep<Measurement>();
2+
const mockCore = mockDeep<typeof Measurement>();
33

44
import type { Measurement } from "@codspeed/core";
55
import Benchmark from "benchmark";
@@ -9,7 +9,7 @@ import { registerOtherBenchmarks } from "./registerOtherBenchmarks";
99

1010
jest.mock("@codspeed/core", () => ({
1111
...jest.requireActual("@codspeed/core"),
12-
measurement: mockCore,
12+
Measurement: mockCore,
1313
}));
1414

1515
beforeEach(() => {

packages/core/binding.gyp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
{
22
"targets": [
33
{
4-
"target_name": "measurement",
5-
"cflags!": [ "-fno-exceptions" ],
6-
"cflags_cc!": [ "-fno-exceptions" ],
7-
"sources": [ "src/measurement.cc" ],
4+
"target_name": "native_core",
5+
"cflags!": [
6+
"-fno-exceptions"
7+
],
8+
"cflags_cc!": [
9+
"-fno-exceptions"
10+
],
11+
"sources": [
12+
"src/native_core/measurement/measurement.cc",
13+
"src/native_core/linux_perf/linux_perf.cc",
14+
"src/native_core/linux_perf/linux_perf_listener.cc",
15+
"src/native_core/native_core.cc"
16+
],
817
"include_dirs": [
918
"<!@(node -p \"require('node-addon-api').include\")"
1019
],
11-
'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],
12-
}
20+
"defines": [
21+
"NAPI_DISABLE_CPP_EXCEPTIONS"
22+
]
23+
},
1324
]
14-
}
25+
}

packages/core/moon.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ tasks:
99
build-native-addon:
1010
command: prebuildify --napi --strip
1111
inputs:
12-
- "src/measurement.cc"
12+
- "src/native_core/**/*.cc"
13+
- "src/native_core/**/*.h"
1314
- "binding.gyp"
1415
outputs:
1516
- "prebuilds"

packages/core/src/index.ts

Lines changed: 17 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,26 @@
1-
import { writeFileSync } from "fs";
2-
import path from "path";
1+
import { tryIntrospect } from "./introspection";
2+
tryIntrospect();
33

4-
const getV8Flags = (nodeVersionMajor: number) => {
5-
const flags = [
6-
"--hash-seed=1",
7-
"--random-seed=1",
8-
"--no-opt",
9-
"--predictable",
10-
"--predictable-gc-schedule",
11-
"--interpreted-frames-native-stack",
12-
"--perf-basic-prof",
13-
];
14-
if (nodeVersionMajor < 18) {
15-
flags.push("--no-randomize-hashes");
16-
}
17-
if (nodeVersionMajor < 20) {
18-
flags.push("--no-scavenge-task");
19-
}
20-
return flags;
21-
};
22-
23-
if (process.env.__CODSPEED_NODE_CORE_INTROSPECTION_PATH__ !== undefined) {
24-
const nodeVersionMajor = parseInt(process.version.slice(1).split(".")[0]);
25-
26-
const introspectionMetadata = {
27-
flags: getV8Flags(nodeVersionMajor),
28-
};
29-
writeFileSync(
30-
process.env.__CODSPEED_NODE_CORE_INTROSPECTION_PATH__,
31-
JSON.stringify(introspectionMetadata)
32-
);
33-
process.exit(0);
34-
}
4+
import native_core from "./native_core";
5+
import { initOptimization } from "./optimization";
356

367
declare const __VERSION__: string;
378

38-
/* eslint-disable @typescript-eslint/no-empty-function */
39-
export interface Measurement {
40-
isInstrumented(): boolean;
41-
startInstrumentation(): void;
42-
stopInstrumentation(at: string): void;
43-
isBound: boolean;
44-
}
9+
const linuxPerf = new native_core.LinuxPerf();
4510

46-
let m: Measurement;
47-
try {
48-
m = {
49-
// eslint-disable-next-line @typescript-eslint/no-var-requires
50-
...require("node-gyp-build")(path.dirname(__dirname)),
51-
isBound: true,
52-
} as Measurement;
53-
} catch (e) {
54-
m = {
55-
isInstrumented: () => false,
56-
startInstrumentation: () => {},
57-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
58-
stopInstrumentation: (at) => {},
59-
isBound: false,
60-
};
61-
}
62-
const skipOptimization = process.env.CODSPEED_FORCE_OPTIMIZATION !== "true";
63-
export const measurement = m;
64-
export const initCore = () => {
65-
if (!skipOptimization) {
66-
// eslint-disable-next-line @typescript-eslint/no-var-requires
67-
require("v8").setFlagsFromString("--allow-natives-syntax");
68-
}
69-
measurement.stopInstrumentation(`Metadata: codspeed-node ${__VERSION__}`);
70-
};
11+
export const isBound = native_core.isBound;
7112

72-
export const optimizeFunction = async (fn: CallableFunction) => {
73-
if (skipOptimization) {
74-
// warmup V8 symbols generation of the performance map
75-
await fn();
76-
return;
77-
}
78-
// Source: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#optimization-killers
79-
await fn(); //Fill type-info
80-
await fn(); // 2 calls are needed to go from uninitialized -> pre-monomorphic -> monomorphic
81-
eval("%OptimizeFunctionOnNextCall(fn)");
82-
await fn(); // optimize
13+
export const setupCore = () => {
14+
initOptimization();
15+
native_core.Measurement.stopInstrumentation(
16+
`Metadata: codspeed-node ${__VERSION__}`
17+
);
18+
linuxPerf.start();
8319
};
8420

85-
export const optimizeFunctionSync = (fn: CallableFunction) => {
86-
if (skipOptimization) {
87-
// warmup V8 symbols generation of the performance map
88-
fn();
89-
return;
90-
}
91-
// Source: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#optimization-killers
92-
fn(); //Fill type-info
93-
fn(); // 2 calls are needed to go from uninitialized -> pre-monomorphic -> monomorphic
94-
eval("%OptimizeFunctionOnNextCall(fn)");
95-
fn(); // optimize
21+
export const teardownCore = () => {
22+
linuxPerf.stop();
9623
};
24+
25+
export { optimizeFunction, optimizeFunctionSync } from "./optimization";
26+
export const Measurement = native_core.Measurement;

packages/core/src/introspection.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { writeFileSync } from "fs";
2+
3+
const getV8Flags = (nodeVersionMajor: number) => {
4+
const flags = [
5+
"--hash-seed=1",
6+
"--random-seed=1",
7+
"--no-opt",
8+
"--predictable",
9+
"--predictable-gc-schedule",
10+
"--interpreted-frames-native-stack",
11+
];
12+
if (nodeVersionMajor < 18) {
13+
flags.push("--no-randomize-hashes");
14+
}
15+
if (nodeVersionMajor < 20) {
16+
flags.push("--no-scavenge-task");
17+
}
18+
return flags;
19+
};
20+
21+
export const tryIntrospect = () => {
22+
if (process.env.__CODSPEED_NODE_CORE_INTROSPECTION_PATH__ !== undefined) {
23+
const nodeVersionMajor = parseInt(process.version.slice(1).split(".")[0]);
24+
25+
const introspectionMetadata = {
26+
flags: getV8Flags(nodeVersionMajor),
27+
};
28+
writeFileSync(
29+
process.env.__CODSPEED_NODE_CORE_INTROSPECTION_PATH__,
30+
JSON.stringify(introspectionMetadata)
31+
);
32+
process.exit(0);
33+
}
34+
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import path from "path";
2+
import { LinuxPerf } from "./linux_perf/linux_perf";
3+
import { Measurement } from "./measurement/measurement";
4+
interface NativeCore {
5+
Measurement: Measurement;
6+
LinuxPerf: typeof LinuxPerf;
7+
}
8+
9+
interface NativeCoreWithBindingStatus extends NativeCore {
10+
isBound: boolean;
11+
}
12+
13+
let native_core: NativeCoreWithBindingStatus;
14+
try {
15+
// eslint-disable-next-line @typescript-eslint/no-var-requires
16+
const nativeCore = require("node-gyp-build")(
17+
path.dirname(__dirname)
18+
) as NativeCore;
19+
native_core = {
20+
...nativeCore,
21+
isBound: true,
22+
};
23+
} catch (e) {
24+
native_core = {
25+
Measurement: {
26+
isInstrumented: () => false,
27+
// eslint-disable-next-line @typescript-eslint/no-empty-function
28+
startInstrumentation: () => {},
29+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
30+
stopInstrumentation: (at) => {},
31+
},
32+
LinuxPerf: class LinuxPerf {
33+
start() {
34+
return false;
35+
}
36+
stop() {
37+
return false;
38+
}
39+
},
40+
41+
isBound: false,
42+
};
43+
}
44+
45+
export default native_core;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include <napi.h>
2+
3+
#include "linux_perf.h"
4+
#include <node_api.h>
5+
6+
Napi::Object LinuxPerf::Initialize(Napi::Env env, Napi::Object exports) {
7+
Napi::Function func = DefineClass(env, "LinuxPerf",
8+
{InstanceMethod("start", &LinuxPerf::Start),
9+
InstanceMethod("stop", &LinuxPerf::Stop)});
10+
11+
exports.Set("LinuxPerf", func);
12+
return exports;
13+
}
14+
15+
LinuxPerf::LinuxPerf(const Napi::CallbackInfo &info)
16+
: Napi::ObjectWrap<LinuxPerf>(info) {
17+
handler = nullptr;
18+
}
19+
20+
Napi::Value LinuxPerf::Start(const Napi::CallbackInfo &info) {
21+
if (handler == nullptr) {
22+
v8::Isolate *isolate = v8::Isolate::GetCurrent();
23+
handler = new LinuxPerfHandler(isolate);
24+
handler->Enable();
25+
return Napi::Boolean::New(info.Env(), true);
26+
}
27+
return Napi::Boolean::New(info.Env(), false);
28+
}
29+
30+
Napi::Value LinuxPerf::Stop(const Napi::CallbackInfo &info) {
31+
if (handler != nullptr) {
32+
handler->Disable();
33+
delete handler;
34+
handler = nullptr;
35+
return Napi::Boolean::New(info.Env(), true);
36+
}
37+
return Napi::Boolean::New(info.Env(), false);
38+
}

0 commit comments

Comments
 (0)