Skip to content

Commit 9ed6ee5

Browse files
bnoordhuisMylesBorins
authored andcommitted
src,tools: speed up startup by 2.5%
Use zero-copy external string resources for storing the built-in JS source code. Saves a few hundred kilobyte of memory and consistently speeds up `benchmark/misc/startup.js` by 2.5%. Everything old is new again! Commit 74954ce ("Add string class that uses ExternalAsciiStringResource.") from 2011 did the same thing but I removed that in 2013 in commit 34b0a36 ("src: don't use NewExternal() with unaligned strings") because of a limitation in the V8 API. V8 no longer requires that strings are aligned if they are one-byte strings so it should be safe to re-enable external strings again. PR-URL: #5458 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Robert Jefe Lindstaedt <robert.lindstaedt@gmail.com>
1 parent 1beb169 commit 9ed6ee5

2 files changed

Lines changed: 62 additions & 151 deletions

File tree

‎src/node_javascript.cc‎

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,46 @@
66

77
namespacenode {
88

9-
using v8::HandleScope;
109
using v8::Local;
1110
using v8::NewStringType;
1211
using v8::Object;
1312
using v8::String;
1413

14+
// id##_data is defined in node_natives.h.
15+
#defineV(id) \
16+
staticstruct : publicString::ExternalOneByteStringResource { \
17+
constchar* data() constoverride { \
18+
returnreinterpret_cast<constchar*>(id##_data); \
19+
} \
20+
size_tlength() constoverride { returnsizeof(id##_data); } \
21+
voidDispose() override { /* Default calls `delete this`. */ } \
22+
} id##_external_data;
23+
NODE_NATIVES_MAP(V)
24+
#undef V
25+
1526
Local<String> MainSource(Environment* env) {
16-
returnString::NewFromUtf8(
17-
env->isolate(),
18-
reinterpret_cast<constchar*>(internal_bootstrap_node_native),
19-
NewStringType::kNormal,
20-
sizeof(internal_bootstrap_node_native)).ToLocalChecked();
27+
auto maybe_string =
28+
String::NewExternalOneByte(
29+
env->isolate(),
30+
&internal_bootstrap_node_external_data);
31+
return maybe_string.ToLocalChecked();
2132
}
2233

2334
voidDefineJavaScript(Environment* env, Local<Object> target) {
24-
HandleScope scope(env->isolate());
25-
26-
for (auto native : natives) {
27-
if (native.source != internal_bootstrap_node_native) {
28-
Local<String> name = String::NewFromUtf8(env->isolate(), native.name);
29-
Local<String> source =
30-
String::NewFromUtf8(
31-
env->isolate(), reinterpret_cast<constchar*>(native.source),
32-
NewStringType::kNormal, native.source_len).ToLocalChecked();
33-
target->Set(name, source);
34-
}
35-
}
35+
auto context = env->context();
36+
#defineV(id) \
37+
do { \
38+
auto key = \
39+
String::NewFromOneByte( \
40+
env->isolate(), id##_name, NewStringType::kNormal, \
41+
sizeof(id##_name)).ToLocalChecked(); \
42+
auto value = \
43+
String::NewExternalOneByte( \
44+
env->isolate(), &id##_external_data).ToLocalChecked(); \
45+
CHECK(target->Set(context, key, value).FromJust()); \
46+
} while (0);
47+
NODE_NATIVES_MAP(V)
48+
#undef V
3649
}
3750

3851
} // namespace node

‎tools/js2c.py‎

Lines changed: 31 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@
3737
importstring
3838

3939

40-
defToCArray(filename, lines):
41-
return','.join(str(ord(c)) forcinlines)
40+
defToCString(contents):
41+
step=20
42+
slices= (contents[i:i+step] foriinxrange(0, len(contents), step))
43+
slices=map(lambdas: ','.join(str(ord(c)) forcins), slices)
44+
return',\n'.join(slices)
4245

4346

4447
defReadFile(filename):
@@ -61,21 +64,6 @@ def ReadLines(filename):
6164
returnresult
6265

6366

64-
defLoadConfigFrom(name):
65-
importConfigParser
66-
config=ConfigParser.ConfigParser()
67-
config.read(name)
68-
returnconfig
69-
70-
71-
defParseValue(string):
72-
string=string.strip()
73-
ifstring.startswith('[') andstring.endswith(']'):
74-
returnstring.lstrip('[').rstrip(']').split()
75-
else:
76-
returnstring
77-
78-
7967
defExpandConstants(lines, constants):
8068
forkey, valueinconstants.items():
8169
lines=lines.replace(key, str(value))
@@ -174,53 +162,37 @@ def ReadMacros(lines):
174162

175163

176164
HEADER_TEMPLATE="""\
177-
#ifndef node_natives_h
178-
#define node_natives_h
179-
namespace node {
180-
181-
%(source_lines)s\
165+
#ifndef NODE_NATIVES_H_
166+
#define NODE_NATIVES_H_
182167
183-
struct _native {
184-
const char* name;
185-
const unsigned char* source;
186-
size_t source_len;
187-
};
168+
#include <stdint.h>
188169
189-
static const struct _native natives[] = { %(native_lines)s };
170+
#define NODE_NATIVES_MAP(V) \\
171+
{node_natives_map}
190172
191-
}
192-
#endif
193-
"""
194-
195-
196-
NATIVE_DECLARATION="""\
197-
{ "%(id)s", %(escaped_id)s_native, sizeof(%(escaped_id)s_native) },
198-
"""
173+
namespace node {{
174+
{sources}
175+
}} // namespace node
199176
200-
SOURCE_DECLARATION="""\
201-
const unsigned char %(escaped_id)s_native[] = { %(data)s };
177+
#endif // NODE_NATIVES_H_
202178
"""
203179

204180

205-
GET_DELAY_INDEX_CASE="""\
206-
if (strcmp(name, "%(id)s") == 0) return %(i)i;
181+
NODE_NATIVES_MAP="""\
182+
V({escaped_id}) \\
207183
"""
208184

209185

210-
GET_DELAY_SCRIPT_SOURCE_CASE="""\
211-
if (index == %(i)i) return Vector<const char>(%(id)s, %(length)i);
186+
SOURCES="""\
187+
static const uint8_t {escaped_id}_name[] = {{
188+
{name}}};
189+
static const uint8_t {escaped_id}_data[] = {{
190+
{data}}};
212191
"""
213192

214193

215-
GET_DELAY_SCRIPT_NAME_CASE="""\
216-
if (index == %(i)i) return Vector<const char>("%(name)s", %(length)i);
217-
"""
218-
219194
defJS2C(source, target):
220-
ids= []
221-
delay_ids= []
222195
modules= []
223-
# Locate the macros file name.
224196
consts= {}
225197
macros= {}
226198
macro_lines= []
@@ -235,18 +207,14 @@ def JS2C(source, target):
235207
(consts, macros) =ReadMacros(macro_lines)
236208

237209
# Build source code lines
238-
source_lines= [ ]
239-
source_lines_empty= []
240-
241-
native_lines= []
210+
node_natives_map= []
211+
sources= []
242212

243213
forsinmodules:
244-
delay=str(s).endswith('-delay.js')
245214
lines=ReadFile(str(s))
246-
247215
lines=ExpandConstants(lines, consts)
248216
lines=ExpandMacros(lines, macros)
249-
data=ToCArray(s, lines)
217+
data=ToCString(lines)
250218

251219
# On Windows, "./foo.bar" in the .gyp file is passed as "foo.bar"
252220
# so don't assume there is always a slash in the file path.
@@ -258,89 +226,19 @@ def JS2C(source, target):
258226
if'.'inid:
259227
id=id.split('.', 1)[0]
260228

261-
ifdelay: id=id[:-6]
262-
ifdelay:
263-
delay_ids.append((id, len(lines)))
264-
else:
265-
ids.append((id, len(lines)))
266-
229+
name=ToCString(id)
267230
escaped_id=id.replace('-', '_').replace('/', '_')
268-
source_lines.append(SOURCE_DECLARATION% {
269-
'id': id,
270-
'escaped_id': escaped_id,
271-
'data': data
272-
})
273-
source_lines_empty.append(SOURCE_DECLARATION% {
274-
'id': id,
275-
'escaped_id': escaped_id,
276-
'data': 0
277-
})
278-
native_lines.append(NATIVE_DECLARATION% {
279-
'id': id,
280-
'escaped_id': escaped_id
281-
})
282-
283-
# Build delay support functions
284-
get_index_cases= [ ]
285-
get_script_source_cases= [ ]
286-
get_script_name_cases= [ ]
287-
288-
i=0
289-
for (id, length) indelay_ids:
290-
native_name="native %s.js"%id
291-
get_index_cases.append(GET_DELAY_INDEX_CASE% { 'id': id, 'i': i })
292-
get_script_source_cases.append(GET_DELAY_SCRIPT_SOURCE_CASE% {
293-
'id': id,
294-
'length': length,
295-
'i': i
296-
})
297-
get_script_name_cases.append(GET_DELAY_SCRIPT_NAME_CASE% {
298-
'name': native_name,
299-
'length': len(native_name),
300-
'i': i
301-
});
302-
i=i+1
303-
304-
for (id, length) inids:
305-
native_name="native %s.js"%id
306-
get_index_cases.append(GET_DELAY_INDEX_CASE% { 'id': id, 'i': i })
307-
get_script_source_cases.append(GET_DELAY_SCRIPT_SOURCE_CASE% {
308-
'id': id,
309-
'length': length,
310-
'i': i
311-
})
312-
get_script_name_cases.append(GET_DELAY_SCRIPT_NAME_CASE% {
313-
'name': native_name,
314-
'length': len(native_name),
315-
'i': i
316-
});
317-
i=i+1
231+
node_natives_map.append(NODE_NATIVES_MAP.format(**locals()))
232+
sources.append(SOURCES.format(**locals()))
233+
234+
node_natives_map=''.join(node_natives_map)
235+
sources=''.join(sources)
318236

319237
# Emit result
320238
output=open(str(target[0]), "w")
321-
output.write(HEADER_TEMPLATE% {
322-
'builtin_count': len(ids) +len(delay_ids),
323-
'delay_count': len(delay_ids),
324-
'source_lines': "\n".join(source_lines),
325-
'native_lines': "\n".join(native_lines),
326-
'get_index_cases': "".join(get_index_cases),
327-
'get_script_source_cases': "".join(get_script_source_cases),
328-
'get_script_name_cases': "".join(get_script_name_cases)
329-
})
239+
output.write(HEADER_TEMPLATE.format(**locals()))
330240
output.close()
331241

332-
iflen(target) >1:
333-
output=open(str(target[1]), "w")
334-
output.write(HEADER_TEMPLATE% {
335-
'builtin_count': len(ids) +len(delay_ids),
336-
'delay_count': len(delay_ids),
337-
'source_lines': "\n".join(source_lines_empty),
338-
'get_index_cases': "".join(get_index_cases),
339-
'get_script_source_cases': "".join(get_script_source_cases),
340-
'get_script_name_cases': "".join(get_script_name_cases)
341-
})
342-
output.close()
343-
344242
defmain():
345243
natives=sys.argv[1]
346244
source_files=sys.argv[2:]

0 commit comments

Comments
 (0)