Skip to content

Commit 629fc77

Browse files
joyeecheungtargos
authored andcommitted
src: use an array for faster binding data lookup
Locally the hashing of the binding names sometimes has significant presence in the profile of bindings, because there can be collisions, which makes the cost of adding a new binding data non-trivial, but it's wasteful to spend time on hashing them or dealing with collisions at all, since we can just use the EmbedderObjectType enum as the key, as the string names are not actually used beyond debugging purposes and can be easily matched with a macro. And since we can just use the enum as the key, we do not even need the map and can just use an array with the enum as indices for the lookup. PR-URL: #46620 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]>
1 parent 23069c3 commit 629fc77

17 files changed

+49
-62
lines changed

node.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,7 @@
585585
'src/async_wrap-inl.h',
586586
'src/base_object.h',
587587
'src/base_object-inl.h',
588+
'src/base_object_types.h',
588589
'src/base64.h',
589590
'src/base64-inl.h',
590591
'src/callback_queue.h',

src/README.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -528,18 +528,32 @@ that state is through the use of `Realm::AddBindingData`, which gives
528528
binding functions access to an object for storing such state.
529529
That object is always a [`BaseObject`][].
530530

531-
Its class needs to have a static `type_name` field based on a
532-
constant string, in order to disambiguate it from other classes of this type,
533-
and which could e.g. match the binding's name (in the example above, that would
534-
be `cares_wrap`).
531+
In the binding, call `SET_BINDING_ID()` with an identifier for the binding
532+
type. For example, for `http_parser::BindingData`, the identifier can be
533+
`http_parser_binding_data`.
534+
535+
If the binding should be supported in a snapshot, the id and the
536+
fully-specified class name should be added to the `SERIALIZABLE_BINDING_TYPES`
537+
list in `base_object_types.h`, and the class should implement the serialization
538+
and deserialization methods. See the comments of `SnapshotableObject` on how to
539+
implement them. Otherwise, add the id and the class name to the
540+
`UNSERIALIZABLE_BINDING_TYPES` list instead.
535541

536542
```cpp
543+
// In base_object_types.h, add the binding to either
544+
// UNSERIALIZABLE_BINDING_TYPES or SERIALIZABLE_BINDING_TYPES.
545+
// The second parameter is a descriptive name of the class, which is
546+
// usually the fully-specified class name.
547+
548+
#define UNSERIALIZABLE_BINDING_TYPES(V) \
549+
V(http_parser_binding_data, http_parser::BindingData)
550+
537551
// In the HTTP parser source code file:
538552
class BindingData : public BaseObject {
539553
public:
540554
BindingData(Realm* realm, Local<Object> obj) : BaseObject(realm, obj) {}
541555

542-
static constexpr FastStringKey type_name { "http_parser" };
556+
SET_BINDING_ID(http_parser_binding_data)
543557

544558
std::vector<char> parser_buffer;
545559
bool parser_buffer_in_use = false;
@@ -569,12 +583,6 @@ void InitializeHttpParser(Local<Object> target,
569583
}
570584
```
571585
572-
If the binding is loaded during bootstrap, add it to the
573-
`SERIALIZABLE_OBJECT_TYPES` list in `src/node_snapshotable.h` and
574-
inherit from the `SnapshotableObject` class instead. See the comments
575-
of `SnapshotableObject` on how to implement its serialization and
576-
deserialization.
577-
578586
<a id="exception-handling"></a>
579587
580588
### Exception handling

src/base_object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
2626

2727
#include <type_traits> // std::remove_reference
28+
#include "base_object_types.h"
2829
#include "memory_tracker.h"
2930
#include "v8.h"
3031

