src: stop including simdjson.h from env.h

env.h includes inspector_profiler.h, which included simdjson.h for
the types of two virtual methods and a parser member. As a result,
about 155 files in src/ parsed the amalgamated simdjson header,
which is over 10 MB.

Keep simdjson out of inspector_profiler.h: the profile connections
now say which field of `result` holds the profile, and
WriteProfile() takes the profile as raw JSON. The parser becomes a
local variable, so its buffers are no longer kept alive for the
lifetime of the Environment.

Also drop the unused include from node_task_runner.h, and let the
config file reader rewind its document instead of parsing the whole
file a second time with a second parser.

Now only the eight files that use simdjson include it. On an Apple
M-series machine, a clean rebuild of the src/ objects takes about
15% less CPU time (376 s to 320 s) and 19% less wall time at -j14.

Signed-off-by: Daniel Lemire <daniel@lemire.me>
PR-URL: https://github.com/nodejs/node/pull/66620
Refs: https://github.com/simdjson/simdjson/pull/2913
Refs: https://github.com/nodejs/node/pull/66495
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Richard Lau <richard.lau@ibm.com>
This commit is contained in:
Daniel Lemire authored and Node.js GitHub Bot committed 2026-10-11 03:36:15 +00:00
1 parent 08a7e391b1
commit 2e9d43c3b7
5 files changed
+45 -70

No files matched your search

