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: https://github.com/nodejs/node/pull/5458 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Robert Jefe Lindstaedt <robert.lindstaedt@gmail.com>
This commit is contained in:
parent
2a456162b6
commit
e4290dec2d
@ -6,33 +6,46 @@
|
||||
|
||||
namespace node {
|
||||
|
||||
using v8::HandleScope;
|
||||
using v8::Local;
|
||||
using v8::NewStringType;
|
||||
using v8::Object;
|
||||
using v8::String;
|
||||
|
||||
// id##_data is defined in node_natives.h.
|
||||
#define V(id) \
|
||||
static struct : public String::ExternalOneByteStringResource { \
|
||||
const char* data() const override { \
|
||||
return reinterpret_cast<const char*>(id##_data); \
|
||||
} \
|
||||
size_t length() const override { return sizeof(id##_data); } \
|
||||
void Dispose() override { /* Default calls `delete this`. */ } \
|
||||
} id##_external_data;
|
||||
NODE_NATIVES_MAP(V)
|
||||
#undef V
|
||||
|
||||
Local<String> MainSource(Environment* env) {
|
||||
return String::NewFromUtf8(
|
||||
env->isolate(),
|
||||
reinterpret_cast<const char*>(internal_bootstrap_node_native),
|
||||
NewStringType::kNormal,
|
||||
sizeof(internal_bootstrap_node_native)).ToLocalChecked();
|
||||
auto maybe_string =
|
||||
String::NewExternalOneByte(
|
||||
env->isolate(),
|
||||
&internal_bootstrap_node_external_data);
|
||||
return maybe_string.ToLocalChecked();
|
||||
}
|
||||
|
||||
void DefineJavaScript(Environment* env, Local<Object> target) {
|
||||
HandleScope scope(env->isolate());
|
||||
|
||||
for (auto native : natives) {
|
||||
if (native.source != internal_bootstrap_node_native) {
|
||||
Local<String> name = String::NewFromUtf8(env->isolate(), native.name);
|
||||
Local<String> source =
|
||||
String::NewFromUtf8(
|
||||
env->isolate(), reinterpret_cast<const char*>(native.source),
|
||||
NewStringType::kNormal, native.source_len).ToLocalChecked();
|
||||
target->Set(name, source);
|
||||
}
|
||||
}
|
||||
auto context = env->context();
|
||||
#define V(id) \
|
||||
do { \
|
||||
auto key = \
|
||||
String::NewFromOneByte( \
|
||||
env->isolate(), id##_name, NewStringType::kNormal, \
|
||||
sizeof(id##_name)).ToLocalChecked(); \
|
||||
auto value = \
|
||||
String::NewExternalOneByte( \
|
||||
env->isolate(), &id##_external_data).ToLocalChecked(); \
|
||||
CHECK(target->Set(context, key, value).FromJust()); \
|
||||
} while (0);
|
||||
NODE_NATIVES_MAP(V)
|
||||
#undef V
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
162
tools/js2c.py
162
tools/js2c.py
@ -37,8 +37,11 @@ import sys
|
||||
import string
|
||||
|
||||
|
||||
def ToCArray(filename, lines):
|
||||
return ','.join(str(ord(c)) for c in lines)
|
||||
def ToCString(contents):
|
||||
step = 20
|
||||
slices = (contents[i:i+step] for i in xrange(0, len(contents), step))
|
||||
slices = map(lambda s: ','.join(str(ord(c)) for c in s), slices)
|
||||
return ',\n'.join(slices)
|
||||
|
||||
|
||||
def ReadFile(filename):
|
||||
@ -61,21 +64,6 @@ def ReadLines(filename):
|
||||
return result
|
||||
|
||||
|
||||
def LoadConfigFrom(name):
|
||||
import ConfigParser
|
||||
config = ConfigParser.ConfigParser()
|
||||
config.read(name)
|
||||
return config
|
||||
|
||||
|
||||
def ParseValue(string):
|
||||
string = string.strip()
|
||||
if string.startswith('[') and string.endswith(']'):
|
||||
return string.lstrip('[').rstrip(']').split()
|
||||
else:
|
||||
return string
|
||||
|
||||
|
||||
def ExpandConstants(lines, constants):
|
||||
for key, value in constants.items():
|
||||
lines = lines.replace(key, str(value))
|
||||
@ -174,53 +162,37 @@ def ReadMacros(lines):
|
||||
|
||||
|
||||
HEADER_TEMPLATE = """\
|
||||
#ifndef node_natives_h
|
||||
#define node_natives_h
|
||||
namespace node {
|
||||
#ifndef NODE_NATIVES_H_
|
||||
#define NODE_NATIVES_H_
|
||||
|
||||
%(source_lines)s\
|
||||
#include <stdint.h>
|
||||
|
||||
struct _native {
|
||||
const char* name;
|
||||
const unsigned char* source;
|
||||
size_t source_len;
|
||||
};
|
||||
#define NODE_NATIVES_MAP(V) \\
|
||||
{node_natives_map}
|
||||
|
||||
static const struct _native natives[] = { %(native_lines)s };
|
||||
namespace node {{
|
||||
{sources}
|
||||
}} // namespace node
|
||||
|
||||
}
|
||||
#endif
|
||||
#endif // NODE_NATIVES_H_
|
||||
"""
|
||||
|
||||
|
||||
NATIVE_DECLARATION = """\
|
||||
{ "%(id)s", %(escaped_id)s_native, sizeof(%(escaped_id)s_native) },
|
||||
"""
|
||||
|
||||
SOURCE_DECLARATION = """\
|
||||
const unsigned char %(escaped_id)s_native[] = { %(data)s };
|
||||
NODE_NATIVES_MAP = """\
|
||||
V({escaped_id}) \\
|
||||
"""
|
||||
|
||||
|
||||
GET_DELAY_INDEX_CASE = """\
|
||||
if (strcmp(name, "%(id)s") == 0) return %(i)i;
|
||||
SOURCES = """\
|
||||
static const uint8_t {escaped_id}_name[] = {{
|
||||
{name}}};
|
||||
static const uint8_t {escaped_id}_data[] = {{
|
||||
{data}}};
|
||||
"""
|
||||
|
||||
|
||||
GET_DELAY_SCRIPT_SOURCE_CASE = """\
|
||||
if (index == %(i)i) return Vector<const char>(%(id)s, %(length)i);
|
||||
"""
|
||||
|
||||
|
||||
GET_DELAY_SCRIPT_NAME_CASE = """\
|
||||
if (index == %(i)i) return Vector<const char>("%(name)s", %(length)i);
|
||||
"""
|
||||
|
||||
def JS2C(source, target):
|
||||
ids = []
|
||||
delay_ids = []
|
||||
modules = []
|
||||
# Locate the macros file name.
|
||||
consts = {}
|
||||
macros = {}
|
||||
macro_lines = []
|
||||
@ -235,18 +207,14 @@ def JS2C(source, target):
|
||||
(consts, macros) = ReadMacros(macro_lines)
|
||||
|
||||
# Build source code lines
|
||||
source_lines = [ ]
|
||||
source_lines_empty = []
|
||||
|
||||
native_lines = []
|
||||
node_natives_map = []
|
||||
sources = []
|
||||
|
||||
for s in modules:
|
||||
delay = str(s).endswith('-delay.js')
|
||||
lines = ReadFile(str(s))
|
||||
|
||||
lines = ExpandConstants(lines, consts)
|
||||
lines = ExpandMacros(lines, macros)
|
||||
data = ToCArray(s, lines)
|
||||
data = ToCString(lines)
|
||||
|
||||
# On Windows, "./foo.bar" in the .gyp file is passed as "foo.bar"
|
||||
# so don't assume there is always a slash in the file path.
|
||||
@ -258,89 +226,19 @@ def JS2C(source, target):
|
||||
if '.' in id:
|
||||
id = id.split('.', 1)[0]
|
||||
|
||||
if delay: id = id[:-6]
|
||||
if delay:
|
||||
delay_ids.append((id, len(lines)))
|
||||
else:
|
||||
ids.append((id, len(lines)))
|
||||
|
||||
name = ToCString(id)
|
||||
escaped_id = id.replace('-', '_').replace('/', '_')
|
||||
source_lines.append(SOURCE_DECLARATION % {
|
||||
'id': id,
|
||||
'escaped_id': escaped_id,
|
||||
'data': data
|
||||
})
|
||||
source_lines_empty.append(SOURCE_DECLARATION % {
|
||||
'id': id,
|
||||
'escaped_id': escaped_id,
|
||||
'data': 0
|
||||
})
|
||||
native_lines.append(NATIVE_DECLARATION % {
|
||||
'id': id,
|
||||
'escaped_id': escaped_id
|
||||
})
|
||||
node_natives_map.append(NODE_NATIVES_MAP.format(**locals()))
|
||||
sources.append(SOURCES.format(**locals()))
|
||||
|
||||
# Build delay support functions
|
||||
get_index_cases = [ ]
|
||||
get_script_source_cases = [ ]
|
||||
get_script_name_cases = [ ]
|
||||
|
||||
i = 0
|
||||
for (id, length) in delay_ids:
|
||||
native_name = "native %s.js" % id
|
||||
get_index_cases.append(GET_DELAY_INDEX_CASE % { 'id': id, 'i': i })
|
||||
get_script_source_cases.append(GET_DELAY_SCRIPT_SOURCE_CASE % {
|
||||
'id': id,
|
||||
'length': length,
|
||||
'i': i
|
||||
})
|
||||
get_script_name_cases.append(GET_DELAY_SCRIPT_NAME_CASE % {
|
||||
'name': native_name,
|
||||
'length': len(native_name),
|
||||
'i': i
|
||||
});
|
||||
i = i + 1
|
||||
|
||||
for (id, length) in ids:
|
||||
native_name = "native %s.js" % id
|
||||
get_index_cases.append(GET_DELAY_INDEX_CASE % { 'id': id, 'i': i })
|
||||
get_script_source_cases.append(GET_DELAY_SCRIPT_SOURCE_CASE % {
|
||||
'id': id,
|
||||
'length': length,
|
||||
'i': i
|
||||
})
|
||||
get_script_name_cases.append(GET_DELAY_SCRIPT_NAME_CASE % {
|
||||
'name': native_name,
|
||||
'length': len(native_name),
|
||||
'i': i
|
||||
});
|
||||
i = i + 1
|
||||
node_natives_map = ''.join(node_natives_map)
|
||||
sources = ''.join(sources)
|
||||
|
||||
# Emit result
|
||||
output = open(str(target[0]), "w")
|
||||
output.write(HEADER_TEMPLATE % {
|
||||
'builtin_count': len(ids) + len(delay_ids),
|
||||
'delay_count': len(delay_ids),
|
||||
'source_lines': "\n".join(source_lines),
|
||||
'native_lines': "\n".join(native_lines),
|
||||
'get_index_cases': "".join(get_index_cases),
|
||||
'get_script_source_cases': "".join(get_script_source_cases),
|
||||
'get_script_name_cases': "".join(get_script_name_cases)
|
||||
})
|
||||
output.write(HEADER_TEMPLATE.format(**locals()))
|
||||
output.close()
|
||||
|
||||
if len(target) > 1:
|
||||
output = open(str(target[1]), "w")
|
||||
output.write(HEADER_TEMPLATE % {
|
||||
'builtin_count': len(ids) + len(delay_ids),
|
||||
'delay_count': len(delay_ids),
|
||||
'source_lines': "\n".join(source_lines_empty),
|
||||
'get_index_cases': "".join(get_index_cases),
|
||||
'get_script_source_cases': "".join(get_script_source_cases),
|
||||
'get_script_name_cases': "".join(get_script_name_cases)
|
||||
})
|
||||
output.close()
|
||||
|
||||
def main():
|
||||
natives = sys.argv[1]
|
||||
source_files = sys.argv[2:]
|
||||
|
Loading…
x
Reference in New Issue
Block a user