src/base_object_types.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@ namespace node {
1010
// what the class passes to SET_BINDING_ID(), the second argument should match
1111
// the C++ class name.
1212
#define SERIALIZABLE_BINDING_TYPES(V) \
13-
V(encoding_binding_data, encoding_binding::BindingData) \
1413
V(fs_binding_data, fs::BindingData) \
1514
V(v8_binding_data, v8_utils::BindingData) \
1615
V(blob_binding_data, BlobBindingData) \
1716
V(process_binding_data, process::BindingData) \
18-
V(timers_binding_data, timers::BindingData) \
1917
V(url_binding_data, url::BindingData)
2018

2119
#define UNSERIALIZABLE_BINDING_TYPES(V) \

src/node_blob.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,7 @@ class BlobBindingData : public SnapshotableObject {
147147

148148
SERIALIZABLE_OBJECT_METHODS()
149149

150-
static constexpr FastStringKey type_name{"node::BlobBindingData"};
151-
static constexpr EmbedderObjectType type_int =
152-
EmbedderObjectType::k_blob_binding_data;
150+
SET_BINDING_ID(blob_binding_data)
153151

154152
void MemoryInfo(MemoryTracker* tracker) const override;
155153
SET_SELF_SIZE(BlobBindingData)

src/node_file.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,7 @@ class BindingData : public SnapshotableObject {
6969

7070
using InternalFieldInfo = InternalFieldInfoBase;
7171
SERIALIZABLE_OBJECT_METHODS()
72-
static constexpr FastStringKey type_name{"node::fs::BindingData"};
73-
static constexpr EmbedderObjectType type_int =
74-
EmbedderObjectType::k_fs_binding_data;
72+
SET_BINDING_ID(fs_binding_data)
7573

7674
void MemoryInfo(MemoryTracker* tracker) const override;
7775
SET_SELF_SIZE(BindingData)

src/node_http2_state.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ class Http2State : public BaseObject {
125125
SET_SELF_SIZE(Http2State)
126126
SET_MEMORY_INFO_NAME(Http2State)
127127

128-
static constexpr FastStringKey type_name { "http2" };
128+
SET_BINDING_ID(http2_binding_data)
129129

130130
private:
131131
struct http2_state_internal {

src/node_http_parser.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class BindingData : public BaseObject {
9595
public:
9696
BindingData(Realm* realm, Local<Object> obj) : BaseObject(realm, obj) {}
9797

98-
static constexpr FastStringKey type_name { "http_parser" };
98+
SET_BINDING_ID(http_parser_binding_data)
9999

100100
std::vector<char> parser_buffer;
101101
bool parser_buffer_in_use = false;

src/node_process.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,7 @@ class BindingData : public SnapshotableObject {
5454
using InternalFieldInfo = InternalFieldInfoBase;
5555

5656
SERIALIZABLE_OBJECT_METHODS()
57-
static constexpr FastStringKey type_name{"node::process::BindingData"};
58-
static constexpr EmbedderObjectType type_int =
59-
EmbedderObjectType::k_process_binding_data;
57+
SET_BINDING_ID(process_binding_data)
6058

6159
BindingData(Realm* realm, v8::Local<v8::Object> object);
6260

src/node_realm-inl.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,11 @@ inline T* Realm::GetBindingData(v8::Local<v8::Context> context) {
6262
static_cast<BindingDataStore*>(context->GetAlignedPointerFromEmbedderData(
6363
ContextEmbedderIndex::kBindingDataStoreIndex));
6464
DCHECK_NOT_NULL(map);
65-
auto it = map->find(T::type_name);
66-
if (UNLIKELY(it == map->end())) return nullptr;
67-
T* result = static_cast<T*>(it->second.get());
65+
constexpr size_t binding_index = static_cast<size_t>(T::binding_type_int);
66+
static_assert(binding_index < std::tuple_size_v<BindingDataStore>);
67+
auto ptr = (*map)[binding_index];
68+
if (UNLIKELY(!ptr)) return nullptr;
69+
T* result = static_cast<T*>(ptr.get());
6870
DCHECK_NOT_NULL(result);
6971
DCHECK_EQ(result->realm(), GetCurrent(context));
7072
return result;
@@ -80,8 +82,10 @@ inline T* Realm::AddBindingData(v8::Local<v8::Context> context,
8082
static_cast<BindingDataStore*>(context->GetAlignedPointerFromEmbedderData(
8183
ContextEmbedderIndex::kBindingDataStoreIndex));
8284
DCHECK_NOT_NULL(map);
83-
auto result = map->emplace(T::type_name, item);
84-
CHECK(result.second);
85+
constexpr size_t binding_index = static_cast<size_t>(T::binding_type_int);
86+
static_assert(binding_index < std::tuple_size_v<BindingDataStore>);
87+
CHECK(!(*map)[binding_index]); // Should not insert the binding twice.
88+
(*map)[binding_index] = item;
8589
DCHECK_EQ(GetBindingData<T>(context), item.get());
8690
return item.get();
8791
}

src/node_realm.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,9 @@ void Realm::DoneBootstrapping() {
287287

288288
void Realm::RunCleanup() {
289289
TRACE_EVENT0(TRACING_CATEGORY_NODE1(realm), "RunCleanup");
290-
binding_data_store_.clear();
291-
290+
for (size_t i = 0; i < binding_data_store_.size(); ++i) {
291+
binding_data_store_[i].reset();
292+
}
292293
cleanup_queue_.Drain();
293294
}
294295

src/node_realm.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ struct RealmSerializeInfo {
2020
friend std::ostream& operator<<(std::ostream& o, const RealmSerializeInfo& i);
2121
};
2222

23-
using BindingDataStore = std::unordered_map<FastStringKey,
24-
BaseObjectPtr<BaseObject>,
25-
FastStringKey::Hash>;
23+
using BindingDataStore = std::array<BaseObjectPtr<BaseObject>,
24+
static_cast<size_t>(
25+
BindingDataType::kBindingDataTypeCount)>;
2626

2727
/**
2828
* node::Realm is a container for a set of JavaScript objects and functions

src/node_snapshotable.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,11 +1308,11 @@ SnapshotableObject::SnapshotableObject(Realm* realm,
13081308
EmbedderObjectType type)
13091309
: BaseObject(realm, wrap), type_(type) {}
13101310

1311-
std::string_view SnapshotableObject::GetTypeName() const {
1311+
std::string SnapshotableObject::GetTypeName() const {
13121312
switch (type_) {
13131313
#define V(PropertyName, NativeTypeName) \
13141314
case EmbedderObjectType::k_##PropertyName: { \
1315-
return NativeTypeName::type_name.as_string_view(); \
1315+
return #NativeTypeName; \
13161316
}
13171317
SERIALIZABLE_OBJECT_TYPES(V)
13181318
#undef V
@@ -1353,7 +1353,7 @@ void DeserializeNodeInternalFields(Local<Object> holder,
13531353
per_process::Debug(DebugCategory::MKSNAPSHOT, \
13541354
"Object %p is %s\n", \
13551355
(*holder), \
1356-
NativeTypeName::type_name.as_string_view()); \
1356+
#NativeTypeName); \
13571357
env_ptr->EnqueueDeserializeRequest( \
13581358
NativeTypeName::Deserialize, \
13591359
holder, \
@@ -1440,7 +1440,7 @@ void SerializeSnapshotableObjects(Realm* realm,
14401440
}
14411441
SnapshotableObject* ptr = static_cast<SnapshotableObject*>(obj);
14421442

1443-
std::string type_name{ptr->GetTypeName()};
1443+
std::string type_name = ptr->GetTypeName();
14441444
per_process::Debug(DebugCategory::MKSNAPSHOT,
14451445
"Serialize snapshotable object %i (%p), "
14461446
"object=%p, type=%s\n",

src/node_snapshotable.h

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,6 @@ struct PropInfo {
2222
SnapshotIndex index; // In the snapshot
2323
};
2424

25-
#define SERIALIZABLE_OBJECT_TYPES(V) \
26-
V(fs_binding_data, fs::BindingData) \
27-
V(v8_binding_data, v8_utils::BindingData) \
28-
V(blob_binding_data, BlobBindingData) \
29-
V(process_binding_data, process::BindingData) \
30-
V(url_binding_data, url::BindingData) \
31-
V(util_weak_reference, util::WeakReference)
32-
33-
enum class EmbedderObjectType : uint8_t {
34-
#define V(PropertyName, NativeType) k_##PropertyName,
35-
SERIALIZABLE_OBJECT_TYPES(V)
36-
#undef V
37-
};
38-
3925
typedef size_t SnapshotIndex;
4026

4127
// When serializing an embedder object, we'll serialize the native states
@@ -102,7 +88,7 @@ class SnapshotableObject : public BaseObject {
10288
SnapshotableObject(Realm* realm,
10389
v8::Local<v8::Object> wrap,
10490
EmbedderObjectType type);
105-
std::string_view GetTypeName() const;
91+
std::string GetTypeName() const;
10692

10793
// If returns false, the object will not be serialized.
10894
virtual bool PrepareForSerialization(v8::Local<v8::Context> context,

src/node_url.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@ class BindingData : public SnapshotableObject {
3737
using InternalFieldInfo = InternalFieldInfoBase;
3838

3939
SERIALIZABLE_OBJECT_METHODS()
40-
static constexpr FastStringKey type_name{"node::url::BindingData"};
41-
static constexpr EmbedderObjectType type_int =
42-
EmbedderObjectType::k_url_binding_data;
40+
SET_BINDING_ID(url_binding_data)
4341

4442
void MemoryInfo(MemoryTracker* tracker) const override;
4543
SET_SELF_SIZE(BindingData)

src/node_util.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ class WeakReference : public SnapshotableObject {
1414
public:
1515
SERIALIZABLE_OBJECT_METHODS()
1616

17-
static constexpr FastStringKey type_name{"node::util::WeakReference"};
18-
static constexpr EmbedderObjectType type_int =
19-
EmbedderObjectType::k_util_weak_reference;
17+
SET_OBJECT_ID(util_weak_reference)
2018

2119
WeakReference(Realm* realm,
2220
v8::Local<v8::Object> object,

src/node_v8.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@ class BindingData : public SnapshotableObject {
2323
using InternalFieldInfo = InternalFieldInfoBase;
2424

2525
SERIALIZABLE_OBJECT_METHODS()
26-
static constexpr FastStringKey type_name{"node::v8::BindingData"};
27-
static constexpr EmbedderObjectType type_int =
28-
EmbedderObjectType::k_v8_binding_data;
26+
SET_BINDING_ID(v8_binding_data)
2927

3028
AliasedFloat64Array heap_statistics_buffer;
3129
AliasedFloat64Array heap_space_statistics_buffer;

0 commit comments

Comments
 (0)