Skip to content

Commit e9b628c

Browse files
authored
Optimize val::array for numeric types (#17292)
The val::array(const std::vector<T>&) is implemented by pushing items one by one to val and is pretty slow for numeric types. A new library function is added, which accepts memory_view and then copy the typed array in JS side one-off. It's about 20x times faster.
1 parent e91ea21 commit e9b628c

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

src/embind/emval.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ var LibraryEmVal = {
127127
return Emval.toHandle([]);
128128
},
129129

130+
_emval_new_array_from_memory_view__sig: 'pp',
131+
_emval_new_array_from_memory_view__deps: ['$Emval'],
132+
_emval_new_array_from_memory_view: function(view) {
133+
view = Emval.toValue(view);
134+
// using for..loop is faster than Array.from
135+
var a = new Array(view.length);
136+
for (i = 0; i < view.length; i++) a[i] = view[i];
137+
return Emval.toHandle(a);
138+
},
139+
130140
_emval_new_object__sig: 'p',
131141
_emval_new_object__deps: ['$Emval'],
132142
_emval_new_object: function() {

system/include/emscripten/val.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ void _emval_decref(EM_VAL value);
5353
void _emval_run_destructors(EM_DESTRUCTORS handle);
5454

5555
EM_VAL _emval_new_array();
56+
EM_VAL _emval_new_array_from_memory_view(EM_VAL mv);
5657
EM_VAL _emval_new_object();
5758
EM_VAL _emval_new_cstring(const char*);
5859
EM_VAL _emval_new_u8string(const char*);
@@ -335,7 +336,13 @@ class val {
335336

336337
template<typename T>
337338
static val array(const std::vector<T>& vec) {
338-
return array(vec.begin(), vec.end());
339+
if constexpr (internal::typeSupportsMemoryView<T>()) {
340+
// for numeric types, pass memory view and copy in JS side one-off
341+
val view{ typed_memory_view(vec.size(), vec.data()) };
342+
return val(internal::_emval_new_array_from_memory_view(view.as_handle()));
343+
} else {
344+
return array(vec.begin(), vec.end());
345+
}
339346
}
340347

341348
static val object() {

tests/embind/embind_benchmark.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,33 @@ void __attribute__((noinline)) pass_gameobject_ptr_benchmark()
462462
printf("C++ pass_gameobject_ptr %d iters: %f msecs.\n", N, (t2-t));
463463
}
464464

465+
void __attribute__((noinline)) numeric_val_array_benchmark() {
466+
using emscripten::val;
467+
468+
std::vector<int> vec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
469+
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
470+
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
471+
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
472+
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63};
473+
474+
const int kLoopTimes = 100000;
475+
double t = emscripten_get_now();
476+
for (int i = 0; i < kLoopTimes; i++) {
477+
val v = val::array(vec.begin(), vec.end());
478+
}
479+
printf("val::array: %lf\n", emscripten_get_now() - t);
480+
481+
t = emscripten_get_now();
482+
for (int i = 0; i < kLoopTimes; i++) {
483+
val v = val::array(vec);
484+
}
485+
printf("val::array opt numeric types: %lf\n", emscripten_get_now() - t);
486+
487+
// It's about 20x times faster.
488+
// val::array: 1021.525756
489+
// val::array opt numeric types: 50.600682
490+
}
491+
465492
int EMSCRIPTEN_KEEPALIVE main()
466493
{
467494
for(int i = 1000; i <= 100000; i *= 10)
@@ -506,4 +533,5 @@ int EMSCRIPTEN_KEEPALIVE main()
506533
call_through_interface1();
507534
call_through_interface2();
508535
returns_val_benchmark();
536+
numeric_val_array_benchmark();
509537
}

0 commit comments

Comments
 (0)