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
}

0 commit comments

Comments
 (0)