X Tutup
The Wayback Machine - https://web.archive.org/web/20221221140958/https://github.com/nodejs/node/issues/45905
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose a faster way to create objects with properties for Node-API #45905

Open
devongovett opened this issue Dec 19, 2022 · 4 comments
Open

Expose a faster way to create objects with properties for Node-API #45905

devongovett opened this issue Dec 19, 2022 · 4 comments
Labels
feature request Issues that request new features to be added to Node.js. node-api Issues and PRs related to the Node-API.

Comments

@devongovett
Copy link
Contributor

devongovett commented Dec 19, 2022

What is the problem this feature will solve?

At the moment, there are a few ways to create objects using the napi C APIs.

  • napi_create_object, and then napi_set_named_property for each property.
  • napi_create_object, and use napi_define_properties to set all properties at once (under the hood it still sets them one by one though).
  • use napi_define_class + napi_new_instance, and then add properties using one of the above methods. You can define accessors on the prototype, or primitive values but instance properties must be set the same way as above.

In my use case I need to create a lot of small objects of the same shape. I've noticed in profiling that v8 functions under napi_set_named_property are slow, specifically v8::internal::JSObject::MigrateToMap. From my research in the v8 source code, this indicates that the object is transitioning from a fast struct/class-like object to a slower dictionary representation (I could be wrong here).

I think v8 exposes ObjectTemplate and FunctionTemplate->InstanceTemplate which are meant to help with this by defining the instance properties up front. This way, the object property slots can be allocated in the object itself rather than in a separate hash map. I found some useful info about v8 object representations here. I see that Node makes use of ObjectTemplate for internal objects, and a previous issue nodejs/node-addon-api#1074 also found this to be the fastest way to create objects (though still not as fast as doing it in JS).

My problem is that napi does not expose a way to create an object together with its properties, or a way to define a template for class instance properties. This means, as far as I can tell, all objects constructed through napi will end up in the slow dictionary mode, leading to slower perf when both constructing and accessing properties.

What is the feature you are proposing to solve the problem?

It would be awesome if node exposed a new napi_create_object_with_properties function, which would accept a list of property descriptors like napi_define_properties and allocate an object and assign properties all at once. This could potentially allow using faster v8 methods to create the object and assign properties so that it doesn't go into hashmap mode when adding properties one by one.

Alternatively, a way to define an instance property template for classes would also work for me. For example, napi_property_attributes could be extended with a napi_instance attribute for defining properties on the instance template rather than the prototype template.

What alternatives have you considered?

I'm not a node or v8 internal expert, just noticed that setting properties on objects seemed slower than expected, so I could be totally wrong about everything above. Opening this issue to get a conversation started. Totally open to other suggestions!

@devongovett devongovett added the feature request Issues that request new features to be added to Node.js. label Dec 19, 2022
@VoltrexKeyva VoltrexKeyva added the node-api Issues and PRs related to the Node-API. label Dec 19, 2022
@bnoordhuis
Copy link
Member

bnoordhuis commented Dec 19, 2022

This means, as far as I can tell, all objects constructed through napi will end up in the slow dictionary mode

Dictionary mode happens when an object has too many properties. In that respect ObjectTemplate vs. adding properties one by one doesn't matter.

Instantiating an ObjectTemplate is marginally faster because you're not going through the JS engine's "add property to object" code path repeatedly but it won't be noticeable unless you create millions of objects or your objects have many properties.

As a practical concern: support for JS engines other than V8 is a design goal of n-api and I don't think other engines have a concept that maps cleanly to ObjecTemplate. Spidermonkey's JSClass for example isn't a 1-to-1 fit.

@devongovett
Copy link
Contributor Author

devongovett commented Dec 19, 2022

Dictionary mode happens when an object has too many properties

Hmm, in my benchmark all the objects I created have exactly 2 properties. Did I misinterpret the profile (what MigrateToMap does)?

I don't think other engines have a concept that maps cleanly to ObjecTemplate

They must have some way of constructing an object with properties upfront through, similar to creating an object literal. And if not, the API could always fall back to constructing an object and assigning properties one by one internally.

@bnoordhuis
Copy link
Member

bnoordhuis commented Dec 19, 2022

what MigrateToMap does

"Map" in V8 nomenclature is what other JS engines call "object shape" or "hidden class", it's a piece of metadata that describes the object's layout.

It's used every time an object's layout changes (at least in C++ code), not just when switching from fast to slow mode.

They must have some way of constructing an object with properties upfront through, similar to creating an object literal.

Oh yeah, they probably do.

I was thinking of lifetime issues. A JSClass has a static lifetime whereas an ObjectTemplate only lives as long as its isolate. Other engines tie such magic objects to the lifetime of the current execution context (shorter still.)

It's probably not insurmountable but some upfront research is necessary. (I'm not volunteering!)

@devongovett
Copy link
Contributor Author

devongovett commented Dec 19, 2022

I created a simple benchmark in this repo showing the difference between various methods. Each creates 100000 objects with 2 properties and appends them to an array.

napi: 48.992ms
v8 tmpl: 16.973ms
js: 2.663ms

Even with ObjectTemplate, JS object literals are still significantly faster.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Issues that request new features to be added to Node.js. node-api Issues and PRs related to the Node-API.
Projects
None yet
Development

No branches or pull requests

3 participants
X Tutup