Skip to content

Commit 9e08c8b

Browse files
committedJun 13, 2023
node-api: provide napi_define_properties fast path
Implement defining properties via V8's `v8::Object::CreateDataProperty()`, which is faster for data-valued, writable, configurable, and enumerable properties. Re: #45905 Signed-off-by: Gabriel Schulhof <Gschulhof@auction.com>
1 parent ac6f594 commit 9e08c8b

File tree

6 files changed

+148
-9
lines changed

6 files changed

+148
-9
lines changed
 

‎Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -1422,6 +1422,7 @@ FORMAT_CPP_FILES ?=
14221422
FORMAT_CPP_FILES += $(LINT_CPP_FILES)
14231423
# C source codes.
14241424
FORMAT_CPP_FILES += $(wildcard \
1425+
benchmark/napi/*/*.c \
14251426
test/js-native-api/*/*.c \
14261427
test/js-native-api/*/*.h \
14271428
test/node-api/*/*.c \
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build/
+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#include <node_api.h>
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
5+
#define NODE_API_CALL(call) \
6+
do { \
7+
napi_status status = call; \
8+
if (status != napi_ok) { \
9+
fprintf(stderr, #call " failed: %d\n", status); \
10+
abort(); \
11+
} \
12+
} while (0)
13+
14+
#define ABORT_IF_FALSE(condition) \
15+
if (!(condition)) { \
16+
fprintf(stderr, #condition " failed\n"); \
17+
abort(); \
18+
}
19+
20+
static napi_value Runner(napi_env env,
21+
napi_callback_info info,
22+
napi_property_attributes attr) {
23+
napi_value argv[2], undefined, js_array_length, start, end;
24+
size_t argc = 2;
25+
napi_valuetype val_type = napi_undefined;
26+
bool is_array = false;
27+
uint32_t array_length = 0;
28+
napi_value* native_array;
29+
30+
// Validate params and retrieve start and end function.
31+
NODE_API_CALL(napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
32+
ABORT_IF_FALSE(argc == 2);
33+
NODE_API_CALL(napi_typeof(env, argv[0], &val_type));
34+
ABORT_IF_FALSE(val_type == napi_object);
35+
NODE_API_CALL(napi_is_array(env, argv[1], &is_array));
36+
ABORT_IF_FALSE(is_array);
37+
NODE_API_CALL(napi_get_array_length(env, argv[1], &array_length));
38+
NODE_API_CALL(napi_get_named_property(env, argv[0], "start", &start));
39+
NODE_API_CALL(napi_typeof(env, start, &val_type));
40+
ABORT_IF_FALSE(val_type == napi_function);
41+
NODE_API_CALL(napi_get_named_property(env, argv[0], "end", &end));
42+
NODE_API_CALL(napi_typeof(env, end, &val_type));
43+
ABORT_IF_FALSE(val_type == napi_function);
44+
45+
NODE_API_CALL(napi_get_undefined(env, &undefined));
46+
NODE_API_CALL(napi_create_uint32(env, array_length, &js_array_length));
47+
48+
// Copy objects into a native array.
49+
native_array = malloc(array_length * sizeof(*native_array));
50+
for (uint32_t idx = 0; idx < array_length; idx++) {
51+
NODE_API_CALL(napi_get_element(env, argv[1], idx, &native_array[idx]));
52+
}
53+
54+
const napi_property_descriptor desc = {
55+
"prop", NULL, NULL, NULL, NULL, js_array_length, attr, NULL};
56+
57+
// Start the benchmark.
58+
napi_call_function(env, argv[0], start, 0, NULL, NULL);
59+
60+
for (uint32_t idx = 0; idx < array_length; idx++) {
61+
NODE_API_CALL(napi_define_properties(env, native_array[idx], 1, &desc));
62+
}
63+
64+
// Conclude the benchmark.
65+
NODE_API_CALL(
66+
napi_call_function(env, argv[0], end, 1, &js_array_length, NULL));
67+
68+
free(native_array);
69+
70+
return undefined;
71+
}
72+
73+
static napi_value RunFastPath(napi_env env, napi_callback_info info) {
74+
return Runner(env, info, napi_writable | napi_enumerable | napi_configurable);
75+
}
76+
77+
static napi_value RunSlowPath(napi_env env, napi_callback_info info) {
78+
return Runner(env, info, napi_writable | napi_enumerable);
79+
}
80+
81+
NAPI_MODULE_INIT() {
82+
napi_property_descriptor props[] = {
83+
{"runFastPath",
84+
NULL,
85+
RunFastPath,
86+
NULL,
87+
NULL,
88+
NULL,
89+
napi_writable | napi_configurable | napi_enumerable,
90+
NULL},
91+
{"runSlowPath",
92+
NULL,
93+
RunSlowPath,
94+
NULL,
95+
NULL,
96+
NULL,
97+
napi_writable | napi_configurable | napi_enumerable,
98+
NULL},
99+
};
100+
101+
NODE_API_CALL(napi_define_properties(
102+
env, exports, sizeof(props) / sizeof(*props), props));
103+
return exports;
104+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
'targets': [
3+
{
4+
'target_name': 'binding',
5+
'sources': [ 'binding.c' ]
6+
}
7+
]
8+
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict';
2+
3+
const common = require('../../common.js');
4+
5+
const binding = require(`./build/${common.buildType}/binding`);
6+
7+
const bench = common.createBenchmark(main, {
8+
n: [5e6],
9+
implem: ['runFastPath', 'runSlowPath'],
10+
});
11+
12+
function main({ n, implem }) {
13+
const objs = Array(n).fill(null).map((item) => new Object());
14+
binding[implem](bench, objs);
15+
}

‎src/js_native_api_v8.cc

+19-9
Original file line numberDiff line numberDiff line change
@@ -1242,16 +1242,26 @@ napi_define_properties(napi_env env,
12421242
}
12431243
} else {
12441244
v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(p->value);
1245+
bool defined_successfully = false;
1246+
1247+
if ((p->attributes & napi_enumerable) &&
1248+
(p->attributes & napi_writable) &&
1249+
(p->attributes & napi_configurable)) {
1250+
auto define_maybe =
1251+
obj->CreateDataProperty(context, property_name, value);
1252+
defined_successfully = define_maybe.FromMaybe(false);
1253+
} else {
1254+
v8::PropertyDescriptor descriptor(value,
1255+
(p->attributes & napi_writable) != 0);
1256+
descriptor.set_enumerable((p->attributes & napi_enumerable) != 0);
1257+
descriptor.set_configurable((p->attributes & napi_configurable) != 0);
1258+
1259+
auto define_maybe =
1260+
obj->DefineProperty(context, property_name, descriptor);
1261+
defined_successfully = define_maybe.FromMaybe(false);
1262+
}
12451263

1246-
v8::PropertyDescriptor descriptor(value,
1247-
(p->attributes & napi_writable) != 0);
1248-
descriptor.set_enumerable((p->attributes & napi_enumerable) != 0);
1249-
descriptor.set_configurable((p->attributes & napi_configurable) != 0);
1250-
1251-
auto define_maybe =
1252-
obj->DefineProperty(context, property_name, descriptor);
1253-
1254-
if (!define_maybe.FromMaybe(false)) {
1264+
if (!defined_successfully) {
12551265
return napi_set_last_error(env, napi_invalid_arg);
12561266
}
12571267
}

0 commit comments

Comments
 (0)