Path: blob/main/test/embind/embind_benchmark.cpp
4150 views
// Copyright 2013 The Emscripten Authors. All rights reserved.1// Emscripten is available under two separate licenses, the MIT license and the2// University of Illinois/NCSA Open Source License. Both these licenses can be3// found in the LICENSE file.45#include <stdio.h>6#include <emscripten.h>7#include <emscripten/bind.h>8#include <memory>910int counter = 0;1112extern "C" {1314int __attribute__((noinline)) EMSCRIPTEN_KEEPALIVE get_counter() {15return counter;16}1718void __attribute__((noinline)) EMSCRIPTEN_KEEPALIVE increment_counter() {19++counter;20}2122int __attribute__((noinline)) EMSCRIPTEN_KEEPALIVE sum_int(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, int v9) {23return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9;24}2526float __attribute__((noinline)) EMSCRIPTEN_KEEPALIVE sum_float(float v1, float v2, float v3, float v4, float v5, float v6, float v7, float v8, float v9) {27return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9;28}2930int __attribute__((noinline)) EMSCRIPTEN_KEEPALIVE returns_input(int i) {31return i;32}3334extern void increment_counter_benchmark_js(int N);35extern void returns_input_benchmark_js();36extern void sum_int_benchmark_js();37extern void sum_float_benchmark_js();3839extern void increment_counter_benchmark_embind_js(int N);40extern void returns_input_benchmark_embind_js();41extern void sum_int_benchmark_embind_js();42extern void sum_float_benchmark_embind_js();4344extern void increment_class_counter_benchmark_embind_js(int N);45extern void move_gameobjects_benchmark_embind_js();4647extern void pass_gameobject_ptr_benchmark();48extern void pass_gameobject_ptr_benchmark_embind_js();4950extern void call_through_interface0();51extern void call_through_interface1();52extern void call_through_interface2();5354extern void returns_val_benchmark();55}5657emscripten::val returns_val(emscripten::val value) {58return emscripten::val(value.as<unsigned>() + 1);59}6061class Vec3 {62public:63Vec3():x(0),y(0),z(0) {}64Vec3(float x_, float y_, float z_):x(x_),y(y_),z(z_) {}65float x,y,z;66};6768Vec3 add(const Vec3 &lhs, const Vec3 &rhs) { return Vec3(lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.z); }6970class Transform {71public:72Transform():scale(1) {}7374Vec3 pos;75Vec3 rot;76float scale;7778Vec3 __attribute__((noinline)) GetPosition() const { return pos; }79Vec3 __attribute__((noinline)) GetRotation() const { return rot; }80float __attribute__((noinline)) GetScale() const { return scale; }8182void __attribute__((noinline)) SetPosition(const Vec3 &pos_) { pos = pos_; }83void __attribute__((noinline)) SetRotation(const Vec3 &rot_) { rot = rot_; }84void __attribute__((noinline)) SetScale(float scale_) { scale = scale_; }85};86typedef std::shared_ptr<Transform> TransformPtr;8788class GameObject {89public:90GameObject() {91transform = std::make_shared<Transform>();92}93std::shared_ptr<Transform> transform;9495TransformPtr __attribute__((noinline)) GetTransform() const { return transform; }96};97typedef std::shared_ptr<GameObject> GameObjectPtr;9899GameObjectPtr create_game_object() {100return std::make_shared<GameObject>();101}102103GameObjectPtr __attribute__((noinline)) pass_gameobject_ptr(GameObjectPtr p) {104return p;105}106107class Foo {108public:109Foo() : class_counter(0) {}110111void __attribute__((noinline)) incr_global_counter() {112++counter;113}114115void __attribute__((noinline)) incr_class_counter() {116++class_counter;117}118119int class_counter_val() const {120return class_counter;121}122123int class_counter;124};125126class Interface {127public:128virtual ~Interface() {}129virtual void call0() = 0;130virtual std::wstring call1(const std::wstring& str1, const std::wstring& str2) = 0;131virtual void call_with_typed_array(size_t size, const void*) = 0;132virtual void call_with_memory_view(size_t size, const unsigned int*) = 0;133};134135EMSCRIPTEN_SYMBOL(HEAP8);136EMSCRIPTEN_SYMBOL(buffer);137138EMSCRIPTEN_SYMBOL(call0);139EMSCRIPTEN_SYMBOL(call1);140EMSCRIPTEN_SYMBOL(call_with_typed_array);141EMSCRIPTEN_SYMBOL(call_with_memory_view);142EMSCRIPTEN_SYMBOL(Uint8Array);143144class InterfaceWrapper : public emscripten::wrapper<Interface>145{146public:147EMSCRIPTEN_WRAPPER(InterfaceWrapper);148149void call0() override {150return call<void>(call0_symbol);151}152153std::wstring call1(const std::wstring& str1, const std::wstring& str2) override {154return call<std::wstring>(call1_symbol, str1, str2);155}156157void call_with_typed_array(size_t size, const void* data) override {158return call<void>(159call_with_typed_array_symbol,160emscripten::val::global(Uint8Array_symbol).new_(161emscripten::val::module_property(HEAP8_symbol)[buffer_symbol],162reinterpret_cast<uintptr_t>(data),163size));164}165166void call_with_memory_view(size_t size, const unsigned int* data) override {167return call<void>(168call_with_memory_view_symbol,169emscripten::typed_memory_view(size, data));170}171};172173void callInterface0(unsigned N, Interface& o) {174for (unsigned i = 0; i < N; i += 8) {175o.call0();176o.call0();177o.call0();178o.call0();179o.call0();180o.call0();181o.call0();182o.call0();183}184}185186void callInterface1(unsigned N, Interface& o) {187static std::wstring foo(L"foo");188static std::wstring bar(L"bar");189static std::wstring baz(L"baz");190static std::wstring qux(L"qux");191for (unsigned i = 0; i < N; i += 7) {192o.call1(193o.call1(194o.call1(foo, bar),195o.call1(baz, qux)),196o.call1(197o.call1(qux, foo),198o.call1(bar, baz)));199}200}201202void callInterface2(unsigned N, Interface& o) {203int i = 0;204for (unsigned i = 0; i < N; i += 8) {205o.call_with_typed_array(sizeof(int), &i);206o.call_with_typed_array(sizeof(int), &i);207o.call_with_typed_array(sizeof(int), &i);208o.call_with_typed_array(sizeof(int), &i);209o.call_with_typed_array(sizeof(int), &i);210o.call_with_typed_array(sizeof(int), &i);211o.call_with_typed_array(sizeof(int), &i);212o.call_with_typed_array(sizeof(int), &i);213}214}215216void callInterface3(unsigned N, Interface& o) {217for (unsigned i = 0; i < N; i += 8) {218o.call_with_memory_view(sizeof(int), &i);219o.call_with_memory_view(sizeof(int), &i);220o.call_with_memory_view(sizeof(int), &i);221o.call_with_memory_view(sizeof(int), &i);222o.call_with_memory_view(sizeof(int), &i);223o.call_with_memory_view(sizeof(int), &i);224o.call_with_memory_view(sizeof(int), &i);225o.call_with_memory_view(sizeof(int), &i);226}227}228229EMSCRIPTEN_BINDINGS(benchmark) {230using namespace emscripten;231232class_<GameObject>("GameObject")233.smart_ptr<GameObjectPtr>("GameObjectPtr")234.function("GetTransform", &GameObject::GetTransform);235236class_<Transform>("Transform")237.smart_ptr<TransformPtr>("TransformPtr")238.function("GetPosition", &Transform::GetPosition)239.function("GetRotation", &Transform::GetRotation)240.function("GetScale", &Transform::GetScale)241.function("SetPosition", &Transform::SetPosition)242.function("SetRotation", &Transform::SetRotation)243.function("SetScale", &Transform::SetScale);244245value_array<Vec3>("Vec3")246.element(&Vec3::x)247.element(&Vec3::y)248.element(&Vec3::z);249250function("create_game_object", &create_game_object);251function("pass_gameobject_ptr", &pass_gameobject_ptr);252function("add", &add);253254function("get_counter", &get_counter);255function("increment_counter", &increment_counter);256function("returns_input", &returns_input);257function("sum_int", &sum_int);258function("sum_float", &sum_float);259260class_<Foo>("Foo")261.constructor<>()262.function("incr_global_counter", &Foo::incr_global_counter)263.function("incr_class_counter", &Foo::incr_class_counter)264.function("class_counter_val", &Foo::class_counter_val)265;266267class_<Interface>("Interface")268.allow_subclass<InterfaceWrapper>("InterfaceWrapper")269;270271function("callInterface0", &callInterface0);272function("callInterface1", &callInterface1);273function("callInterface2", &callInterface2);274function("callInterface3", &callInterface3);275276function("returns_val", &returns_val);277}278279void __attribute__((noinline)) emscripten_get_now_benchmark(int N) {280volatile float t = emscripten_get_now();281for (int i = 0; i < N; ++i) {282emscripten_get_now();283emscripten_get_now();284emscripten_get_now();285emscripten_get_now();286emscripten_get_now();287emscripten_get_now();288emscripten_get_now();289emscripten_get_now();290emscripten_get_now();291emscripten_get_now();292}293volatile float t2 = emscripten_get_now();294printf("C++ emscripten_get_now %d iters: %f msecs.\n", N, (t2-t));295}296297void __attribute__((noinline)) increment_counter_benchmark(int N) {298volatile float t = emscripten_get_now();299for ( int i = 0; i < N; ++i) {300increment_counter();301increment_counter();302increment_counter();303increment_counter();304increment_counter();305increment_counter();306increment_counter();307increment_counter();308increment_counter();309increment_counter();310}311volatile float t2 = emscripten_get_now();312printf("C++ increment_counter %d iters: %f msecs.\n", N, (t2-t));313}314315void __attribute__((noinline)) increment_class_counter_benchmark(int N) {316Foo foo;317volatile float t = emscripten_get_now();318for (int i = 0; i < N; ++i) {319foo.incr_class_counter();320foo.incr_class_counter();321foo.incr_class_counter();322foo.incr_class_counter();323foo.incr_class_counter();324foo.incr_class_counter();325foo.incr_class_counter();326foo.incr_class_counter();327foo.incr_class_counter();328foo.incr_class_counter();329}330volatile float t2 = emscripten_get_now();331printf("C++ increment_class_counter %d iters: %f msecs. result: %d\n", N, (t2-t), foo.class_counter);332}333334void __attribute__((noinline)) returns_input_benchmark() {335volatile int r = 0;336volatile float t = emscripten_get_now();337for (int i = 0; i < 100000; ++i) {338r += returns_input(i);339r += returns_input(i);340r += returns_input(i);341r += returns_input(i);342r += returns_input(i);343r += returns_input(i);344r += returns_input(i);345r += returns_input(i);346r += returns_input(i);347r += returns_input(i);348}349volatile float t2 = emscripten_get_now();350printf("C++ returns_input 100000 iters: %f msecs.\n", (t2-t));351}352353void __attribute__((noinline)) sum_int_benchmark() {354volatile float t = emscripten_get_now();355volatile int r = 0;356for (int i = 0; i < 100000; ++i) {357r += sum_int(i,2,3,4,5,6,7,8,9);358r += sum_int(i,2,3,4,5,6,7,8,9);359r += sum_int(i,2,3,4,5,6,7,8,9);360r += sum_int(i,2,3,4,5,6,7,8,9);361r += sum_int(i,2,3,4,5,6,7,8,9);362r += sum_int(i,2,3,4,5,6,7,8,9);363r += sum_int(i,2,3,4,5,6,7,8,9);364r += sum_int(i,2,3,4,5,6,7,8,9);365r += sum_int(i,2,3,4,5,6,7,8,9);366r += sum_int(i,2,3,4,5,6,7,8,9);367}368volatile float t2 = emscripten_get_now();369printf("C++ sum_int 100000 iters: %f msecs.\n", (t2-t));370}371372void __attribute__((noinline)) sum_float_benchmark() {373volatile float f = 0.f;374volatile float t = emscripten_get_now();375for (int i = 0; i < 100000; ++i) {376f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);377f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);378f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);379f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);380f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);381f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);382f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);383f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);384f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);385f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);386}387volatile float t2 = emscripten_get_now();388printf("C++ sum_float 100000 iters: %f msecs.\n", (t2-t));389}390391void __attribute__((noinline)) move_gameobjects_benchmark()392{393const int N = 10000;394GameObjectPtr objects[N];395for (int i = 0; i < N; ++i) {396objects[i] = create_game_object();397}398399volatile float t = emscripten_get_now();400for (int i = 0; i < N; ++i) {401TransformPtr t = objects[i]->GetTransform();402Vec3 pos = add(t->GetPosition(), Vec3(2.f, 0.f, 1.f));403Vec3 rot = add(t->GetRotation(), Vec3(0.1f, 0.2f, 0.3f));404t->SetPosition(pos);405t->SetRotation(rot);406}407volatile float t2 = emscripten_get_now();408409Vec3 accum;410for (int i = 0; i < N; ++i) {411accum = add(add(accum, objects[i]->GetTransform()->GetPosition()), objects[i]->GetTransform()->GetRotation());412}413printf("C++ move_gameobjects %d iters: %f msecs. Result: %f\n", N, (t2-t), accum.x+accum.y+accum.z);414}415416void __attribute__((noinline)) pass_gameobject_ptr_benchmark()417{418const int N = 100000;419GameObjectPtr objects[N];420for (int i = 0; i < N; ++i) {421objects[i] = create_game_object();422}423424volatile float t = emscripten_get_now();425for (int i = 0; i < N; ++i) {426objects[i] = pass_gameobject_ptr(objects[i]);427}428volatile float t2 = emscripten_get_now();429430printf("C++ pass_gameobject_ptr %d iters: %f msecs.\n", N, (t2-t));431}432433void __attribute__((noinline)) numeric_val_array_benchmark() {434using emscripten::val;435436std::vector<int> vec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,43713, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,43826, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,43939, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,44052, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63};441442const int kLoopTimes = 100000;443double t = emscripten_get_now();444for (int i = 0; i < kLoopTimes; i++) {445val v = val::array(vec.begin(), vec.end());446}447printf("\nval::array(Iter begin, Iter end): %lf msecs.\n", emscripten_get_now() - t);448449t = emscripten_get_now();450for (int i = 0; i < kLoopTimes; i++) {451val v = val::array(vec);452}453printf("val::array(const std::vector<T>& vec) with opt numeric types: %lf msecs.\n", emscripten_get_now() - t);454455// It's about 20x times faster.456// val::array(Iter begin, Iter end): 727.300000 msecs.457// val::array(const std::vector<T>& vec) with opt numeric types: 29.700000 msecs.458459// If compile with `--std=c++20`, the result is very close.460// val::array(Iter begin, Iter end): 30.400000 msecs.461// val::array(const std::vector<T>& vec) with opt numeric types: 27.500000 msecs.462}463464int main() {465for (int i = 1000; i <= 100000; i *= 10) {466emscripten_get_now_benchmark(i);467}468469printf("\n");470for (int i = 1000; i <= 100000; i *= 10) {471increment_counter_benchmark(i);472increment_counter_benchmark_js(i);473increment_counter_benchmark_embind_js(i);474printf("\n");475}476477for (int i = 1000; i <= 100000; i *= 10) {478increment_class_counter_benchmark(i);479increment_class_counter_benchmark_embind_js(i);480printf("\n");481}482483returns_input_benchmark();484returns_input_benchmark_js();485returns_input_benchmark_embind_js();486printf("\n");487sum_int_benchmark();488sum_int_benchmark_js();489sum_int_benchmark_embind_js();490printf("\n");491sum_float_benchmark();492sum_float_benchmark_js();493sum_float_benchmark_embind_js();494printf("\n");495move_gameobjects_benchmark();496move_gameobjects_benchmark_embind_js();497printf("\n");498pass_gameobject_ptr_benchmark();499pass_gameobject_ptr_benchmark_embind_js();500501emscripten_get_now();502call_through_interface0();503call_through_interface1();504call_through_interface2();505returns_val_benchmark();506numeric_val_array_benchmark();507}508509510