Skip to content

Commit 6918256

Browse files
nigrosimoneaduh95
authored andcommitted
benchmark: add a node::MakeCallback benchmark
The addon calls into JS with node::MakeCallback from a libuv timer, so every call opens a top-level callback scope, like an I/O callback does. Signed-off-by: Nigro Simone <nigro.simone@gmail.com> PR-URL: #66316 Refs: nodejs/performance#24 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 9696e47 commit 6918256

4 files changed

Lines changed: 90 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build/
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#include <node.h>
2+
#include <uv.h>
3+
#include <v8.h>
4+
5+
using v8::Context;
6+
using v8::Function;
7+
using v8::FunctionCallbackInfo;
8+
using v8::Global;
9+
using v8::HandleScope;
10+
using v8::Isolate;
11+
using v8::Local;
12+
using v8::Object;
13+
using v8::Value;
14+
15+
struct State {
16+
uv_timer_t timer;
17+
Isolate* isolate;
18+
int64_t n;
19+
Global<Function> fn;
20+
Global<Function> done;
21+
};
22+
23+
static void OnTimer(uv_timer_t* handle) {
24+
State* state = static_cast<State*>(handle->data);
25+
Isolate* isolate = state->isolate;
26+
HandleScope handle_scope(isolate);
27+
Local<Function> fn = state->fn.Get(isolate);
28+
Local<Context> context = fn->GetCreationContextChecked(isolate);
29+
Context::Scope context_scope(context);
30+
Local<Object> recv = context->Global();
31+
for (int64_t i = 0; i < state->n; i++) {
32+
HandleScope inner_scope(isolate);
33+
(void)node::MakeCallback(isolate, recv, fn, 0, nullptr, {0, 0});
34+
}
35+
Local<Function> done = state->done.Get(isolate);
36+
(void)node::MakeCallback(isolate, recv, done, 0, nullptr, {0, 0});
37+
uv_close(reinterpret_cast<uv_handle_t*>(&state->timer),
38+
[](uv_handle_t* h) { delete static_cast<State*>(h->data); });
39+
}
40+
41+
// run(n, fn, done): calls fn n times from a timer, then calls done.
42+
static void Run(const FunctionCallbackInfo<Value>& args) {
43+
Isolate* isolate = args.GetIsolate();
44+
State* state = new State;
45+
state->isolate = isolate;
46+
state->n = args[0]->IntegerValue(isolate->GetCurrentContext()).FromJust();
47+
state->fn.Reset(isolate, args[1].As<Function>());
48+
state->done.Reset(isolate, args[2].As<Function>());
49+
state->timer.data = state;
50+
uv_timer_init(node::GetCurrentEventLoop(isolate), &state->timer);
51+
uv_timer_start(&state->timer, OnTimer, 0, 0);
52+
}
53+
54+
static void Initialize(Local<Object> target, Local<Value> module, void* data) {
55+
NODE_SET_METHOD(target, "run", Run);
56+
}
57+
58+
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
'targets': [
3+
{
4+
'target_name': 'binding',
5+
'sources': [ 'binding.cc' ]
6+
}
7+
]
8+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict';
2+
3+
const common = require('../../common.js');
4+
5+
// The addon calls into JS with node::MakeCallback from a libuv timer, so
6+
// every call opens a top-level callback scope, like an I/O callback does.
7+
8+
let binding;
9+
try {
10+
binding = require(`./build/${common.buildType}/binding`);
11+
} catch {
12+
console.error('napi/make_callback/index.js Binding failed to load');
13+
process.exit(0);
14+
}
15+
16+
const bench = common.createBenchmark(main, {
17+
n: [1e6, 1e7],
18+
});
19+
20+
function main({ n }) {
21+
bench.start();
22+
binding.run(n, () => {}, () => bench.end(n));
23+
}

0 commit comments

Comments
 (0)