mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 10:15:07 +08:00
Environments created from the built-in snapshot get the builtins' code cache from that snapshot. An embedder that bootstraps an Environment from scratch (its own isolate and context, no EmbedderSnapshotData) has no way to provide one: every builtin the bootstrap touches is compiled from source in every such process, and each of them then serializes a fresh cache (SaveCodeCache) that only a later worker thread would ever consume. Add node::EmbedderBuiltinCodeCache for that case. Its entries pair a builtin id with a v8::ScriptCompiler::CachedData; Generate(context) compiles every builtin in a context of the right kind of isolate and returns them for a build step to embed, and SetBuiltinCodeCache(isolate_data, cache) attaches them to an IsolateData, next to where a snapshot's code cache lives, so that every Environment created from it afterwards starts with them. The setter runs CachedData::CompatibilityCheck() against the isolate first and returns the result, leaving the IsolateData untouched for a cache made with another V8 version, flag set or read-only snapshot. Entries from a snapshot still merge with it (RefreshCodeCache() now merges instead of assuming a single call). CreateEnvironment() plus LoadEnvironment() of an empty script goes from about 38 ms to 13 ms with a cache attached. ProcessInitializationFlags::kNoHarvestBuiltinCodeCache stops serializing caches for builtins compiled without one, for embedders that supply their own or never create workers. The default is unchanged because worker threads copy the harvested cache. A cctest generates a cache, attaches it, checks that a new Environment's bootstrap compiles from it and that a corrupted cache is refused; embedtest gains --no-harvest-builtin-code-cache for a test that a worker does or does not find a harvested cache depending on the flag. Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com> PR-URL: https://github.com/nodejs/node/pull/65352 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
78 lines
2.8 KiB
C++
78 lines
2.8 KiB
C++
#include "node_builtins.h"
|
|
#include "node_threadsafe_cow-inl.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
#include "node_test_fixture.h"
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
using node::builtins::BuiltinCodeCacheData;
|
|
using node::builtins::BuiltinLoader;
|
|
using node::builtins::BuiltinSourceMap;
|
|
using node::builtins::CodeCacheInfo;
|
|
|
|
class PerProcessTest : public ::testing::Test {
|
|
protected:
|
|
static const BuiltinSourceMap get_sources_for_test() {
|
|
return *BuiltinLoader().source_.read();
|
|
}
|
|
|
|
// id -> first byte of the cached data, after feeding `batches` in order.
|
|
static std::vector<std::pair<std::string, uint8_t>> RefreshCodeCacheWith(
|
|
const std::vector<std::vector<CodeCacheInfo>>& batches) {
|
|
BuiltinLoader loader;
|
|
for (const auto& batch : batches) loader.RefreshCodeCache(batch);
|
|
std::vector<std::pair<std::string, uint8_t>> out;
|
|
node::RwLock::ScopedReadLock lock(loader.code_cache_->mutex);
|
|
EXPECT_TRUE(loader.code_cache_->has_code_cache);
|
|
for (const auto& [id, data] : loader.code_cache_->map) {
|
|
out.emplace_back(id, data.data[0]);
|
|
}
|
|
std::sort(out.begin(), out.end());
|
|
return out;
|
|
}
|
|
};
|
|
|
|
CodeCacheInfo MakeCodeCacheInfo(const std::string& id, uint8_t marker) {
|
|
auto* bytes = new uint8_t[4]{marker, marker, marker, marker};
|
|
auto cached_data = std::make_shared<v8::ScriptCompiler::CachedData>(
|
|
bytes, 4, v8::ScriptCompiler::CachedData::BufferOwned);
|
|
return CodeCacheInfo{id, BuiltinCodeCacheData(std::move(cached_data))};
|
|
}
|
|
|
|
namespace {
|
|
|
|
TEST_F(PerProcessTest, EmbeddedSources) {
|
|
const auto& sources = PerProcessTest::get_sources_for_test();
|
|
ASSERT_TRUE(std::any_of(sources.cbegin(), sources.cend(), [](auto p) {
|
|
return p.second.source.is_one_byte();
|
|
})) << "BuiltinLoader::source_ should have some 8bit items";
|
|
|
|
ASSERT_TRUE(std::any_of(sources.cbegin(), sources.cend(), [](auto p) {
|
|
return !p.second.source.is_one_byte();
|
|
})) << "BuiltinLoader::source_ should have some 16bit items";
|
|
}
|
|
|
|
// RefreshCodeCache() merges: it can be fed the snapshot's code cache and then
|
|
// an embedder's, and the entry supplied last wins for a shared id.
|
|
TEST_F(PerProcessTest, RefreshCodeCacheMerges) {
|
|
const auto merged = PerProcessTest::RefreshCodeCacheWith({
|
|
{MakeCodeCacheInfo("internal/a", 1), MakeCodeCacheInfo("internal/b", 1)},
|
|
{MakeCodeCacheInfo("internal/b", 2), MakeCodeCacheInfo("embedder/c", 2)},
|
|
});
|
|
const std::vector<std::pair<std::string, uint8_t>> expected = {
|
|
{"embedder/c", 2}, {"internal/a", 1}, {"internal/b", 2}};
|
|
EXPECT_EQ(merged, expected);
|
|
|
|
// A single call still behaves as before.
|
|
const auto single = PerProcessTest::RefreshCodeCacheWith(
|
|
{{MakeCodeCacheInfo("internal/a", 7)}});
|
|
ASSERT_EQ(single.size(), 1u);
|
|
EXPECT_EQ(single[0].second, 7);
|
|
}
|
|
|
|
} // end namespace
|