+32 -48
View File
@@ -13,7 +13,9 @@
#include <cinttypes>
#include <limits>
#include <optional>
#include <sstream>
#include "simdjson.h"
#include "simdutf.h"
namespace node {
@@ -109,6 +111,28 @@ bool StringViewToUTF8(const v8_inspector::StringView& source,
return *utf8_length == result_len;
}
// Return the raw JSON of the profile object in `result`: either the
// `field` member of `result` or, if `field` is nullptr, `result` itself.
static std::optional<std::string_view> GetProfile(
simdjson::ondemand::object* result, const char* field, const char* type) {
simdjson::ondemand::object profile_object;
if (field == nullptr) {
profile_object = *result;
} else if ((*result)[field].get_object().get(profile_object)) {
fprintf(
stderr, "'%s' from %s profile result is not an Object\n", field, type);
return std::nullopt;
}
std::string_view profile_raw;
if (profile_object.raw_json().get(profile_raw)) {
fprintf(stderr,
"Cannot get raw string of the 'profile' field from %s profile\n",
type);
return std::nullopt;
}
return profile_raw;
}
void V8ProfilerConnection::V8ProfilerSessionDelegate::SendMessageToFrontend(
const v8_inspector::StringView& message) {
Environment* env = connection_->env();
@@ -134,9 +158,10 @@ void V8ProfilerConnection::V8ProfilerSessionDelegate::SendMessageToFrontend(
return;
}
simdjson::ondemand::parser json_parser;
simdjson::ondemand::document parsed;
simdjson::ondemand::object response;
if (connection_->json_parser_
if (json_parser
.iterate(
message_utf8.data(), message_utf8_length, message_utf8.size())
.get(parsed) ||
@@ -184,7 +209,10 @@ void V8ProfilerConnection::V8ProfilerSessionDelegate::SendMessageToFrontend(
return;
}
connection_->WriteProfile(&result);
auto profile = GetProfile(&result, connection_->profile_field(), type);
if (profile.has_value()) {
connection_->WriteProfile(*profile);
}
connection_->RemoveProfileId(id);
}
@@ -214,32 +242,7 @@ std::string V8CoverageConnection::GetFilename() const {
env()->thread_id());
}
std::optional<std::string_view> V8ProfilerConnection::GetProfile(
simdjson::ondemand::object* result) {
simdjson::ondemand::object profile_object;
if ((*result)["profile"].get_object().get(profile_object)) {
fprintf(
stderr, "'profile' from %s profile result is not an Object\n", type());
return std::nullopt;
}
std::string_view profile_raw;
if (profile_object.raw_json().get(profile_raw)) {
fprintf(stderr,
"Cannot get raw string of the 'profile' field from %s profile\n",
type());
return std::nullopt;
}
return profile_raw;
}
void V8ProfilerConnection::WriteProfile(simdjson::ondemand::object* result) {
// Generate the profile output from the subclass.
auto profile_opt = GetProfile(result);
if (!profile_opt.has_value()) {
return;
}
std::string_view profile = profile_opt.value();
void V8ProfilerConnection::WriteProfile(std::string_view profile) {
// Create the directory if necessary.
std::string directory = GetDirectory();
DCHECK(!directory.empty());
@@ -254,7 +257,7 @@ void V8ProfilerConnection::WriteProfile(simdjson::ondemand::object* result) {
WriteResult(env_, path.c_str(), profile);
}
void V8CoverageConnection::WriteProfile(simdjson::ondemand::object* result) {
void V8CoverageConnection::WriteProfile(std::string_view profile) {
Isolate* isolate = env_->isolate();
HandleScope handle_scope(isolate);
@@ -271,13 +274,6 @@ void V8CoverageConnection::WriteProfile(simdjson::ondemand::object* result) {
Local<Context> context = env_->context();
Context::Scope context_scope(context);
// Generate the profile output from the subclass.
auto profile_opt = GetProfile(result);
if (!profile_opt.has_value()) {
return;
}
std::string_view profile = profile_opt.value();
// append source-map cache information to coverage object:
Local<Value> source_map_cache_v;
{
@@ -349,18 +345,6 @@ void V8CoverageConnection::WriteProfile(simdjson::ondemand::object* result) {
}
}
std::optional<std::string_view> V8CoverageConnection::GetProfile(
simdjson::ondemand::object* result) {
std::string_view profile_raw;
if (result->raw_json().get(profile_raw)) {
fprintf(stderr,
"Cannot get raw string of the 'profile' field from %s profile\n",
type());
return std::nullopt;
}
return profile_raw;
}
std::string V8CoverageConnection::GetDirectory() const {
return env()->coverage_directory();
}
+8 -11
View File
@@ -7,10 +7,9 @@
#error("This header can only be used when inspector is enabled")
#endif
#include <optional>
#include <string_view>
#include <unordered_set>
#include "inspector_agent.h"
#include "simdjson.h"
namespace node {
// Forward declaration to break recursive dependency chain with src/env.h.
@@ -59,11 +58,11 @@ class V8ProfilerConnection {
virtual std::string GetDirectory() const = 0;
// Return the filename the profile should be written as.
virtual std::string GetFilename() const = 0;
// Return the profile object parsed from `message.result`,
// which will be then written as a JSON.
virtual std::optional<std::string_view> GetProfile(
simdjson::ondemand::object* result);
virtual void WriteProfile(simdjson::ondemand::object* result);
// Return the field of `message.result` that holds the profile object,
// or nullptr if `message.result` is itself the profile object.
virtual const char* profile_field() const { return "profile"; }
// Write the profile, given as raw JSON.
virtual void WriteProfile(std::string_view profile);
bool HasProfileId(uint64_t id) const { return profile_ids_.contains(id); }
@@ -76,7 +75,6 @@ class V8ProfilerConnection {
std::unordered_set<uint64_t> profile_ids_;
protected:
simdjson::ondemand::parser json_parser_;
Environment* env_ = nullptr;
};
@@ -92,9 +90,8 @@ class V8CoverageConnection : public V8ProfilerConnection {
std::string GetDirectory() const override;
std::string GetFilename() const override;
std::optional<std::string_view> GetProfile(
simdjson::ondemand::object* result) override;
void WriteProfile(simdjson::ondemand::object* result) override;
const char* profile_field() const override { return nullptr; }
void WriteProfile(std::string_view profile) override;
void WriteSourceMapCache();
void TakeCoverage();
void StopCoverage();
+4 -10
View File
@@ -573,19 +573,13 @@ ParseResult ConfigReader::ParseConfig(const std::string_view& config_path) {
return ParseResult::InvalidContent;
}
simdjson::ondemand::parser config_parser;
simdjson::ondemand::document config_document;
if (config_parser.iterate(file_content).get(config_document)) {
FPrintF(stderr, "Can't parse %s\n", config_path.data());
// Walk the document again, this time to parse the configuration.
document.rewind();
if (document.get_object().get(main_object)) {
return ParseResult::InvalidContent;
}
simdjson::ondemand::object config_object;
if (config_document.get_object().get(config_object)) {
return ParseResult::InvalidContent;
}
return ParseConfigObject(&config_object, config_path, true);
return ParseConfigObject(&main_object, config_path, true);
}
std::string ConfigReader::GetNodeOptions() {
+1
View File
@@ -1,4 +1,5 @@
#include "node_task_runner.h"
#include "simdjson.h"
#include "util-inl.h"
#include <regex> // NOLINT(build/c++11)
-1
View File
@@ -4,7 +4,6 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#include "node_internals.h"
#include "simdjson.h"
#include "spawn_sync.h"
#include "uv.h"