Skip to content

Commit 3836ab5

Browse files
committed
build: speed up compilation of mksnapshot output
Incremental compilation of Node.js is slow. Currently on a powerful Linux machine, it takes about 5.8 seconds to compile `gen/node_snapshot.cc` with g++. As in the previous PR which dealt with `node_js2c`, we add a new build define `NODE_MKSNAPSHOT_USE_STRING_LITERALS` which is used by `node_mksnapshot`. When this flag is set, we emit string literals instead of array literals for the snapshot blob and for the code cache, i.e.: ```c++ // old: static const uint8_t X[] = { ... }; static const uint8_t *X = "..."; ``` I only enabled the new flag on Linux/macOS, since those are systems that I have available for testing. On my Linux system with gcc, it speeds up compilation of this file by 3.7s (5.8s -> 2.1s). On my Mac system with clang, it speeds up compilation by 1.7s (3.4s -> 1.7s). Again, the right thing here is probably to generate separate files for the snapshot blob and for each code cache output, but this is a nice intermediate speedup. Refs: #47984 Refs: #48160
1 parent 817c579 commit 3836ab5

File tree

2 files changed

+67
-13
lines changed

2 files changed

+67
-13
lines changed

node.gyp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,9 @@
872872
'node_target_type=="executable"', {
873873
'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ],
874874
}],
875+
['OS in "linux mac"', {
876+
'defines': [ 'NODE_MKSNAPSHOT_USE_STRING_LITERALS' ],
877+
}],
875878
[ 'use_openssl_def==1', {
876879
# TODO(bnoordhuis) Make all platforms export the same list of symbols.
877880
# Teach mkssldef.py to generate linker maps that UNIX linkers understand.

src/node_snapshotable.cc

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -742,18 +742,56 @@ static std::string FormatSize(size_t size) {
742742
return buf;
743743
}
744744

745-
static void WriteStaticCodeCacheData(std::ostream* ss,
746-
const builtins::CodeCacheInfo& info) {
745+
#ifdef NODE_MKSNAPSHOT_USE_STRING_LITERALS
746+
static void WriteDataAsCharString(std::ostream* ss,
747+
const uint8_t* data,
748+
size_t length) {
749+
for (size_t i = 0; i < length; i++) {
750+
const uint8_t ch = data[i];
751+
// We can print most printable characters directly. The exceptions are '\'
752+
// (escape characters), " (would end the string), and ? (trigraphs). The
753+
// latter may be overly conservative: we compile with C++17 which doesn't
754+
// support trigraphs.
755+
if (ch >= ' ' && ch <= '~' && ch != '\\' && ch != '"' && ch != '?') {
756+
*ss << ch;
757+
} else {
758+
// All other characters are blindly output as octal.
759+
const char c0 = '0' + ((ch >> 6) & 7);
760+
const char c1 = '0' + ((ch >> 3) & 7);
761+
const char c2 = '0' + (ch & 7);
762+
*ss << "\\" << c0 << c1 << c2;
763+
}
764+
if (i % 64 == 63) {
765+
// Go to a newline every 64 bytes since many text editors have
766+
// problems with very long lines.
767+
*ss << "\"\n\"";
768+
}
769+
}
770+
}
771+
772+
static void WriteStaticCodeCacheDataAsStringLiteral(
773+
std::ostream* ss, const builtins::CodeCacheInfo& info) {
774+
*ss << "static const uint8_t *" << GetCodeCacheDefName(info.id)
775+
<< "= reinterpret_cast<const uint8_t *>(\"";
776+
WriteDataAsCharString(ss, info.data.data, info.data.length);
777+
*ss << "\");\n";
778+
}
779+
#else
780+
static void WriteStaticCodeCacheDataAsArray(
781+
std::ostream* ss, const builtins::CodeCacheInfo& info) {
747782
*ss << "static const uint8_t " << GetCodeCacheDefName(info.id) << "[] = {\n";
748783
WriteVector(ss, info.data.data, info.data.length);
749-
*ss << "};";
784+
*ss << "};\n";
750785
}
786+
#endif
751787

752-
static void WriteCodeCacheInitializer(std::ostream* ss, const std::string& id) {
788+
static void WriteCodeCacheInitializer(std::ostream* ss,
789+
const std::string& id,
790+
size_t size) {
753791
std::string def_name = GetCodeCacheDefName(id);
754792
*ss << " { \"" << id << "\",\n";
755793
*ss << " {" << def_name << ",\n";
756-
*ss << " arraysize(" << def_name << "),\n";
794+
*ss << " " << size << ",\n";
757795
*ss << " }\n";
758796
*ss << " },\n";
759797
}
@@ -767,21 +805,34 @@ void FormatBlob(std::ostream& ss, const SnapshotData* data) {
767805
// This file is generated by tools/snapshot. Do not edit.
768806
769807
namespace node {
770-
771-
static const char v8_snapshot_blob_data[] = {
772808
)";
809+
810+
#ifdef NODE_MKSNAPSHOT_USE_STRING_LITERALS
811+
ss << R"(static const char *v8_snapshot_blob_data = ")";
812+
WriteDataAsCharString(
813+
&ss,
814+
reinterpret_cast<const uint8_t*>(data->v8_snapshot_blob_data.data),
815+
data->v8_snapshot_blob_data.raw_size);
816+
ss << R"(";)";
817+
#else
818+
ss << R"(static const char v8_snapshot_blob_data[] = {)";
773819
WriteVector(&ss,
774820
data->v8_snapshot_blob_data.data,
775821
data->v8_snapshot_blob_data.raw_size);
776-
ss << R"(};
822+
ss << R"(};)";
823+
#endif
777824

778-
static const int v8_snapshot_blob_size = )"
825+
ss << R"(static const int v8_snapshot_blob_size = )"
779826
<< data->v8_snapshot_blob_data.raw_size << ";";
780827

781-
// Windows can't deal with too many large vector initializers.
782-
// Store the data into static arrays first.
783828
for (const auto& item : data->code_cache) {
784-
WriteStaticCodeCacheData(&ss, item);
829+
#ifdef NODE_MKSNAPSHOT_USE_STRING_LITERALS
830+
WriteStaticCodeCacheDataAsStringLiteral(&ss, item);
831+
#else
832+
// Windows can't deal with too many large vector initializers.
833+
// Store the data into static arrays first.
834+
WriteStaticCodeCacheDataAsArray(&ss, item);
835+
#endif
785836
}
786837

787838
ss << R"(const SnapshotData snapshot_data {
@@ -808,7 +859,7 @@ static const int v8_snapshot_blob_size = )"
808859
// -- code_cache begins --
809860
{)";
810861
for (const auto& item : data->code_cache) {
811-
WriteCodeCacheInitializer(&ss, item.id);
862+
WriteCodeCacheInitializer(&ss, item.id, item.data.length);
812863
}
813864
ss << R"(
814865
}

0 commit comments

Comments
 (0)