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

Statically cache the String value of g_internal_field for a small performance gain #4863

Merged
merged 5 commits into from
Sep 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## vNext (TBD)

### Enhancements
* Small improvement to performance by caching JSI property String object [#4863](https://github.com/realm/realm-js/pull/4863)
* None.

### Fixed
Expand Down
20 changes: 16 additions & 4 deletions src/jsi/jsi_class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,10 @@ inline void copyProperty(JsiEnv env, const fbjsi::Object& from, const fbjsi::Obj
defineProperty(env, to, name, *prop);
}

inline constexpr const char g_internal_field[] = "__Realm_internal";
// The name used for the property on the JS object which stores the reference to the corresponding C++ object.
// We use an empty string as testing showed it was 1% faster with JSC and 4% faster with Hermes than using
// an actual string, and also has the benefit that it is not a valid Realm object key name.
inline constexpr const char g_internal_field[] = "";

template <typename T>
using ClassDefinition = js::ClassDefinition<js::realmjsi::Types, T>;
Expand Down Expand Up @@ -240,6 +243,10 @@ class ObjectWrap {
// Also, may need to suppress destruction.
inline static std::optional<JsiFunc> s_ctor;

// Cache the JSI String instance representing the field name of the internal
// C++ object for the lifetime of the current env, as this is a hot path
inline static std::optional<fbjsi::String> s_js_internal_field_name;

/**
* @brief callback for invalid access to index setters
* Throws an error when a users attemps to write to an index on a type that
Expand Down Expand Up @@ -310,9 +317,10 @@ class ObjectWrap {
.asFunction(env));

js::Context<js::realmjsi::Types>::register_invalidator([] {
// Ensure the static constructor is destructed when the runtime goes away.
// This is to avoid the reassignment of s_ctor throwing because the runtime has disappeared.
// Ensure the static constructor and JSI String reference are destructed when the runtime goes away.
// This is to avoid reassignment and destruction throwing because the runtime has disappeared.
s_ctor.reset();
s_js_internal_field_name.reset();
});

for (auto&& [name, prop] : s_type.static_properties) {
Expand Down Expand Up @@ -490,7 +498,11 @@ class ObjectWrap {

static Internal* get_internal(JsiEnv env, const JsiObj& object)
{
auto internal = object->getProperty(env, g_internal_field);
if (REALM_UNLIKELY(!s_js_internal_field_name)) {
s_js_internal_field_name = fbjsi::String::createFromAscii(env, g_internal_field);
}

auto internal = object->getProperty(env, *s_js_internal_field_name);
if (internal.isUndefined()) {
// In the case of a user opening a Realm with a class-based model,
// the user defined constructor will get called before the "internal" property has been set.
Expand Down