Skip to content

Commit e37f83b

Browse files
[emval] Reduce C++ -> JS calls in emscripten::val lifetime management (#21366)
Related to #21300, but some obviously good incremental wins to reduce the cost of ref counting: 1. Have C++ consistently avoid inc/dec for the special reserved values that don't need to be counted, this saves decref calls every time such a `val` object goes out of scope. 2. Add an rvalue reference version of toWireType for `emscripten::val` that can transfer ownership to JS. This saves one call to incref and one call to decref for the case of a c++ function with return type `emscripten::val` The cost seems to be single-digit bytes to the WASM.
1 parent 56fc942 commit e37f83b

File tree

2 files changed

+35
-8
lines changed

2 files changed

+35
-8
lines changed

system/include/emscripten/val.h

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ enum {
4949
_EMVAL_UNDEFINED = 2,
5050
_EMVAL_NULL = 4,
5151
_EMVAL_TRUE = 6,
52-
_EMVAL_FALSE = 8
52+
_EMVAL_FALSE = 8,
53+
_EMVAL_LAST_RESERVED_HANDLE = 8,
5354
};
5455

5556
typedef struct _EM_DESTRUCTORS* EM_DESTRUCTORS;
@@ -385,11 +386,13 @@ class val {
385386
}
386387

387388
val(const val& v) : val(v.as_handle()) {
388-
internal::_emval_incref(handle);
389+
if (uses_ref_count()) {
390+
internal::_emval_incref(handle);
391+
}
389392
}
390393

391394
~val() {
392-
if (handle) {
395+
if (uses_ref_count()) {
393396
internal::_emval_decref(as_handle());
394397
handle = 0;
395398
}
@@ -402,6 +405,13 @@ class val {
402405
return handle;
403406
}
404407

408+
// Takes ownership of the handle away from, and invalidates, this instance.
409+
EM_VAL release_ownership() {
410+
EM_VAL taken = as_handle();
411+
handle = 0;
412+
return taken;
413+
}
414+
405415
val& operator=(val&& v) & {
406416
val tmp(std::move(v));
407417
this->~val();
@@ -630,6 +640,12 @@ class val {
630640
: handle(handle), thread(pthread_self())
631641
{}
632642

643+
// Whether this value is a uses incref/decref (true) or is a special reserved
644+
// value (false).
645+
bool uses_ref_count() const {
646+
return handle > reinterpret_cast<EM_VAL>(internal::_EMVAL_LAST_RESERVED_HANDLE);
647+
}
648+
633649
template<typename WrapperType>
634650
friend val internal::wrapped_extend(const std::string& , const val& );
635651

@@ -786,9 +802,20 @@ template<typename T>
786802
struct BindingType<T, typename std::enable_if<std::is_base_of<val, T>::value &&
787803
!std::is_const<T>::value>::type> {
788804
typedef EM_VAL WireType;
805+
806+
// Marshall to JS with move semantics when we can invalidate the temporary val
807+
// object.
808+
static WireType toWireType(val&& v) {
809+
return v.release_ownership();
810+
}
811+
812+
// Marshal to JS with copy semantics when we cannot transfer the val objects
813+
// reference count.
789814
static WireType toWireType(const val& v) {
790815
EM_VAL handle = v.as_handle();
791-
_emval_incref(handle);
816+
if (v.uses_ref_count()) {
817+
_emval_incref(handle);
818+
}
792819
return handle;
793820
}
794821
static T fromWireType(WireType v) {

test/code_size/embind_val_wasm.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"a.html.gz": 431,
44
"a.js": 7080,
55
"a.js.gz": 2999,
6-
"a.wasm": 11428,
7-
"a.wasm.gz": 5724,
8-
"total": 19181,
9-
"total_gz": 9154
6+
"a.wasm": 11431,
7+
"a.wasm.gz": 5727,
8+
"total": 19184,
9+
"total_gz": 9157
1010
}

0 commit comments

Comments
 (0)