// https://github.com/kunitoki/LuaBridge3 // Copyright 2026, kunitoki // Inspired from https://github.com/ThePhD/lua-bindings-shootout by ThePhD // SPDX-License-Identifier: MIT #include "benchmark_common.hpp" #include #include #include namespace { using namespace lbsbench; std::tuple sol3_multi_return(double value) { return { value, value * 2.0 }; } void registerBasic(sol::state& lua) { lua.new_usertype("c", sol::constructors(), "set", &Basic::set, "get", &Basic::get, "var", &Basic::var); } void registerBasicLarge(sol::state& lua) { lua.new_usertype("cl", sol::constructors(), "var", &BasicLarge::var, "var0", &BasicLarge::var0, "var1", &BasicLarge::var1, "var2", &BasicLarge::var2, "var3", &BasicLarge::var3, "var4", &BasicLarge::var4, "var5", &BasicLarge::var5, "var6", &BasicLarge::var6, "var7", &BasicLarge::var7, "var8", &BasicLarge::var8, "var9", &BasicLarge::var9, "var10", &BasicLarge::var10, "var11", &BasicLarge::var11, "var12", &BasicLarge::var12, "var13", &BasicLarge::var13, "var14", &BasicLarge::var14, "var15", &BasicLarge::var15, "var16", &BasicLarge::var16, "var17", &BasicLarge::var17, "var18", &BasicLarge::var18, "var19", &BasicLarge::var19, "var20", &BasicLarge::var20, "var21", &BasicLarge::var21, "var22", &BasicLarge::var22, "var23", &BasicLarge::var23, "var24", &BasicLarge::var24, "var25", &BasicLarge::var25, "var26", &BasicLarge::var26, "var27", &BasicLarge::var27, "var28", &BasicLarge::var28, "var29", &BasicLarge::var29, "var30", &BasicLarge::var30, "var31", &BasicLarge::var31, "var32", &BasicLarge::var32, "var33", &BasicLarge::var33, "var34", &BasicLarge::var34, "var35", &BasicLarge::var35, "var36", &BasicLarge::var36, "var37", &BasicLarge::var37, "var38", &BasicLarge::var38, "var39", &BasicLarge::var39, "var40", &BasicLarge::var40, "var41", &BasicLarge::var41, "var42", &BasicLarge::var42, "var43", &BasicLarge::var43, "var44", &BasicLarge::var44, "var45", &BasicLarge::var45, "var46", &BasicLarge::var46, "var47", &BasicLarge::var47, "var48", &BasicLarge::var48, "var49", &BasicLarge::var49); } void registerBasicGetterSetter(sol::state& lua) { lua.new_usertype("c", sol::constructors(), "val", sol::property(&Basic::get, &Basic::set)); } void registerCounter(sol::state& lua) { lua.new_usertype("Counter", sol::constructors(), "get", &Counter::get, "static_add", &Counter::static_add); } void registerSharedObject(sol::state& lua) { lua.new_usertype("SharedObject", sol::call_constructor, sol::factories([]() { return std::make_shared(); }), "get", &SharedObject::get); lua.set_function("get_shared", &shared_object_return); lua.set_function("use_shared", &shared_object_get_value); } void table_global_string_get_measure(benchmark::State& state) { sol::state lua; lua["value"] = kMagicValue; double x = 0; for (auto _ : state) { (void) _; x += lua["value"].get(); } benchmark::DoNotOptimize(x); } void table_global_string_set_measure(benchmark::State& state) { sol::state lua; double v = 0; for (auto _ : state) { (void) _; v += kMagicValue; lua["value"] = v; } benchmark::DoNotOptimize(v); } void table_get_measure(benchmark::State& state) { sol::state lua; lua.script("warble = { value = 24.0 }"); sol::table t = lua["warble"]; double x = 0; for (auto _ : state) { (void) _; x += t["value"].get(); } benchmark::DoNotOptimize(x); } void table_set_measure(benchmark::State& state) { sol::state lua; lua.script("warble = { value = 24.0 }"); sol::table t = lua["warble"]; double v = 0; for (auto _ : state) { (void) _; v += kMagicValue; t["value"] = v; } benchmark::DoNotOptimize(v); } void table_chained_get_measure(benchmark::State& state) { sol::state lua; lua.script("ulahibe = { warble = { value = 24.0 } }"); double x = 0; for (auto _ : state) { (void) _; x += lua["ulahibe"]["warble"]["value"].get(); } benchmark::DoNotOptimize(x); } void table_chained_set_measure(benchmark::State& state) { sol::state lua; lua.script("ulahibe = { warble = { value = 24.0 } }"); double v = 0; for (auto _ : state) { (void) _; v += kMagicValue; lua["ulahibe"]["warble"]["value"] = v; } benchmark::DoNotOptimize(v); } void c_function_measure(benchmark::State& state) { sol::state lua; lua.set_function("f", +[](double value) { return value; }); lua.script("function invoke_f() return f(24.0) end"); for (auto _ : state) { (void) _; lua["invoke_f"](); } } void lua_function_in_c_measure(benchmark::State& state) { sol::state lua; lua.script("function f(i) return i end"); sol::function f = lua["f"]; double x = 0; for (auto _ : state) { (void) _; x += f.call(kMagicValue); } benchmark::DoNotOptimize(x); } void c_function_through_lua_in_c_measure(benchmark::State& state) { sol::state lua; lua.set_function("f", +[](double value) { return value; }); sol::function f = lua["f"]; double x = 0; for (auto _ : state) { (void) _; x += f.call(kMagicValue); } benchmark::DoNotOptimize(x); } void member_function_call_measure(benchmark::State& state) { sol::state lua; registerBasic(lua); lua.script("b = c.new()\nfunction call_member() b:set(b:get() + 1.0) end"); for (auto _ : state) { (void) _; lua["call_member"](); } } void userdata_variable_access_measure(benchmark::State& state) { sol::state lua; registerBasic(lua); lua.script("b = c.new()\nfunction access_var() b.var = b.var + 1.0 return b.var end"); for (auto _ : state) { (void) _; lua["access_var"](); } } void userdata_variable_access_large_measure(benchmark::State& state) { sol::state lua; registerBasicLarge(lua); lua.script("b = cl.new()\nfunction access_var_large() b.var0 = b.var0 + 1 return b.var0 end"); for (auto _ : state) { (void) _; lua["access_var_large"](); } } void userdata_variable_access_last_measure(benchmark::State& state) { sol::state lua; registerBasicLarge(lua); lua.script("b = cl.new()\nfunction access_var_last() b.var49 = b.var49 + 1 return b.var49 end"); for (auto _ : state) { (void) _; lua["access_var_last"](); } } void stateful_function_object_measure(benchmark::State& state) { sol::state lua; lua.set_function("f", StatefulFunction{}); sol::function f = lua["f"]; double x = 0; for (auto _ : state) { (void) _; x += f.call(kMagicValue); } benchmark::DoNotOptimize(x); } void multi_return_lua_measure(benchmark::State& state) { sol::state lua; lua.set_function("f", &sol3_multi_return); lua.script("function invoke_multi() local a,b=f(24.0) return a+b end"); for (auto _ : state) { (void) _; lua["invoke_multi"](); } } void multi_return_measure(benchmark::State& state) { sol::state lua; lua.set_function("f", &sol3_multi_return); sol::function f = lua["f"]; double x = 0; for (auto _ : state) { (void) _; std::tuple values = f.call(kMagicValue); x += std::get<0>(values); x += std::get<1>(values); } benchmark::DoNotOptimize(x); } void derived_base_measure(benchmark::State& state) { sol::state lua; lua.new_usertype("ComplexBaseA", "a_func", &ComplexBaseA::a_func, "a", &ComplexBaseA::a); lua.new_usertype("ComplexBaseB", "b_func", &ComplexBaseB::b_func, "b", &ComplexBaseB::b); lua.new_usertype("ComplexAB", sol::base_classes, sol::bases(), "ab_func", &ComplexAB::ab_func, "ab", &ComplexAB::ab); ComplexAB ab; lua["b"] = &ab; lua.script("function call_base() return b:a_func() + b:b_func() end"); for (auto _ : state) { (void) _; lua["call_base"](); } } void optional_success_measure(benchmark::State& state) { sol::state lua; lua.script("warble = { value = 24.0 }"); double x = 0; for (auto _ : state) { (void) _; sol::optional value = lua["warble"]["value"]; x += value.value_or(1.0); } benchmark::DoNotOptimize(x); } void optional_half_failure_measure(benchmark::State& state) { sol::state lua; lua.script("warble = { value = 'x' }"); double x = 0; for (auto _ : state) { (void) _; sol::optional value = lua["warble"]["value"]; x += value.value_or(1.0); } benchmark::DoNotOptimize(x); } void optional_failure_measure(benchmark::State& state) { sol::state lua; double x = 0; for (auto _ : state) { (void) _; sol::optional value = lua["warble"]["value"]; x += value.value_or(1.0); } benchmark::DoNotOptimize(x); } void return_userdata_measure(benchmark::State& state) { sol::state lua; registerBasic(lua); lua.set_function("f", &basic_return); lua.set_function("h", &basic_get_var); lua.script("function invoke_userdata() return h(f()) end"); for (auto _ : state) { (void) _; lua["invoke_userdata"](); } } void userdata_variable_write_measure(benchmark::State& state) { sol::state lua; registerBasic(lua); lua.script("b = c.new()\nfunction write_var() b.var = 24.0 end"); for (auto _ : state) { (void) _; lua["write_var"](); } } void userdata_property_getter_measure(benchmark::State& state) { sol::state lua; registerBasicGetterSetter(lua); lua.script("b = c.new()\nfunction read_getter() return b.val end"); for (auto _ : state) { (void) _; lua["read_getter"](); } } void userdata_property_setter_measure(benchmark::State& state) { sol::state lua; registerBasicGetterSetter(lua); lua.script("b = c.new()\nfunction write_setter() b.val = 24.0 end"); for (auto _ : state) { (void) _; lua["write_setter"](); } } void lambda_capture_measure(benchmark::State& state) { sol::state lua; double extra = kMagicValue; lua.set_function("f", [extra](double v) { return v + extra; }); lua.script("function invoke_lambda() return f(24.0) end"); for (auto _ : state) { (void) _; lua["invoke_lambda"](); } } void shared_ptr_return_measure(benchmark::State& state) { sol::state lua; registerSharedObject(lua); lua.script("function invoke_shared() return get_shared():get() end"); for (auto _ : state) { (void) _; lua["invoke_shared"](); } } void shared_ptr_pass_measure(benchmark::State& state) { sol::state lua; registerSharedObject(lua); lua.script("obj = SharedObject()\nfunction invoke_pass_shared() return use_shared(obj) end"); for (auto _ : state) { (void) _; lua["invoke_pass_shared"](); } } void static_member_function_call_measure(benchmark::State& state) { sol::state lua; registerCounter(lua); lua.script("function invoke_static() return Counter.static_add(10, 32) end"); for (auto _ : state) { (void) _; lua["invoke_static"](); } } void derived_method_call_measure(benchmark::State& state) { sol::state lua; lua.new_usertype("ComplexBaseA", "a_func", &ComplexBaseA::a_func, "a", &ComplexBaseA::a); lua.new_usertype("ComplexBaseB", "b_func", &ComplexBaseB::b_func, "b", &ComplexBaseB::b); lua.new_usertype("ComplexAB", sol::constructors(), sol::base_classes, sol::bases(), "ab_func", &ComplexAB::ab_func, "ab", &ComplexAB::ab); ComplexAB ab; lua["obj"] = &ab; lua.script("function call_derived() return obj:ab_func() end"); for (auto _ : state) { (void) _; lua["call_derived"](); } } void implicit_inheritance_measure(benchmark::State& state) { sol::state lua; lua.new_usertype("ComplexBaseA", "a_func", &ComplexBaseA::a_func); lua.new_usertype("ComplexAB", sol::constructors(), sol::base_classes, sol::bases(), "ab_func", &ComplexAB::ab_func); lua.set_function("call_a", +[](ComplexBaseA* obj) -> double { return obj->a_func(); }); lua.script("obj = ComplexAB.new()"); lua.script("function test_implicit() return call_a(obj) end"); for (auto _ : state) { (void) _; lua["test_implicit"](); } } } // namespace BENCHMARK(table_global_string_get_measure)->Name("table_global_string_get_measure"); BENCHMARK(table_global_string_set_measure)->Name("table_global_string_set_measure"); BENCHMARK(table_get_measure)->Name("table_get_measure"); BENCHMARK(table_set_measure)->Name("table_set_measure"); BENCHMARK(table_chained_get_measure)->Name("table_chained_get_measure"); BENCHMARK(table_chained_set_measure)->Name("table_chained_set_measure"); BENCHMARK(c_function_measure)->Name("c_function_measure"); BENCHMARK(c_function_through_lua_in_c_measure)->Name("c_function_through_lua_in_c_measure"); BENCHMARK(lua_function_in_c_measure)->Name("lua_function_in_c_measure"); BENCHMARK(member_function_call_measure)->Name("member_function_call_measure"); BENCHMARK(userdata_variable_access_measure)->Name("userdata_variable_access_measure"); BENCHMARK(userdata_variable_access_large_measure)->Name("userdata_variable_access_large_measure"); BENCHMARK(userdata_variable_access_last_measure)->Name("userdata_variable_access_last_measure"); BENCHMARK(multi_return_lua_measure)->Name("multi_return_lua_measure"); BENCHMARK(multi_return_measure)->Name("multi_return_measure"); BENCHMARK(stateful_function_object_measure)->Name("stateful_function_object_measure"); BENCHMARK(derived_base_measure)->Name("derived_base_measure"); BENCHMARK(return_userdata_measure)->Name("return_userdata_measure"); BENCHMARK(optional_success_measure)->Name("optional_success_measure"); BENCHMARK(optional_half_failure_measure)->Name("optional_half_failure_measure"); BENCHMARK(optional_failure_measure)->Name("optional_failure_measure"); BENCHMARK(implicit_inheritance_measure)->Name("implicit_inheritance_measure"); BENCHMARK(userdata_variable_write_measure)->Name("userdata_variable_write_measure"); BENCHMARK(userdata_property_getter_measure)->Name("userdata_property_getter_measure"); BENCHMARK(userdata_property_setter_measure)->Name("userdata_property_setter_measure"); BENCHMARK(lambda_capture_measure)->Name("lambda_capture_measure"); BENCHMARK(shared_ptr_return_measure)->Name("shared_ptr_return_measure"); BENCHMARK(shared_ptr_pass_measure)->Name("shared_ptr_pass_measure"); BENCHMARK(static_member_function_call_measure)->Name("static_member_function_call_measure"); BENCHMARK(derived_method_call_measure)->Name("derived_method_call_measure");