Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/test/embind/embind_benchmark.cpp
4150 views
1
// Copyright 2013 The Emscripten Authors. All rights reserved.
2
// Emscripten is available under two separate licenses, the MIT license and the
3
// University of Illinois/NCSA Open Source License. Both these licenses can be
4
// found in the LICENSE file.
5
6
#include <stdio.h>
7
#include <emscripten.h>
8
#include <emscripten/bind.h>
9
#include <memory>
10
11
int counter = 0;
12
13
extern "C" {
14
15
int __attribute__((noinline)) EMSCRIPTEN_KEEPALIVE get_counter() {
16
return counter;
17
}
18
19
void __attribute__((noinline)) EMSCRIPTEN_KEEPALIVE increment_counter() {
20
++counter;
21
}
22
23
int __attribute__((noinline)) EMSCRIPTEN_KEEPALIVE sum_int(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, int v9) {
24
return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9;
25
}
26
27
float __attribute__((noinline)) EMSCRIPTEN_KEEPALIVE sum_float(float v1, float v2, float v3, float v4, float v5, float v6, float v7, float v8, float v9) {
28
return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9;
29
}
30
31
int __attribute__((noinline)) EMSCRIPTEN_KEEPALIVE returns_input(int i) {
32
return i;
33
}
34
35
extern void increment_counter_benchmark_js(int N);
36
extern void returns_input_benchmark_js();
37
extern void sum_int_benchmark_js();
38
extern void sum_float_benchmark_js();
39
40
extern void increment_counter_benchmark_embind_js(int N);
41
extern void returns_input_benchmark_embind_js();
42
extern void sum_int_benchmark_embind_js();
43
extern void sum_float_benchmark_embind_js();
44
45
extern void increment_class_counter_benchmark_embind_js(int N);
46
extern void move_gameobjects_benchmark_embind_js();
47
48
extern void pass_gameobject_ptr_benchmark();
49
extern void pass_gameobject_ptr_benchmark_embind_js();
50
51
extern void call_through_interface0();
52
extern void call_through_interface1();
53
extern void call_through_interface2();
54
55
extern void returns_val_benchmark();
56
}
57
58
emscripten::val returns_val(emscripten::val value) {
59
return emscripten::val(value.as<unsigned>() + 1);
60
}
61
62
class Vec3 {
63
public:
64
Vec3():x(0),y(0),z(0) {}
65
Vec3(float x_, float y_, float z_):x(x_),y(y_),z(z_) {}
66
float x,y,z;
67
};
68
69
Vec3 add(const Vec3 &lhs, const Vec3 &rhs) { return Vec3(lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.z); }
70
71
class Transform {
72
public:
73
Transform():scale(1) {}
74
75
Vec3 pos;
76
Vec3 rot;
77
float scale;
78
79
Vec3 __attribute__((noinline)) GetPosition() const { return pos; }
80
Vec3 __attribute__((noinline)) GetRotation() const { return rot; }
81
float __attribute__((noinline)) GetScale() const { return scale; }
82
83
void __attribute__((noinline)) SetPosition(const Vec3 &pos_) { pos = pos_; }
84
void __attribute__((noinline)) SetRotation(const Vec3 &rot_) { rot = rot_; }
85
void __attribute__((noinline)) SetScale(float scale_) { scale = scale_; }
86
};
87
typedef std::shared_ptr<Transform> TransformPtr;
88
89
class GameObject {
90
public:
91
GameObject() {
92
transform = std::make_shared<Transform>();
93
}
94
std::shared_ptr<Transform> transform;
95
96
TransformPtr __attribute__((noinline)) GetTransform() const { return transform; }
97
};
98
typedef std::shared_ptr<GameObject> GameObjectPtr;
99
100
GameObjectPtr create_game_object() {
101
return std::make_shared<GameObject>();
102
}
103
104
GameObjectPtr __attribute__((noinline)) pass_gameobject_ptr(GameObjectPtr p) {
105
return p;
106
}
107
108
class Foo {
109
public:
110
Foo() : class_counter(0) {}
111
112
void __attribute__((noinline)) incr_global_counter() {
113
++counter;
114
}
115
116
void __attribute__((noinline)) incr_class_counter() {
117
++class_counter;
118
}
119
120
int class_counter_val() const {
121
return class_counter;
122
}
123
124
int class_counter;
125
};
126
127
class Interface {
128
public:
129
virtual ~Interface() {}
130
virtual void call0() = 0;
131
virtual std::wstring call1(const std::wstring& str1, const std::wstring& str2) = 0;
132
virtual void call_with_typed_array(size_t size, const void*) = 0;
133
virtual void call_with_memory_view(size_t size, const unsigned int*) = 0;
134
};
135
136
EMSCRIPTEN_SYMBOL(HEAP8);
137
EMSCRIPTEN_SYMBOL(buffer);
138
139
EMSCRIPTEN_SYMBOL(call0);
140
EMSCRIPTEN_SYMBOL(call1);
141
EMSCRIPTEN_SYMBOL(call_with_typed_array);
142
EMSCRIPTEN_SYMBOL(call_with_memory_view);
143
EMSCRIPTEN_SYMBOL(Uint8Array);
144
145
class InterfaceWrapper : public emscripten::wrapper<Interface>
146
{
147
public:
148
EMSCRIPTEN_WRAPPER(InterfaceWrapper);
149
150
void call0() override {
151
return call<void>(call0_symbol);
152
}
153
154
std::wstring call1(const std::wstring& str1, const std::wstring& str2) override {
155
return call<std::wstring>(call1_symbol, str1, str2);
156
}
157
158
void call_with_typed_array(size_t size, const void* data) override {
159
return call<void>(
160
call_with_typed_array_symbol,
161
emscripten::val::global(Uint8Array_symbol).new_(
162
emscripten::val::module_property(HEAP8_symbol)[buffer_symbol],
163
reinterpret_cast<uintptr_t>(data),
164
size));
165
}
166
167
void call_with_memory_view(size_t size, const unsigned int* data) override {
168
return call<void>(
169
call_with_memory_view_symbol,
170
emscripten::typed_memory_view(size, data));
171
}
172
};
173
174
void callInterface0(unsigned N, Interface& o) {
175
for (unsigned i = 0; i < N; i += 8) {
176
o.call0();
177
o.call0();
178
o.call0();
179
o.call0();
180
o.call0();
181
o.call0();
182
o.call0();
183
o.call0();
184
}
185
}
186
187
void callInterface1(unsigned N, Interface& o) {
188
static std::wstring foo(L"foo");
189
static std::wstring bar(L"bar");
190
static std::wstring baz(L"baz");
191
static std::wstring qux(L"qux");
192
for (unsigned i = 0; i < N; i += 7) {
193
o.call1(
194
o.call1(
195
o.call1(foo, bar),
196
o.call1(baz, qux)),
197
o.call1(
198
o.call1(qux, foo),
199
o.call1(bar, baz)));
200
}
201
}
202
203
void callInterface2(unsigned N, Interface& o) {
204
int i = 0;
205
for (unsigned i = 0; i < N; i += 8) {
206
o.call_with_typed_array(sizeof(int), &i);
207
o.call_with_typed_array(sizeof(int), &i);
208
o.call_with_typed_array(sizeof(int), &i);
209
o.call_with_typed_array(sizeof(int), &i);
210
o.call_with_typed_array(sizeof(int), &i);
211
o.call_with_typed_array(sizeof(int), &i);
212
o.call_with_typed_array(sizeof(int), &i);
213
o.call_with_typed_array(sizeof(int), &i);
214
}
215
}
216
217
void callInterface3(unsigned N, Interface& o) {
218
for (unsigned i = 0; i < N; i += 8) {
219
o.call_with_memory_view(sizeof(int), &i);
220
o.call_with_memory_view(sizeof(int), &i);
221
o.call_with_memory_view(sizeof(int), &i);
222
o.call_with_memory_view(sizeof(int), &i);
223
o.call_with_memory_view(sizeof(int), &i);
224
o.call_with_memory_view(sizeof(int), &i);
225
o.call_with_memory_view(sizeof(int), &i);
226
o.call_with_memory_view(sizeof(int), &i);
227
}
228
}
229
230
EMSCRIPTEN_BINDINGS(benchmark) {
231
using namespace emscripten;
232
233
class_<GameObject>("GameObject")
234
.smart_ptr<GameObjectPtr>("GameObjectPtr")
235
.function("GetTransform", &GameObject::GetTransform);
236
237
class_<Transform>("Transform")
238
.smart_ptr<TransformPtr>("TransformPtr")
239
.function("GetPosition", &Transform::GetPosition)
240
.function("GetRotation", &Transform::GetRotation)
241
.function("GetScale", &Transform::GetScale)
242
.function("SetPosition", &Transform::SetPosition)
243
.function("SetRotation", &Transform::SetRotation)
244
.function("SetScale", &Transform::SetScale);
245
246
value_array<Vec3>("Vec3")
247
.element(&Vec3::x)
248
.element(&Vec3::y)
249
.element(&Vec3::z);
250
251
function("create_game_object", &create_game_object);
252
function("pass_gameobject_ptr", &pass_gameobject_ptr);
253
function("add", &add);
254
255
function("get_counter", &get_counter);
256
function("increment_counter", &increment_counter);
257
function("returns_input", &returns_input);
258
function("sum_int", &sum_int);
259
function("sum_float", &sum_float);
260
261
class_<Foo>("Foo")
262
.constructor<>()
263
.function("incr_global_counter", &Foo::incr_global_counter)
264
.function("incr_class_counter", &Foo::incr_class_counter)
265
.function("class_counter_val", &Foo::class_counter_val)
266
;
267
268
class_<Interface>("Interface")
269
.allow_subclass<InterfaceWrapper>("InterfaceWrapper")
270
;
271
272
function("callInterface0", &callInterface0);
273
function("callInterface1", &callInterface1);
274
function("callInterface2", &callInterface2);
275
function("callInterface3", &callInterface3);
276
277
function("returns_val", &returns_val);
278
}
279
280
void __attribute__((noinline)) emscripten_get_now_benchmark(int N) {
281
volatile float t = emscripten_get_now();
282
for (int i = 0; i < N; ++i) {
283
emscripten_get_now();
284
emscripten_get_now();
285
emscripten_get_now();
286
emscripten_get_now();
287
emscripten_get_now();
288
emscripten_get_now();
289
emscripten_get_now();
290
emscripten_get_now();
291
emscripten_get_now();
292
emscripten_get_now();
293
}
294
volatile float t2 = emscripten_get_now();
295
printf("C++ emscripten_get_now %d iters: %f msecs.\n", N, (t2-t));
296
}
297
298
void __attribute__((noinline)) increment_counter_benchmark(int N) {
299
volatile float t = emscripten_get_now();
300
for ( int i = 0; i < N; ++i) {
301
increment_counter();
302
increment_counter();
303
increment_counter();
304
increment_counter();
305
increment_counter();
306
increment_counter();
307
increment_counter();
308
increment_counter();
309
increment_counter();
310
increment_counter();
311
}
312
volatile float t2 = emscripten_get_now();
313
printf("C++ increment_counter %d iters: %f msecs.\n", N, (t2-t));
314
}
315
316
void __attribute__((noinline)) increment_class_counter_benchmark(int N) {
317
Foo foo;
318
volatile float t = emscripten_get_now();
319
for (int i = 0; i < N; ++i) {
320
foo.incr_class_counter();
321
foo.incr_class_counter();
322
foo.incr_class_counter();
323
foo.incr_class_counter();
324
foo.incr_class_counter();
325
foo.incr_class_counter();
326
foo.incr_class_counter();
327
foo.incr_class_counter();
328
foo.incr_class_counter();
329
foo.incr_class_counter();
330
}
331
volatile float t2 = emscripten_get_now();
332
printf("C++ increment_class_counter %d iters: %f msecs. result: %d\n", N, (t2-t), foo.class_counter);
333
}
334
335
void __attribute__((noinline)) returns_input_benchmark() {
336
volatile int r = 0;
337
volatile float t = emscripten_get_now();
338
for (int i = 0; i < 100000; ++i) {
339
r += returns_input(i);
340
r += returns_input(i);
341
r += returns_input(i);
342
r += returns_input(i);
343
r += returns_input(i);
344
r += returns_input(i);
345
r += returns_input(i);
346
r += returns_input(i);
347
r += returns_input(i);
348
r += returns_input(i);
349
}
350
volatile float t2 = emscripten_get_now();
351
printf("C++ returns_input 100000 iters: %f msecs.\n", (t2-t));
352
}
353
354
void __attribute__((noinline)) sum_int_benchmark() {
355
volatile float t = emscripten_get_now();
356
volatile int r = 0;
357
for (int i = 0; i < 100000; ++i) {
358
r += sum_int(i,2,3,4,5,6,7,8,9);
359
r += sum_int(i,2,3,4,5,6,7,8,9);
360
r += sum_int(i,2,3,4,5,6,7,8,9);
361
r += sum_int(i,2,3,4,5,6,7,8,9);
362
r += sum_int(i,2,3,4,5,6,7,8,9);
363
r += sum_int(i,2,3,4,5,6,7,8,9);
364
r += sum_int(i,2,3,4,5,6,7,8,9);
365
r += sum_int(i,2,3,4,5,6,7,8,9);
366
r += sum_int(i,2,3,4,5,6,7,8,9);
367
r += sum_int(i,2,3,4,5,6,7,8,9);
368
}
369
volatile float t2 = emscripten_get_now();
370
printf("C++ sum_int 100000 iters: %f msecs.\n", (t2-t));
371
}
372
373
void __attribute__((noinline)) sum_float_benchmark() {
374
volatile float f = 0.f;
375
volatile float t = emscripten_get_now();
376
for (int i = 0; i < 100000; ++i) {
377
f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
378
f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
379
f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
380
f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
381
f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
382
f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
383
f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
384
f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
385
f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
386
f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
387
}
388
volatile float t2 = emscripten_get_now();
389
printf("C++ sum_float 100000 iters: %f msecs.\n", (t2-t));
390
}
391
392
void __attribute__((noinline)) move_gameobjects_benchmark()
393
{
394
const int N = 10000;
395
GameObjectPtr objects[N];
396
for (int i = 0; i < N; ++i) {
397
objects[i] = create_game_object();
398
}
399
400
volatile float t = emscripten_get_now();
401
for (int i = 0; i < N; ++i) {
402
TransformPtr t = objects[i]->GetTransform();
403
Vec3 pos = add(t->GetPosition(), Vec3(2.f, 0.f, 1.f));
404
Vec3 rot = add(t->GetRotation(), Vec3(0.1f, 0.2f, 0.3f));
405
t->SetPosition(pos);
406
t->SetRotation(rot);
407
}
408
volatile float t2 = emscripten_get_now();
409
410
Vec3 accum;
411
for (int i = 0; i < N; ++i) {
412
accum = add(add(accum, objects[i]->GetTransform()->GetPosition()), objects[i]->GetTransform()->GetRotation());
413
}
414
printf("C++ move_gameobjects %d iters: %f msecs. Result: %f\n", N, (t2-t), accum.x+accum.y+accum.z);
415
}
416
417
void __attribute__((noinline)) pass_gameobject_ptr_benchmark()
418
{
419
const int N = 100000;
420
GameObjectPtr objects[N];
421
for (int i = 0; i < N; ++i) {
422
objects[i] = create_game_object();
423
}
424
425
volatile float t = emscripten_get_now();
426
for (int i = 0; i < N; ++i) {
427
objects[i] = pass_gameobject_ptr(objects[i]);
428
}
429
volatile float t2 = emscripten_get_now();
430
431
printf("C++ pass_gameobject_ptr %d iters: %f msecs.\n", N, (t2-t));
432
}
433
434
void __attribute__((noinline)) numeric_val_array_benchmark() {
435
using emscripten::val;
436
437
std::vector<int> vec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
438
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
439
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
440
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
441
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63};
442
443
const int kLoopTimes = 100000;
444
double t = emscripten_get_now();
445
for (int i = 0; i < kLoopTimes; i++) {
446
val v = val::array(vec.begin(), vec.end());
447
}
448
printf("\nval::array(Iter begin, Iter end): %lf msecs.\n", emscripten_get_now() - t);
449
450
t = emscripten_get_now();
451
for (int i = 0; i < kLoopTimes; i++) {
452
val v = val::array(vec);
453
}
454
printf("val::array(const std::vector<T>& vec) with opt numeric types: %lf msecs.\n", emscripten_get_now() - t);
455
456
// It's about 20x times faster.
457
// val::array(Iter begin, Iter end): 727.300000 msecs.
458
// val::array(const std::vector<T>& vec) with opt numeric types: 29.700000 msecs.
459
460
// If compile with `--std=c++20`, the result is very close.
461
// val::array(Iter begin, Iter end): 30.400000 msecs.
462
// val::array(const std::vector<T>& vec) with opt numeric types: 27.500000 msecs.
463
}
464
465
int main() {
466
for (int i = 1000; i <= 100000; i *= 10) {
467
emscripten_get_now_benchmark(i);
468
}
469
470
printf("\n");
471
for (int i = 1000; i <= 100000; i *= 10) {
472
increment_counter_benchmark(i);
473
increment_counter_benchmark_js(i);
474
increment_counter_benchmark_embind_js(i);
475
printf("\n");
476
}
477
478
for (int i = 1000; i <= 100000; i *= 10) {
479
increment_class_counter_benchmark(i);
480
increment_class_counter_benchmark_embind_js(i);
481
printf("\n");
482
}
483
484
returns_input_benchmark();
485
returns_input_benchmark_js();
486
returns_input_benchmark_embind_js();
487
printf("\n");
488
sum_int_benchmark();
489
sum_int_benchmark_js();
490
sum_int_benchmark_embind_js();
491
printf("\n");
492
sum_float_benchmark();
493
sum_float_benchmark_js();
494
sum_float_benchmark_embind_js();
495
printf("\n");
496
move_gameobjects_benchmark();
497
move_gameobjects_benchmark_embind_js();
498
printf("\n");
499
pass_gameobject_ptr_benchmark();
500
pass_gameobject_ptr_benchmark_embind_js();
501
502
emscripten_get_now();
503
call_through_interface0();
504
call_through_interface1();
505
call_through_interface2();
506
returns_val_benchmark();
507
numeric_val_array_benchmark();
508
}
509
510