初始化内容
This commit is contained in:
+105
@@ -0,0 +1,105 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
cmake_policy (SET CMP0169 OLD)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
set(LUABRIDGE_BENCHMARK_WITH_SOL3 ON CACHE BOOL "Build Sol3 benchmark target")
|
||||
set(LUABRIDGE_SOL2_GIT_REPOSITORY "https://github.com/ThePhD/sol2.git" CACHE STRING "sol2 repository URL")
|
||||
set(LUABRIDGE_SOL2_GIT_TAG "v3.3.0" CACHE STRING "sol2 git tag or commit")
|
||||
|
||||
set(LUABRIDGE_BENCHMARK_WITH_LUABRIDGE ON CACHE BOOL "Build LuaBridge benchmark target")
|
||||
set(LUABRIDGE_VANILLA_GIT_REPOSITORY "https://github.com/vinniefalco/LuaBridge.git" CACHE STRING "LuaBridge vanilla repository URL")
|
||||
set(LUABRIDGE_VANILLA_GIT_TAG "master" CACHE STRING "LuaBridge vanilla git tag or commit")
|
||||
|
||||
set(LUABRIDGE_GOOGLE_BENCHMARK_GIT_REPOSITORY "https://github.com/google/benchmark.git" CACHE STRING "Google Benchmark repository URL")
|
||||
set(LUABRIDGE_GOOGLE_BENCHMARK_GIT_TAG "v1.8.4" CACHE STRING "Google Benchmark git tag or commit")
|
||||
|
||||
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
|
||||
set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
|
||||
FetchContent_Declare(
|
||||
googlebenchmark
|
||||
GIT_REPOSITORY ${LUABRIDGE_GOOGLE_BENCHMARK_GIT_REPOSITORY}
|
||||
GIT_TAG ${LUABRIDGE_GOOGLE_BENCHMARK_GIT_TAG})
|
||||
FetchContent_MakeAvailable(googlebenchmark)
|
||||
|
||||
if (LUABRIDGE_BENCHMARK_WITH_SOL3)
|
||||
FetchContent_Declare(
|
||||
sol2
|
||||
GIT_REPOSITORY ${LUABRIDGE_SOL2_GIT_REPOSITORY}
|
||||
GIT_TAG ${LUABRIDGE_SOL2_GIT_TAG})
|
||||
FetchContent_GetProperties(sol2)
|
||||
if (NOT sol2_POPULATED)
|
||||
FetchContent_Populate(sol2)
|
||||
endif()
|
||||
|
||||
# Work around a sol2 optional<T&>::emplace bug on recent Apple Clang toolchains.
|
||||
set(SOL2_OPTIONAL_IMPL "${sol2_SOURCE_DIR}/include/sol/optional_implementation.hpp")
|
||||
if (EXISTS "${SOL2_OPTIONAL_IMPL}")
|
||||
file(READ "${SOL2_OPTIONAL_IMPL}" SOL2_OPTIONAL_IMPL_CONTENT)
|
||||
set(SOL2_OPTIONAL_REFBLOCK_OLD "\t\ttemplate <class... Args>\n\t\tT& emplace(Args&&... args) noexcept {\n\t\t\tstatic_assert(std::is_constructible<T, Args&&...>::value, \"T must be constructible with Args\");\n\n\t\t\t*this = nullopt;\n\t\t\tthis->construct(std::forward<Args>(args)...);\n\t\t}\n")
|
||||
string(FIND "${SOL2_OPTIONAL_IMPL_CONTENT}" "${SOL2_OPTIONAL_REFBLOCK_OLD}" SOL2_PATCH_NEEDLE_POS)
|
||||
if (NOT SOL2_PATCH_NEEDLE_POS EQUAL -1)
|
||||
set(SOL2_OPTIONAL_REFBLOCK_NEW "\t\ttemplate <class... Args>\n\t\tT& emplace(Args&&... args) noexcept {\n\t\t\tstatic_assert(std::is_constructible<T, Args&&...>::value, \"T must be constructible with Args\");\n\n\t\t\t*this = nullopt;\n\t\t\tint emplace_workaround[] = { 0, ((*this = std::forward<Args>(args)), 0)... };\n\t\t\t(void) emplace_workaround;\n\t\t\treturn *m_value;\n\t\t}\n")
|
||||
string(REPLACE
|
||||
"${SOL2_OPTIONAL_REFBLOCK_OLD}"
|
||||
"${SOL2_OPTIONAL_REFBLOCK_NEW}"
|
||||
SOL2_OPTIONAL_IMPL_CONTENT
|
||||
"${SOL2_OPTIONAL_IMPL_CONTENT}")
|
||||
file(WRITE "${SOL2_OPTIONAL_IMPL}" "${SOL2_OPTIONAL_IMPL_CONTENT}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (LUABRIDGE_BENCHMARK_WITH_LUABRIDGE)
|
||||
FetchContent_Declare(
|
||||
luabridge_vanilla
|
||||
GIT_REPOSITORY ${LUABRIDGE_VANILLA_GIT_REPOSITORY}
|
||||
GIT_TAG ${LUABRIDGE_VANILLA_GIT_TAG})
|
||||
FetchContent_GetProperties(luabridge_vanilla)
|
||||
if (NOT luabridge_vanilla_POPULATED)
|
||||
FetchContent_Populate(luabridge_vanilla)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
function(add_luabridge_benchmark_target target_name source_file)
|
||||
add_executable(${target_name}
|
||||
${source_file}
|
||||
benchmark_common.cpp
|
||||
../Tests/Lua/LuaLibrary5.4.8.cpp)
|
||||
|
||||
target_include_directories(${target_name} PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
${CMAKE_CURRENT_LIST_DIR}/..
|
||||
${CMAKE_CURRENT_LIST_DIR}/../Tests
|
||||
${CMAKE_CURRENT_LIST_DIR}/../Tests/Lua/Lua.5.4.8/src)
|
||||
|
||||
target_compile_definitions(${target_name} PRIVATE
|
||||
LUABRIDGE_BENCHMARK_LUA54=1
|
||||
LUABRIDGE_TEST_LUA_VERSION=504)
|
||||
|
||||
target_link_libraries(${target_name} PRIVATE
|
||||
benchmark::benchmark
|
||||
benchmark::benchmark_main)
|
||||
endfunction()
|
||||
|
||||
add_luabridge_benchmark_target(LuaBridge3Benchmark benchmark_luabridge3.cpp)
|
||||
target_include_directories(LuaBridge3Benchmark PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}/../Source)
|
||||
|
||||
if (LUABRIDGE_BENCHMARK_WITH_LUABRIDGE)
|
||||
add_luabridge_benchmark_target(LuaBridgeVanillaBenchmark benchmark_luabridge.cpp)
|
||||
target_include_directories(LuaBridgeVanillaBenchmark PRIVATE
|
||||
${luabridge_vanilla_SOURCE_DIR}/Source)
|
||||
endif()
|
||||
|
||||
if (LUABRIDGE_BENCHMARK_WITH_SOL3)
|
||||
add_luabridge_benchmark_target(Sol3Benchmark benchmark_sol3.cpp)
|
||||
target_include_directories(Sol3Benchmark PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}/../Source
|
||||
${sol2_SOURCE_DIR}/include)
|
||||
|
||||
target_compile_definitions(Sol3Benchmark PRIVATE
|
||||
SOL_ALL_SAFETIES_ON=1
|
||||
SOL_NO_EXCEPTIONS=1
|
||||
SOL_LUA_VERSION=504)
|
||||
endif()
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
# Lua Binding Benchmarks
|
||||
|
||||
This directory contains Google Benchmark based executables for:
|
||||
|
||||
- LuaBridge3 (current workspace)
|
||||
- LuaBridge vanilla (`https://github.com/vinniefalco/LuaBridge`)
|
||||
- sol3 (`https://github.com/ThePhD/sol2`)
|
||||
|
||||
All benchmark executables are built with the same embedded Lua 5.4.8 runtime source (`Tests/Lua/LuaLibrary5.4.8.cpp`) for fair comparisons.
|
||||
|
||||
## Build
|
||||
|
||||
From project root:
|
||||
|
||||
```bash
|
||||
cmake -S . -B Build -DCMAKE_BUILD_TYPE=Release -DLUABRIDGE_BENCHMARKS=ON
|
||||
cmake --build Build --config Release --target LuaBridge3Benchmark LuaBridgeVanillaBenchmark
|
||||
```
|
||||
|
||||
To also build Sol3 benchmark target:
|
||||
|
||||
```bash
|
||||
cmake -S . -B Build -DCMAKE_BUILD_TYPE=Release -DLUABRIDGE_BENCHMARKS=ON -DLUABRIDGE_BENCHMARK_WITH_SOL3=ON
|
||||
cmake --build Build --config Release --target Sol3Benchmark
|
||||
```
|
||||
|
||||
## Dependency Sources (FetchContent)
|
||||
|
||||
Defaults:
|
||||
|
||||
- Google Benchmark: `https://github.com/google/benchmark.git` (`v1.8.4`)
|
||||
- sol3: `https://github.com/ThePhD/sol2.git` (`v3.5.0`)
|
||||
- LuaBridge vanilla: `https://github.com/vinniefalco/LuaBridge.git` (`master`)
|
||||
|
||||
You can override these at configure time:
|
||||
|
||||
```bash
|
||||
cmake -S . -B Build \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLUABRIDGE_BENCHMARKS=ON \
|
||||
-DLUABRIDGE_SOL2_GIT_REPOSITORY=https://github.com/ThePhD/sol2.git \
|
||||
-DLUABRIDGE_SOL2_GIT_TAG=v3.5.0 \
|
||||
-DLUABRIDGE_VANILLA_GIT_REPOSITORY=https://github.com/vinniefalco/LuaBridge.git \
|
||||
-DLUABRIDGE_VANILLA_GIT_TAG=master \
|
||||
-DLUABRIDGE_GOOGLE_BENCHMARK_GIT_REPOSITORY=https://github.com/google/benchmark.git \
|
||||
-DLUABRIDGE_GOOGLE_BENCHMARK_GIT_TAG=v1.8.4
|
||||
```
|
||||
|
||||
## Run Benchmarks
|
||||
|
||||
Each executable supports standard Google Benchmark CLI flags.
|
||||
|
||||
```bash
|
||||
./Build/Benchmarks/LuaBridge3Benchmark --benchmark_out=Build/Benchmarks/luabridge3.json --benchmark_out_format=json
|
||||
./Build/Benchmarks/LuaBridgeVanillaBenchmark --benchmark_out=Build/Benchmarks/luabridge_vanilla.json --benchmark_out_format=json
|
||||
./Build/Benchmarks/Sol3Benchmark --benchmark_out=Build/Benchmarks/sol3.json --benchmark_out_format=json # if enabled
|
||||
```
|
||||
|
||||
Recommended consistency flags for fair comparison:
|
||||
|
||||
```bash
|
||||
--benchmark_min_time=0.1 --benchmark_repetitions=5
|
||||
```
|
||||
|
||||
## Plot Results
|
||||
|
||||
The script `plot_benchmarks.py` merges one or more Google Benchmark JSON files and generates a grouped comparison chart.
|
||||
|
||||
```bash
|
||||
python3 Benchmarks/plot_benchmarks.py \
|
||||
--input Build/Benchmarks/luabridge3.json Build/Benchmarks/luabridge_vanilla.json Build/Benchmarks/sol3.json \
|
||||
--output Build/Benchmarks/lua_bindings_comparison.png
|
||||
```
|
||||
|
||||
Outputs:
|
||||
|
||||
- PNG chart (grouped bars, lower is better)
|
||||
- Optional skipped/error report file next to the image (`*_skipped.txt`)
|
||||
|
||||
## Notes
|
||||
|
||||
- Some vanilla LuaBridge benchmarks are marked as skipped where the feature is unsupported.
|
||||
- Sol3 target is optional (`LUABRIDGE_BENCHMARK_WITH_SOL3`) because current sol2 headers can fail to compile on some toolchains.
|
||||
- If you need stricter reproducibility, pin all FetchContent dependencies to commits instead of branches.
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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 <string>
|
||||
|
||||
namespace lbsbench {
|
||||
|
||||
void luaCheckOrThrow(lua_State* L, int status, std::string_view where)
|
||||
{
|
||||
if (status == LUA_OK)
|
||||
return;
|
||||
|
||||
const char* message = lua_tostring(L, -1);
|
||||
std::string error(where);
|
||||
error += ": ";
|
||||
error += (message ? message : "unknown lua error");
|
||||
lua_pop(L, 1);
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
|
||||
void luaDoStringOrThrow(lua_State* L, std::string_view code, std::string_view where)
|
||||
{
|
||||
const int status = luaL_dostring(L, std::string(code).c_str());
|
||||
luaCheckOrThrow(L, status, where);
|
||||
}
|
||||
|
||||
} // namespace lbsbench
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
// https://github.com/kunitoki/LuaBridge3
|
||||
// Copyright 2026, kunitoki
|
||||
// Inspired from https://github.com/ThePhD/lua-bindings-shootout by ThePhD
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Lua/LuaLibrary.h"
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace lbsbench {
|
||||
|
||||
inline constexpr double kMagicValue = 24.0;
|
||||
|
||||
struct Counter
|
||||
{
|
||||
int value = 0;
|
||||
|
||||
void inc()
|
||||
{
|
||||
++value;
|
||||
}
|
||||
|
||||
int add(int x)
|
||||
{
|
||||
value += x;
|
||||
return value;
|
||||
}
|
||||
|
||||
int get() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void set(int v)
|
||||
{
|
||||
value = v;
|
||||
}
|
||||
|
||||
static int static_add(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
};
|
||||
|
||||
struct Basic
|
||||
{
|
||||
double var = 0.0;
|
||||
|
||||
double get() const
|
||||
{
|
||||
return var;
|
||||
}
|
||||
|
||||
void set(double v)
|
||||
{
|
||||
var = v;
|
||||
}
|
||||
};
|
||||
|
||||
struct BasicLarge
|
||||
{
|
||||
std::int64_t var = 0;
|
||||
std::int64_t var0 = 0;
|
||||
std::int64_t var1 = 0;
|
||||
std::int64_t var2 = 0;
|
||||
std::int64_t var3 = 0;
|
||||
std::int64_t var4 = 0;
|
||||
std::int64_t var5 = 0;
|
||||
std::int64_t var6 = 0;
|
||||
std::int64_t var7 = 0;
|
||||
std::int64_t var8 = 0;
|
||||
std::int64_t var9 = 0;
|
||||
std::int64_t var10 = 0;
|
||||
std::int64_t var11 = 0;
|
||||
std::int64_t var12 = 0;
|
||||
std::int64_t var13 = 0;
|
||||
std::int64_t var14 = 0;
|
||||
std::int64_t var15 = 0;
|
||||
std::int64_t var16 = 0;
|
||||
std::int64_t var17 = 0;
|
||||
std::int64_t var18 = 0;
|
||||
std::int64_t var19 = 0;
|
||||
std::int64_t var20 = 0;
|
||||
std::int64_t var21 = 0;
|
||||
std::int64_t var22 = 0;
|
||||
std::int64_t var23 = 0;
|
||||
std::int64_t var24 = 0;
|
||||
std::int64_t var25 = 0;
|
||||
std::int64_t var26 = 0;
|
||||
std::int64_t var27 = 0;
|
||||
std::int64_t var28 = 0;
|
||||
std::int64_t var29 = 0;
|
||||
std::int64_t var30 = 0;
|
||||
std::int64_t var31 = 0;
|
||||
std::int64_t var32 = 0;
|
||||
std::int64_t var33 = 0;
|
||||
std::int64_t var34 = 0;
|
||||
std::int64_t var35 = 0;
|
||||
std::int64_t var36 = 0;
|
||||
std::int64_t var37 = 0;
|
||||
std::int64_t var38 = 0;
|
||||
std::int64_t var39 = 0;
|
||||
std::int64_t var40 = 0;
|
||||
std::int64_t var41 = 0;
|
||||
std::int64_t var42 = 0;
|
||||
std::int64_t var43 = 0;
|
||||
std::int64_t var44 = 0;
|
||||
std::int64_t var45 = 0;
|
||||
std::int64_t var46 = 0;
|
||||
std::int64_t var47 = 0;
|
||||
std::int64_t var48 = 0;
|
||||
std::int64_t var49 = 0;
|
||||
};
|
||||
|
||||
struct ComplexBaseA
|
||||
{
|
||||
double a = kMagicValue;
|
||||
|
||||
double a_func() const
|
||||
{
|
||||
return a;
|
||||
}
|
||||
};
|
||||
|
||||
struct ComplexBaseB
|
||||
{
|
||||
double b = kMagicValue;
|
||||
|
||||
double b_func() const
|
||||
{
|
||||
return b;
|
||||
}
|
||||
};
|
||||
|
||||
struct ComplexAB : ComplexBaseA, ComplexBaseB
|
||||
{
|
||||
double ab = kMagicValue;
|
||||
|
||||
double ab_func() const
|
||||
{
|
||||
return ab;
|
||||
}
|
||||
};
|
||||
|
||||
struct StatefulFunction
|
||||
{
|
||||
double operator()(double v) const
|
||||
{
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
struct SharedObject : std::enable_shared_from_this<SharedObject>
|
||||
{
|
||||
double value = kMagicValue;
|
||||
|
||||
double get() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
struct Vec3Source
|
||||
{
|
||||
float x = 0.f, y = 0.f, z = 0.f;
|
||||
Vec3Source() = default;
|
||||
Vec3Source(float x_, float y_, float z_) : x(x_), y(y_), z(z_) {}
|
||||
};
|
||||
|
||||
struct Vec3Target
|
||||
{
|
||||
float x = 0.f, y = 0.f, z = 0.f;
|
||||
Vec3Target() = default;
|
||||
Vec3Target(float x_, float y_, float z_) : x(x_), y(y_), z(z_) {}
|
||||
};
|
||||
|
||||
struct ColorSource
|
||||
{
|
||||
float r = 0.f, g = 0.f, b = 0.f;
|
||||
ColorSource() = default;
|
||||
ColorSource(float r_, float g_, float b_) : r(r_), g(g_), b(b_) {}
|
||||
};
|
||||
|
||||
inline float sumVec3(Vec3Target v)
|
||||
{
|
||||
return v.x + v.y + v.z;
|
||||
}
|
||||
|
||||
inline float sumVec3Ref(const Vec3Target& v)
|
||||
{
|
||||
return v.x + v.y + v.z;
|
||||
}
|
||||
|
||||
inline Basic* basic_return()
|
||||
{
|
||||
static Basic value{};
|
||||
return &value;
|
||||
}
|
||||
|
||||
inline double basic_get_var(Basic* b)
|
||||
{
|
||||
return b ? b->var : 0.0;
|
||||
}
|
||||
|
||||
inline std::shared_ptr<SharedObject> shared_object_return()
|
||||
{
|
||||
static std::shared_ptr<SharedObject> obj = std::make_shared<SharedObject>();
|
||||
return obj;
|
||||
}
|
||||
|
||||
inline double shared_object_get_value(std::shared_ptr<SharedObject> obj)
|
||||
{
|
||||
return obj ? obj->get() : 0.0;
|
||||
}
|
||||
|
||||
void luaCheckOrThrow(lua_State* L, int status, std::string_view where);
|
||||
void luaDoStringOrThrow(lua_State* L, std::string_view code, std::string_view where);
|
||||
|
||||
inline void setSkipped(benchmark::State& state, std::string_view reason)
|
||||
{
|
||||
state.SkipWithError(std::string(reason).c_str());
|
||||
}
|
||||
|
||||
} // namespace lbsbench
|
||||
+514
@@ -0,0 +1,514 @@
|
||||
// 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 <LuaBridge/LuaBridge.h>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace lbsbench;
|
||||
|
||||
int vanilla_multi_return(lua_State* L)
|
||||
{
|
||||
const double i = lua_tonumber(L, 1);
|
||||
luabridge::push(L, i);
|
||||
luabridge::push(L, i * 2.0);
|
||||
return 2;
|
||||
}
|
||||
|
||||
void registerBasicGetterSetter(lua_State* L)
|
||||
{
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.beginClass<Basic>("c")
|
||||
.addConstructor<void (*)()>()
|
||||
.addProperty("val", &Basic::get, &Basic::set)
|
||||
.endClass();
|
||||
}
|
||||
|
||||
void registerCounter(lua_State* L)
|
||||
{
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.beginClass<Counter>("Counter")
|
||||
.addConstructor<void (*)()>()
|
||||
.addFunction("get", &Counter::get)
|
||||
.addStaticFunction("static_add", &Counter::static_add)
|
||||
.endClass();
|
||||
}
|
||||
|
||||
lua_State* makeLua()
|
||||
{
|
||||
lua_State* L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
return L;
|
||||
}
|
||||
|
||||
void registerBasic(lua_State* L)
|
||||
{
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.beginClass<Basic>("c")
|
||||
.addConstructor<void (*)()>()
|
||||
.addFunction("set", &Basic::set)
|
||||
.addFunction("get", &Basic::get)
|
||||
.addData("var", &Basic::var)
|
||||
.endClass();
|
||||
}
|
||||
|
||||
void table_global_string_get_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luabridge::setGlobal(L, kMagicValue, "value");
|
||||
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
x += static_cast<double>(luabridge::getGlobal(L, "value"));
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void table_global_string_set_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
|
||||
double v = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
v += kMagicValue;
|
||||
luabridge::setGlobal(L, v, "value");
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(v);
|
||||
}
|
||||
|
||||
void table_get_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luaDoStringOrThrow(L, "warble = { value = 24.0 }", "vanilla table_get setup");
|
||||
luabridge::LuaRef t = luabridge::getGlobal(L, "warble");
|
||||
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
x += static_cast<double>(t["value"]);
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void table_set_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luaDoStringOrThrow(L, "warble = { value = 24.0 }", "vanilla table_set setup");
|
||||
luabridge::LuaRef t = luabridge::getGlobal(L, "warble");
|
||||
|
||||
double v = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
v += kMagicValue;
|
||||
t["value"] = v;
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(v);
|
||||
}
|
||||
|
||||
void table_chained_get_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luaDoStringOrThrow(L, "ulahibe = { warble = { value = 24.0 } }", "vanilla chained_get setup");
|
||||
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
luabridge::LuaRef tw = luabridge::getGlobal(L, "ulahibe")["warble"];
|
||||
x += static_cast<double>(tw["value"]);
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void table_chained_set_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luaDoStringOrThrow(L, "ulahibe = { warble = { value = 24.0 } }", "vanilla chained_set setup");
|
||||
|
||||
double v = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
v += kMagicValue;
|
||||
luabridge::LuaRef tw = luabridge::getGlobal(L, "ulahibe")["warble"];
|
||||
tw["value"] = v;
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(v);
|
||||
}
|
||||
|
||||
void c_function_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luabridge::getGlobalNamespace(L).addFunction("f", +[](double v) { return v; });
|
||||
luaDoStringOrThrow(L, "function invoke_f() return f(24.0) end", "vanilla c_function setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_f");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla invoke_f");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void lua_function_in_c_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luaDoStringOrThrow(L, "function f(i) return i end", "vanilla lua_function setup");
|
||||
|
||||
luabridge::LuaRef f = luabridge::getGlobal(L, "f");
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
x += static_cast<double>(f(kMagicValue));
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void c_function_through_lua_in_c_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luabridge::getGlobalNamespace(L).addFunction("f", +[](double v) { return v; });
|
||||
|
||||
luabridge::LuaRef f = luabridge::getGlobal(L, "f");
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
x += static_cast<double>(f(kMagicValue));
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void member_function_call_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerBasic(L);
|
||||
luaDoStringOrThrow(L, "b = c()", "vanilla member setup");
|
||||
luaDoStringOrThrow(L, "function call_member() b:set(b:get() + 1.0) end", "vanilla member closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "call_member");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 0, 0), "vanilla call_member");
|
||||
}
|
||||
}
|
||||
|
||||
void userdata_variable_access_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerBasic(L);
|
||||
luaDoStringOrThrow(L, "b = c()", "vanilla userdata setup");
|
||||
luaDoStringOrThrow(L, "function access_var() b.var = b.var + 1.0 return b.var end", "vanilla userdata closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "access_var");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla access_var");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void userdata_variable_access_large_measure(benchmark::State& state)
|
||||
{
|
||||
setSkipped(state, "unsupported in LuaBridge vanilla benchmark parity mode");
|
||||
}
|
||||
|
||||
void userdata_variable_access_last_measure(benchmark::State& state)
|
||||
{
|
||||
setSkipped(state, "unsupported in LuaBridge vanilla benchmark parity mode");
|
||||
}
|
||||
|
||||
void stateful_function_object_measure(benchmark::State& state)
|
||||
{
|
||||
setSkipped(state, "unsupported in LuaBridge vanilla benchmark parity mode");
|
||||
}
|
||||
|
||||
void multi_return_lua_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luabridge::getGlobalNamespace(L).addCFunction("f", vanilla_multi_return);
|
||||
luaDoStringOrThrow(L, "function invoke_multi() local a,b=f(24.0) return a+b end", "vanilla multi_return_lua setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_multi");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla invoke_multi");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void multi_return_measure(benchmark::State& state)
|
||||
{
|
||||
setSkipped(state, "unsupported conceptual multi-return conversion in LuaBridge vanilla");
|
||||
}
|
||||
|
||||
void derived_base_measure(benchmark::State& state)
|
||||
{
|
||||
setSkipped(state, "unsupported for multi inheritance in LuaBridge vanilla");
|
||||
}
|
||||
|
||||
void return_userdata_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerBasic(L);
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.addFunction("f", &basic_return)
|
||||
.addFunction("h", &basic_get_var);
|
||||
luaDoStringOrThrow(L, "function invoke_userdata() return h(f()) end", "vanilla return_userdata setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_userdata");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla invoke_userdata");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void optional_success_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luaDoStringOrThrow(L, "warble = { value = 24.0 }", "vanilla optional_success setup");
|
||||
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
luabridge::LuaRef tt = luabridge::getGlobal(L, "warble");
|
||||
if (tt.isTable())
|
||||
{
|
||||
luabridge::LuaRef tv = tt["value"];
|
||||
x += tv.isNumber() ? static_cast<double>(tv) : 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
x += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void optional_half_failure_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luaDoStringOrThrow(L, "warble = { value = 'x' }", "vanilla optional_half_failure setup");
|
||||
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
luabridge::LuaRef tt = luabridge::getGlobal(L, "warble");
|
||||
if (tt.isTable())
|
||||
{
|
||||
luabridge::LuaRef tv = tt["value"];
|
||||
x += tv.isNumber() ? static_cast<double>(tv) : 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
x += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void optional_failure_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
luabridge::LuaRef tt = luabridge::getGlobal(L, "warble");
|
||||
if (tt.isTable())
|
||||
{
|
||||
luabridge::LuaRef tv = tt["value"];
|
||||
x += tv.isNumber() ? static_cast<double>(tv) : 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
x += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void userdata_variable_write_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerBasic(L);
|
||||
luaDoStringOrThrow(L, "b = c()", "vanilla userdata_write setup");
|
||||
luaDoStringOrThrow(L, "function write_var() b.var = 24.0 end", "vanilla userdata_write closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "write_var");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 0, 0), "vanilla write_var");
|
||||
}
|
||||
}
|
||||
|
||||
void userdata_property_getter_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerBasicGetterSetter(L);
|
||||
luaDoStringOrThrow(L, "b = c()", "vanilla property_getter setup");
|
||||
luaDoStringOrThrow(L, "function read_getter() return b.val end", "vanilla property_getter closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "read_getter");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla read_getter");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void userdata_property_setter_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerBasicGetterSetter(L);
|
||||
luaDoStringOrThrow(L, "b = c()", "vanilla property_setter setup");
|
||||
luaDoStringOrThrow(L, "function write_setter() b.val = 24.0 end", "vanilla property_setter closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "write_setter");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 0, 0), "vanilla write_setter");
|
||||
}
|
||||
}
|
||||
|
||||
void lambda_capture_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
double extra = kMagicValue;
|
||||
luabridge::getGlobalNamespace(L).addFunction("f", std::function<double(double)>([extra](double v) { return v + extra; }));
|
||||
luaDoStringOrThrow(L, "function invoke_lambda() return f(24.0) end", "vanilla lambda_capture setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_lambda");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla invoke_lambda");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void shared_ptr_return_measure(benchmark::State& state)
|
||||
{
|
||||
setSkipped(state, "unsupported shared_ptr container in LuaBridge vanilla");
|
||||
}
|
||||
|
||||
void shared_ptr_pass_measure(benchmark::State& state)
|
||||
{
|
||||
setSkipped(state, "unsupported shared_ptr container in LuaBridge vanilla");
|
||||
}
|
||||
|
||||
void static_member_function_call_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerCounter(L);
|
||||
luaDoStringOrThrow(L, "function invoke_static() return Counter.static_add(10, 32) end", "vanilla static_member_function setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_static");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla invoke_static");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void derived_method_call_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.beginClass<ComplexBaseA>("ComplexBaseA")
|
||||
.addFunction("a_func", &ComplexBaseA::a_func)
|
||||
.addProperty("a", &ComplexBaseA::a)
|
||||
.endClass()
|
||||
.deriveClass<ComplexAB, ComplexBaseA>("ComplexAB")
|
||||
.addConstructor<void (*)()>()
|
||||
.addFunction("ab_func", &ComplexAB::ab_func)
|
||||
.addProperty("ab", &ComplexAB::ab)
|
||||
.endClass();
|
||||
|
||||
luaDoStringOrThrow(L, "obj = ComplexAB()", "vanilla derived_method setup");
|
||||
luaDoStringOrThrow(L, "function call_derived() return obj:ab_func() end", "vanilla derived_method closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "call_derived");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla call_derived");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void implicit_inheritance_measure(benchmark::State& state)
|
||||
{
|
||||
setSkipped(state, "unsupported for multi inheritance in LuaBridge vanilla");
|
||||
}
|
||||
|
||||
} // 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");
|
||||
@@ -0,0 +1,805 @@
|
||||
// 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 "LuaBridge/LuaBridge.h"
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <tuple>
|
||||
|
||||
namespace luabridge {
|
||||
|
||||
template <>
|
||||
struct StackConversion<lbsbench::Vec3Target>
|
||||
{
|
||||
static constexpr bool enabled = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct StackConverter<lbsbench::Vec3Target, lbsbench::Vec3Source>
|
||||
{
|
||||
static lbsbench::Vec3Target convert(const lbsbench::Vec3Source& s)
|
||||
{
|
||||
return {s.x, s.y, s.z};
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct StackConverter<lbsbench::Vec3Target, lbsbench::ColorSource>
|
||||
{
|
||||
static lbsbench::Vec3Target convert(const lbsbench::ColorSource& s)
|
||||
{
|
||||
return {s.r, s.g, s.b};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace luabridge
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace lbsbench;
|
||||
|
||||
std::tuple<double, double> lb3_multi_return(double value)
|
||||
{
|
||||
return { value, value * 2.0 };
|
||||
}
|
||||
|
||||
void registerBasic(lua_State* L)
|
||||
{
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.beginClass<Basic>("c")
|
||||
.addConstructor<void (*)()>()
|
||||
.addFunction("set", &Basic::set)
|
||||
.addFunction("get", &Basic::get)
|
||||
.addProperty("var", &Basic::var)
|
||||
.endClass();
|
||||
}
|
||||
|
||||
void registerBasicLarge(lua_State* L)
|
||||
{
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.beginClass<BasicLarge>("cl")
|
||||
.addConstructor<void (*)()>()
|
||||
.addProperty("var", &BasicLarge::var)
|
||||
.addProperty("var0", &BasicLarge::var0)
|
||||
.addProperty("var1", &BasicLarge::var1)
|
||||
.addProperty("var2", &BasicLarge::var2)
|
||||
.addProperty("var3", &BasicLarge::var3)
|
||||
.addProperty("var4", &BasicLarge::var4)
|
||||
.addProperty("var5", &BasicLarge::var5)
|
||||
.addProperty("var6", &BasicLarge::var6)
|
||||
.addProperty("var7", &BasicLarge::var7)
|
||||
.addProperty("var8", &BasicLarge::var8)
|
||||
.addProperty("var9", &BasicLarge::var9)
|
||||
.addProperty("var10", &BasicLarge::var10)
|
||||
.addProperty("var11", &BasicLarge::var11)
|
||||
.addProperty("var12", &BasicLarge::var12)
|
||||
.addProperty("var13", &BasicLarge::var13)
|
||||
.addProperty("var14", &BasicLarge::var14)
|
||||
.addProperty("var15", &BasicLarge::var15)
|
||||
.addProperty("var16", &BasicLarge::var16)
|
||||
.addProperty("var17", &BasicLarge::var17)
|
||||
.addProperty("var18", &BasicLarge::var18)
|
||||
.addProperty("var19", &BasicLarge::var19)
|
||||
.addProperty("var20", &BasicLarge::var20)
|
||||
.addProperty("var21", &BasicLarge::var21)
|
||||
.addProperty("var22", &BasicLarge::var22)
|
||||
.addProperty("var23", &BasicLarge::var23)
|
||||
.addProperty("var24", &BasicLarge::var24)
|
||||
.addProperty("var25", &BasicLarge::var25)
|
||||
.addProperty("var26", &BasicLarge::var26)
|
||||
.addProperty("var27", &BasicLarge::var27)
|
||||
.addProperty("var28", &BasicLarge::var28)
|
||||
.addProperty("var29", &BasicLarge::var29)
|
||||
.addProperty("var30", &BasicLarge::var30)
|
||||
.addProperty("var31", &BasicLarge::var31)
|
||||
.addProperty("var32", &BasicLarge::var32)
|
||||
.addProperty("var33", &BasicLarge::var33)
|
||||
.addProperty("var34", &BasicLarge::var34)
|
||||
.addProperty("var35", &BasicLarge::var35)
|
||||
.addProperty("var36", &BasicLarge::var36)
|
||||
.addProperty("var37", &BasicLarge::var37)
|
||||
.addProperty("var38", &BasicLarge::var38)
|
||||
.addProperty("var39", &BasicLarge::var39)
|
||||
.addProperty("var40", &BasicLarge::var40)
|
||||
.addProperty("var41", &BasicLarge::var41)
|
||||
.addProperty("var42", &BasicLarge::var42)
|
||||
.addProperty("var43", &BasicLarge::var43)
|
||||
.addProperty("var44", &BasicLarge::var44)
|
||||
.addProperty("var45", &BasicLarge::var45)
|
||||
.addProperty("var46", &BasicLarge::var46)
|
||||
.addProperty("var47", &BasicLarge::var47)
|
||||
.addProperty("var48", &BasicLarge::var48)
|
||||
.addProperty("var49", &BasicLarge::var49)
|
||||
.endClass();
|
||||
}
|
||||
|
||||
void registerBasicRW(lua_State* L)
|
||||
{
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.beginClass<Basic>("c")
|
||||
.addConstructor<void (*)()>()
|
||||
.addPropertyReadWrite("var", &Basic::var)
|
||||
.endClass();
|
||||
}
|
||||
|
||||
void registerBasicGetterSetter(lua_State* L)
|
||||
{
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.beginClass<Basic>("c")
|
||||
.addConstructor<void (*)()>()
|
||||
.addProperty("val", &Basic::get, &Basic::set)
|
||||
.endClass();
|
||||
}
|
||||
|
||||
void registerCounter(lua_State* L)
|
||||
{
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.beginClass<Counter>("Counter")
|
||||
.addConstructor<void (*)()>()
|
||||
.addFunction("get", &Counter::get)
|
||||
.addStaticFunction("static_add", &Counter::static_add)
|
||||
.endClass();
|
||||
}
|
||||
|
||||
void registerSharedObject(lua_State* L)
|
||||
{
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.beginClass<SharedObject>("SharedObject")
|
||||
.addConstructorFrom<std::shared_ptr<SharedObject>, void(*)()>()
|
||||
.addFunction("get", &SharedObject::get)
|
||||
.endClass()
|
||||
.addFunction("get_shared", &shared_object_return)
|
||||
.addFunction("use_shared", &shared_object_get_value);
|
||||
}
|
||||
|
||||
lua_State* makeLua()
|
||||
{
|
||||
lua_State* L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
luabridge::registerMainThread(L);
|
||||
#if LUABRIDGE_HAS_EXCEPTIONS
|
||||
luabridge::enableExceptions(L);
|
||||
#endif
|
||||
return L;
|
||||
}
|
||||
|
||||
void table_global_string_get_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luabridge::setGlobal(L, kMagicValue, "value");
|
||||
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
x += static_cast<double>(luabridge::getGlobal(L, "value"));
|
||||
}
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void table_global_string_set_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
|
||||
double v = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
v += kMagicValue;
|
||||
luabridge::setGlobal(L, v, "value");
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(v);
|
||||
}
|
||||
|
||||
void table_get_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luaDoStringOrThrow(L, "warble = { value = 24.0 }", "table_get setup");
|
||||
luabridge::LuaRef t = luabridge::getGlobal(L, "warble");
|
||||
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
x += static_cast<double>(t["value"]);
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void table_set_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luaDoStringOrThrow(L, "warble = { value = 24.0 }", "table_set setup");
|
||||
luabridge::LuaRef t = luabridge::getGlobal(L, "warble");
|
||||
|
||||
double v = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
v += kMagicValue;
|
||||
t["value"] = v;
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(v);
|
||||
}
|
||||
|
||||
void table_chained_get_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luaDoStringOrThrow(L, "ulahibe = { warble = { value = 24.0 } }", "table_chained_get setup");
|
||||
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
luabridge::LuaRef tw = luabridge::getGlobal(L, "ulahibe")["warble"];
|
||||
x += static_cast<double>(tw["value"]);
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void table_chained_set_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luaDoStringOrThrow(L, "ulahibe = { warble = { value = 24.0 } }", "table_chained_set setup");
|
||||
|
||||
double v = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
v += kMagicValue;
|
||||
luabridge::LuaRef tw = luabridge::getGlobal(L, "ulahibe")["warble"];
|
||||
tw["value"] = v;
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(v);
|
||||
}
|
||||
|
||||
void c_function_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luabridge::getGlobalNamespace(L).addFunction("f", +[](double v) { return v; });
|
||||
|
||||
luaDoStringOrThrow(L, "function invoke_f() return f(24.0) end", "c_function setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_f");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_f");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void lua_function_in_c_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luaDoStringOrThrow(L, "function f(i) return i end", "lua_function setup");
|
||||
|
||||
luabridge::LuaRef f = luabridge::getGlobal(L, "f");
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
x += f.call<double>(kMagicValue).valueOr(0.0);
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void c_function_through_lua_in_c_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luabridge::getGlobalNamespace(L).addFunction("f", +[](double v) { return v; });
|
||||
|
||||
luabridge::LuaRef f = luabridge::getGlobal(L, "f");
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
x += f.call<double>(kMagicValue).valueOr(0.0);
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void member_function_call_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerBasic(L);
|
||||
luaDoStringOrThrow(L, "b = c()", "member_function setup");
|
||||
luaDoStringOrThrow(L, "function call_member() b:set(b:get() + 1.0) end", "member_function closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "call_member");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 0, 0), "call_member");
|
||||
}
|
||||
}
|
||||
|
||||
void userdata_variable_access_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerBasic(L);
|
||||
luaDoStringOrThrow(L, "b = c()", "userdata_variable_access setup");
|
||||
luaDoStringOrThrow(L, "function access_var() return b.var end", "userdata_variable_access closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "access_var");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "access_var");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void userdata_variable_access_large_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerBasicLarge(L);
|
||||
luaDoStringOrThrow(L, "b = cl()", "userdata_variable_access_large setup");
|
||||
luaDoStringOrThrow(L, "function access_var_large() return b.var0 end", "userdata_variable_access_large closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "access_var_large");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "access_var_large");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void userdata_variable_access_last_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerBasicLarge(L);
|
||||
luaDoStringOrThrow(L, "b = cl()", "userdata_variable_access_last setup");
|
||||
luaDoStringOrThrow(L, "function access_var_last() return b.var49 end", "userdata_variable_access_last closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "access_var_last");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "access_var_last");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void stateful_function_object_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luabridge::getGlobalNamespace(L).addFunction("f", StatefulFunction{});
|
||||
|
||||
luabridge::LuaRef f = luabridge::getGlobal(L, "f");
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
x += f.call<double>(kMagicValue).valueOr(0.0);
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void multi_return_lua_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luabridge::getGlobalNamespace(L).addFunction("f", &lb3_multi_return);
|
||||
luaDoStringOrThrow(L, "function invoke_multi() local a,b=f(24.0) return a+b end", "multi_return_lua setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_multi");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_multi");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void multi_return_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luabridge::getGlobalNamespace(L).addFunction("f", &lb3_multi_return);
|
||||
luabridge::LuaRef f = luabridge::getGlobal(L, "f");
|
||||
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
auto result = f.call<std::tuple<double, double>>(kMagicValue).valueOr(std::make_tuple(0.0, 0.0));
|
||||
x += std::get<0>(result);
|
||||
x += std::get<1>(result);
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void derived_base_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.beginClass<ComplexBaseA>("ComplexBaseA")
|
||||
.addFunction("a_func", &ComplexBaseA::a_func)
|
||||
.addProperty("a", &ComplexBaseA::a)
|
||||
.endClass()
|
||||
.beginClass<ComplexBaseB>("ComplexBaseB")
|
||||
.addFunction("b_func", &ComplexBaseB::b_func)
|
||||
.addProperty("b", &ComplexBaseB::b)
|
||||
.endClass()
|
||||
.deriveClass<ComplexAB, ComplexBaseA, ComplexBaseB>("ComplexAB")
|
||||
.addConstructor<void (*)()>()
|
||||
.addFunction("ab_func", &ComplexAB::ab_func)
|
||||
.addProperty("ab", &ComplexAB::ab)
|
||||
.endClass();
|
||||
|
||||
luaDoStringOrThrow(L, "obj = ComplexAB()", "base_derived setup");
|
||||
luaDoStringOrThrow(L, "function call_base() return obj:a_func() + obj:b_func() end", "base_derived closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "call_base");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "call_base");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void optional_success_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luaDoStringOrThrow(L, "warble = { value = 24.0 }", "optional_success setup");
|
||||
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
auto result = luabridge::tryGetGlobalField<double>(L, "warble", "value");
|
||||
x += result ? *result : 1.0;
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void optional_half_failure_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
luaDoStringOrThrow(L, "warble = { value = 'x' }", "optional_half_failure setup");
|
||||
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
auto result = luabridge::tryGetGlobalField<double>(L, "warble", "value");
|
||||
x += result ? *result : 1.0;
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void optional_failure_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
|
||||
double x = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
auto result = luabridge::tryGetGlobalField<double>(L, "warble", "value");
|
||||
x += result ? *result : 1.0;
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
|
||||
void return_userdata_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerBasic(L);
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.addFunction("f", &basic_return)
|
||||
.addFunction("h", &basic_get_var);
|
||||
luaDoStringOrThrow(L, "function invoke_userdata() return h(f()) end", "return_userdata setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_userdata");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_userdata");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void userdata_variable_write_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerBasicRW(L);
|
||||
luaDoStringOrThrow(L, "b = c()", "userdata_variable_write setup");
|
||||
luaDoStringOrThrow(L, "function write_var() b.var = 24.0 end", "userdata_variable_write closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "write_var");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 0, 0), "write_var");
|
||||
}
|
||||
}
|
||||
|
||||
void userdata_property_getter_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerBasicGetterSetter(L);
|
||||
luaDoStringOrThrow(L, "b = c()", "userdata_property_getter setup");
|
||||
luaDoStringOrThrow(L, "function read_getter() return b.val end", "userdata_property_getter closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "read_getter");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "read_getter");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void userdata_property_setter_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerBasicGetterSetter(L);
|
||||
luaDoStringOrThrow(L, "b = c()", "userdata_property_setter setup");
|
||||
luaDoStringOrThrow(L, "function write_setter() b.val = 24.0 end", "userdata_property_setter closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "write_setter");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 0, 0), "write_setter");
|
||||
}
|
||||
}
|
||||
|
||||
void lambda_capture_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
double extra = kMagicValue;
|
||||
luabridge::getGlobalNamespace(L).addFunction("f", [extra](double v) { return v + extra; });
|
||||
luaDoStringOrThrow(L, "function invoke_lambda() return f(24.0) end", "lambda_capture setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_lambda");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_lambda");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void shared_ptr_return_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerSharedObject(L);
|
||||
luaDoStringOrThrow(L, "function invoke_shared() return get_shared():get() end", "shared_ptr_return setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_shared");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_shared");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void shared_ptr_pass_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerSharedObject(L);
|
||||
luaDoStringOrThrow(L, "obj = SharedObject()", "shared_ptr_pass setup");
|
||||
luaDoStringOrThrow(L, "function invoke_pass_shared() return use_shared(obj) end", "shared_ptr_pass closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_pass_shared");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_pass_shared");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void static_member_function_call_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerCounter(L);
|
||||
luaDoStringOrThrow(L, "function invoke_static() return Counter.static_add(10, 32) end", "static_member_function setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_static");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_static");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void derived_method_call_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.beginClass<ComplexBaseA>("ComplexBaseA")
|
||||
.addFunction("a_func", &ComplexBaseA::a_func)
|
||||
.addProperty("a", &ComplexBaseA::a)
|
||||
.endClass()
|
||||
.beginClass<ComplexBaseB>("ComplexBaseB")
|
||||
.addFunction("b_func", &ComplexBaseB::b_func)
|
||||
.addProperty("b", &ComplexBaseB::b)
|
||||
.endClass()
|
||||
.deriveClass<ComplexAB, ComplexBaseA, ComplexBaseB>("ComplexAB")
|
||||
.addConstructor<void (*)()>()
|
||||
.addFunction("ab_func", &ComplexAB::ab_func)
|
||||
.addProperty("ab", &ComplexAB::ab)
|
||||
.endClass();
|
||||
|
||||
luaDoStringOrThrow(L, "obj = ComplexAB()", "derived_method setup");
|
||||
luaDoStringOrThrow(L, "function call_derived() return obj:ab_func() end", "derived_method closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "call_derived");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "call_derived");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void implicit_inheritance_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.beginClass<ComplexBaseA>("ComplexBaseA")
|
||||
.addFunction("a_func", &ComplexBaseA::a_func)
|
||||
.endClass()
|
||||
.deriveClass<ComplexAB, ComplexBaseA>("ComplexAB")
|
||||
.addConstructor<void (*)()>()
|
||||
.addFunction("ab_func", &ComplexAB::ab_func)
|
||||
.endClass()
|
||||
.addFunction("call_a", +[](ComplexBaseA* obj) -> double { return obj->a_func(); });
|
||||
|
||||
luaDoStringOrThrow(L, "obj = ComplexAB()", "implicit_inheritance setup");
|
||||
luaDoStringOrThrow(L, "function test_implicit() return call_a(obj) end", "implicit_inheritance closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "test_implicit");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "test_implicit");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void registerConverter(lua_State* L)
|
||||
{
|
||||
luabridge::getGlobalNamespace(L)
|
||||
.beginClass<Vec3Source>("Vec3Source")
|
||||
.addConstructor<void(float, float, float)>()
|
||||
.addConverter<Vec3Target>()
|
||||
.endClass()
|
||||
.beginClass<ColorSource>("ColorSource")
|
||||
.addConstructor<void(float, float, float)>()
|
||||
.addConverter<Vec3Target>()
|
||||
.endClass()
|
||||
.beginClass<Vec3Target>("Vec3Target")
|
||||
.addConstructor<void(float, float, float)>()
|
||||
.endClass()
|
||||
.addFunction("sumVec3", &sumVec3)
|
||||
.addFunction("sumVec3Ref", &sumVec3Ref);
|
||||
}
|
||||
|
||||
void converter_exact_type_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerConverter(L);
|
||||
luaDoStringOrThrow(L, "obj = Vec3Target(1, 2, 3)", "converter_exact_type setup");
|
||||
luaDoStringOrThrow(L, "function invoke_exact() return sumVec3(obj) end", "converter_exact_type closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_exact");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_exact");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void converter_phase3_value_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerConverter(L);
|
||||
luaDoStringOrThrow(L, "obj = Vec3Source(1, 2, 3)", "converter_phase3_value setup");
|
||||
luaDoStringOrThrow(L, "function invoke_conv_value() return sumVec3(obj) end", "converter_phase3_value closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_conv_value");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_conv_value");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void converter_phase3_ref_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerConverter(L);
|
||||
luaDoStringOrThrow(L, "obj = Vec3Source(1, 2, 3)", "converter_phase3_ref setup");
|
||||
luaDoStringOrThrow(L, "function invoke_conv_ref() return sumVec3Ref(obj) end", "converter_phase3_ref closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_conv_ref");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_conv_ref");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void converter_multi_registered_measure(benchmark::State& state)
|
||||
{
|
||||
lua_State* L = makeLua();
|
||||
registerConverter(L);
|
||||
luaDoStringOrThrow(L, "obj = ColorSource(0.5, 1, 0)", "converter_multi_registered setup");
|
||||
luaDoStringOrThrow(L, "function invoke_conv_multi() return sumVec3(obj) end", "converter_multi_registered closure setup");
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
(void) _;
|
||||
lua_getglobal(L, "invoke_conv_multi");
|
||||
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_conv_multi");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
} // 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");
|
||||
BENCHMARK(converter_exact_type_measure)->Name("converter_exact_type_measure");
|
||||
BENCHMARK(converter_phase3_value_measure)->Name("converter_phase3_value_measure");
|
||||
BENCHMARK(converter_phase3_ref_measure)->Name("converter_phase3_ref_measure");
|
||||
BENCHMARK(converter_multi_registered_measure)->Name("converter_multi_registered_measure");
|
||||
+618
@@ -0,0 +1,618 @@
|
||||
// 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 <sol/sol.hpp>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace lbsbench;
|
||||
|
||||
std::tuple<double, double> sol3_multi_return(double value)
|
||||
{
|
||||
return { value, value * 2.0 };
|
||||
}
|
||||
|
||||
void registerBasic(sol::state& lua)
|
||||
{
|
||||
lua.new_usertype<Basic>("c",
|
||||
sol::constructors<Basic()>(),
|
||||
"set", &Basic::set,
|
||||
"get", &Basic::get,
|
||||
"var", &Basic::var);
|
||||
}
|
||||
|
||||
void registerBasicLarge(sol::state& lua)
|
||||
{
|
||||
lua.new_usertype<BasicLarge>("cl",
|
||||
sol::constructors<BasicLarge()>(),
|
||||
"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<Basic>("c",
|
||||
sol::constructors<Basic()>(),
|
||||
"val", sol::property(&Basic::get, &Basic::set));
|
||||
}
|
||||
|
||||
void registerCounter(sol::state& lua)
|
||||
{
|
||||
lua.new_usertype<Counter>("Counter",
|
||||
sol::constructors<Counter()>(),
|
||||
"get", &Counter::get,
|
||||
"static_add", &Counter::static_add);
|
||||
}
|
||||
|
||||
void registerSharedObject(sol::state& lua)
|
||||
{
|
||||
lua.new_usertype<SharedObject>("SharedObject",
|
||||
sol::call_constructor,
|
||||
sol::factories([]() { return std::make_shared<SharedObject>(); }),
|
||||
"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<double>();
|
||||
}
|
||||
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<double>();
|
||||
}
|
||||
|
||||
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<double>();
|
||||
}
|
||||
|
||||
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<double>(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<double>(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<double>(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<double, double> values = f.call<double, double>(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>("ComplexBaseA",
|
||||
"a_func", &ComplexBaseA::a_func,
|
||||
"a", &ComplexBaseA::a);
|
||||
|
||||
lua.new_usertype<ComplexBaseB>("ComplexBaseB",
|
||||
"b_func", &ComplexBaseB::b_func,
|
||||
"b", &ComplexBaseB::b);
|
||||
|
||||
lua.new_usertype<ComplexAB>("ComplexAB",
|
||||
sol::base_classes, sol::bases<ComplexBaseA, ComplexBaseB>(),
|
||||
"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<double> 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<double> 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<double> 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>("ComplexBaseA",
|
||||
"a_func", &ComplexBaseA::a_func,
|
||||
"a", &ComplexBaseA::a);
|
||||
|
||||
lua.new_usertype<ComplexBaseB>("ComplexBaseB",
|
||||
"b_func", &ComplexBaseB::b_func,
|
||||
"b", &ComplexBaseB::b);
|
||||
|
||||
lua.new_usertype<ComplexAB>("ComplexAB",
|
||||
sol::constructors<ComplexAB()>(),
|
||||
sol::base_classes, sol::bases<ComplexBaseA, ComplexBaseB>(),
|
||||
"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>("ComplexBaseA",
|
||||
"a_func", &ComplexBaseA::a_func);
|
||||
|
||||
lua.new_usertype<ComplexAB>("ComplexAB",
|
||||
sol::constructors<ComplexAB()>(),
|
||||
sol::base_classes, sol::bases<ComplexBaseA>(),
|
||||
"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");
|
||||
+282
@@ -0,0 +1,282 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.patches as mpatches
|
||||
import numpy as np
|
||||
|
||||
|
||||
# ── Label helpers ─────────────────────────────────────────────────────────────
|
||||
|
||||
_SUFFIX = "_measure"
|
||||
|
||||
def _clean_label(name: str) -> str:
|
||||
if name.endswith(_SUFFIX):
|
||||
name = name[: -len(_SUFFIX)]
|
||||
return name.replace("_", " ")
|
||||
|
||||
|
||||
# ── JSON loading ──────────────────────────────────────────────────────────────
|
||||
|
||||
def infer_library_name(path: str) -> str:
|
||||
return Path(path).stem.replace("benchmark_", "")
|
||||
|
||||
|
||||
def load_google_benchmark_json(path: str, library_name: str) -> dict:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
|
||||
case_values: dict[str, float] = {}
|
||||
case_stddev: dict[str, float] = {}
|
||||
case_errors: dict[str, str] = {}
|
||||
|
||||
for entry in data.get("benchmarks", []):
|
||||
name = entry.get("name", "")
|
||||
run_type = entry.get("run_type", "")
|
||||
|
||||
# Prefer aggregate mean/stddev when available
|
||||
if run_type == "aggregate":
|
||||
base_name = entry.get("run_name", name)
|
||||
agg = entry.get("aggregate_name")
|
||||
t = entry.get("real_time", entry.get("cpu_time", 0.0))
|
||||
if agg == "mean":
|
||||
case_values[base_name] = t
|
||||
elif agg == "stddev":
|
||||
case_stddev[base_name] = t
|
||||
continue
|
||||
|
||||
if run_type not in ("iteration", ""):
|
||||
continue
|
||||
|
||||
if entry.get("error_occurred"):
|
||||
case_errors[name] = entry.get("error_message", "error")
|
||||
continue
|
||||
|
||||
if name not in case_values:
|
||||
case_values[name] = entry.get("real_time", entry.get("cpu_time", 0.0))
|
||||
|
||||
return {"library": library_name, "values": case_values, "stddev": case_stddev, "errors": case_errors}
|
||||
|
||||
|
||||
# ── Merge ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
def merge_results(result_sets):
|
||||
merged: dict[str, dict[str, float]] = defaultdict(dict)
|
||||
stddev: dict[str, dict[str, float]] = defaultdict(dict)
|
||||
errors: dict[str, dict[str, str]] = defaultdict(dict)
|
||||
|
||||
for result in result_sets:
|
||||
lib = result["library"]
|
||||
for case_name, value in result["values"].items():
|
||||
merged[case_name][lib] = value
|
||||
for case_name, sd in result.get("stddev", {}).items():
|
||||
stddev[case_name][lib] = sd
|
||||
for case_name, error in result["errors"].items():
|
||||
errors[case_name][lib] = error
|
||||
|
||||
return merged, stddev, errors
|
||||
|
||||
|
||||
# ── Plotting ──────────────────────────────────────────────────────────────────
|
||||
|
||||
# Dark theme colours
|
||||
_BG = "#1E1E2E" # figure / axes background
|
||||
_FG = "#CDD6F4" # text, ticks, labels
|
||||
_GRID = "#313244" # grid lines
|
||||
_SPINE = "#45475A" # axis spines
|
||||
_UNSUP = "#585B70" # "unsupported" text
|
||||
|
||||
# Bright palette suited for dark backgrounds
|
||||
_PALETTE = [
|
||||
"#89B4FA", # blue
|
||||
"#FAB387", # peach
|
||||
"#A6E3A1", # green
|
||||
"#F38BA8", # red
|
||||
"#CBA6F7", # mauve
|
||||
"#94E2D5", # teal
|
||||
"#F9E2AF", # yellow
|
||||
"#89DCEB", # sky
|
||||
]
|
||||
|
||||
_LIB_ORDER = ["LuaBridge3Benchmark", "LuaBridgeVanillaBenchmark", "Sol3Benchmark"]
|
||||
_LIB_ORDER_MAP = {lib: i for i, lib in enumerate(_LIB_ORDER)}
|
||||
|
||||
|
||||
def plot_grouped_bars(merged: dict, stddev: dict, errors: dict, output_file: str, log_scale: bool = False) -> None:
|
||||
case_names = sorted(merged.keys())
|
||||
all_libs = {lib for cases in merged.values() for lib in cases}
|
||||
libraries = sorted(all_libs, key=lambda l: _LIB_ORDER_MAP.get(l, len(_LIB_ORDER)))
|
||||
|
||||
if not case_names or not libraries:
|
||||
raise RuntimeError("No benchmark samples found to plot")
|
||||
|
||||
n_cases = len(case_names)
|
||||
n_libs = len(libraries)
|
||||
clean_labels = [_clean_label(cn) for cn in case_names]
|
||||
|
||||
# ── Layout ────────────────────────────────────────────────────────────────
|
||||
bar_h = 0.80
|
||||
group_h = bar_h / n_libs
|
||||
fig_h = max(10, n_cases * bar_h + 2)
|
||||
|
||||
with plt.rc_context({
|
||||
"text.color": _FG,
|
||||
"axes.labelcolor": _FG,
|
||||
"xtick.color": _FG,
|
||||
"ytick.color": _FG,
|
||||
}):
|
||||
fig, ax = plt.subplots(figsize=(14, fig_h))
|
||||
fig.patch.set_facecolor(_BG)
|
||||
ax.set_facecolor(_BG)
|
||||
|
||||
colors = {lib: _PALETTE[i % len(_PALETTE)] for i, lib in enumerate(libraries)}
|
||||
y_positions = np.arange(n_cases, dtype=float)
|
||||
|
||||
# Max value including error bars — used to size x-axis
|
||||
x_max_with_err = 1.0
|
||||
for cn in case_names:
|
||||
for lib in libraries:
|
||||
val = merged[cn].get(lib, float("nan"))
|
||||
if not np.isnan(val):
|
||||
sd = stddev.get(cn, {}).get(lib, 0.0) or 0.0
|
||||
x_max_with_err = max(x_max_with_err, val + sd)
|
||||
|
||||
for i, library in enumerate(libraries):
|
||||
values = [merged[cn].get(library, float("nan")) for cn in case_names]
|
||||
sds = [stddev.get(cn, {}).get(library, float("nan")) for cn in case_names]
|
||||
bar_y = y_positions + (i - (n_libs - 1) / 2.0) * group_h
|
||||
|
||||
xerr_vals = [sd if not np.isnan(sd) else 0.0 for sd in sds]
|
||||
has_errors = any(sd > 0 for sd in xerr_vals)
|
||||
|
||||
ax.barh(
|
||||
bar_y,
|
||||
values,
|
||||
height=group_h * 0.85,
|
||||
color=colors[library],
|
||||
label=library,
|
||||
xerr=xerr_vals if has_errors else None,
|
||||
error_kw={"ecolor": _FG, "capsize": 3, "elinewidth": 1.2, "capthick": 1.2},
|
||||
zorder=4,
|
||||
)
|
||||
|
||||
for y, val, sd in zip(bar_y, values, sds):
|
||||
if np.isnan(val):
|
||||
ax.text(
|
||||
0, y, " unsupported",
|
||||
va="center", ha="left",
|
||||
fontsize=10, color=_UNSUP, style="italic",
|
||||
zorder=5,
|
||||
)
|
||||
else:
|
||||
label = f" {val:.1f} ±{sd:.1f} ns" if not np.isnan(sd) and sd > 0 else f" {val:.1f} ns"
|
||||
ax.text(
|
||||
val, y, label,
|
||||
va="center", ha="left",
|
||||
fontsize=9, color=_FG,
|
||||
zorder=5,
|
||||
)
|
||||
|
||||
# ── Axes ──────────────────────────────────────────────────────────────
|
||||
ax.set_yticks(y_positions)
|
||||
ax.set_yticklabels(clean_labels, fontsize=12)
|
||||
|
||||
half_span = (n_libs - 1) / 2.0 * group_h + group_h * 0.425
|
||||
ax.set_ylim(-half_span, n_cases - 1 + half_span)
|
||||
ax.invert_yaxis()
|
||||
|
||||
if log_scale:
|
||||
ax.set_xscale("log")
|
||||
ax.set_xlabel("Time (ns, log scale)", fontsize=13)
|
||||
else:
|
||||
ax.set_xlabel("Time (ns)", fontsize=13)
|
||||
|
||||
ax.set_xlim(0, x_max_with_err * 1.20)
|
||||
ax.xaxis.grid(True, color=_GRID, linestyle="--", alpha=1.0, zorder=2)
|
||||
ax.set_axisbelow(True)
|
||||
for spine in ax.spines.values():
|
||||
spine.set_edgecolor(_SPINE)
|
||||
ax.spines["top"].set_visible(True)
|
||||
ax.spines["right"].set_visible(False)
|
||||
ax.xaxis.set_tick_params(which="both", top=True, bottom=True, labeltop=True, labelbottom=True)
|
||||
ax.tick_params(axis="x", which="both", color=_SPINE, labelsize=11)
|
||||
|
||||
# ── Legend & title ────────────────────────────────────────────────────
|
||||
legend_handles = [mpatches.Patch(color=colors[lib], label=lib) for lib in libraries]
|
||||
ax.legend(
|
||||
handles=legend_handles,
|
||||
loc="upper right",
|
||||
fontsize=11,
|
||||
framealpha=1.0,
|
||||
facecolor=_SPINE,
|
||||
edgecolor=_SPINE,
|
||||
labelcolor=_FG,
|
||||
)
|
||||
ax.set_title(
|
||||
"Lua Binding Benchmarks — lower is better (ns)",
|
||||
fontsize=16, pad=10, fontweight="bold", color=_FG,
|
||||
)
|
||||
|
||||
fig.subplots_adjust(left=0.22, right=0.97, top=0.97, bottom=0.03)
|
||||
plt.savefig(output_file, dpi=150, facecolor=fig.get_facecolor())
|
||||
plt.close()
|
||||
|
||||
# ── Text summary ──────────────────────────────────────────────────────────
|
||||
txt_file = Path(output_file).with_suffix(".txt")
|
||||
col_w = max(len(lib) for lib in libraries) + 2
|
||||
label_w = max(len(lbl) for lbl in clean_labels) + 2
|
||||
|
||||
with open(txt_file, "w", encoding="utf-8") as f:
|
||||
header = f"{'Benchmark':<{label_w}}" + "".join(f"{lib:>{col_w}}" for lib in libraries)
|
||||
f.write(header + "\n")
|
||||
f.write("-" * len(header) + "\n")
|
||||
for cn, lbl in zip(case_names, clean_labels):
|
||||
row = f"{lbl:<{label_w}}"
|
||||
for lib in libraries:
|
||||
val = merged[cn].get(lib)
|
||||
cell = f"{val:>{col_w - 3}.1f} ns" if val is not None else f"{'n/a':>{col_w}}"
|
||||
row += cell
|
||||
f.write(row + "\n")
|
||||
print(f"Saved: {txt_file}")
|
||||
|
||||
|
||||
# ── Entry point ───────────────────────────────────────────────────────────────
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Plot comparisons from Google Benchmark JSON files"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--input", nargs="+", required=True,
|
||||
help="One or more Google Benchmark JSON files"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output", default="Benchmarks/benchmark_comparison.png",
|
||||
help="Output PNG file"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log", action="store_true",
|
||||
help="Use a logarithmic x-axis"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
result_sets = [
|
||||
load_google_benchmark_json(path, infer_library_name(path))
|
||||
for path in args.input
|
||||
]
|
||||
|
||||
merged, stddev, errors = merge_results(result_sets)
|
||||
|
||||
Path(args.output).parent.mkdir(parents=True, exist_ok=True)
|
||||
plot_grouped_bars(merged, stddev, errors, args.output, log_scale=args.log)
|
||||
print(f"Saved: {args.output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,2 @@
|
||||
matplotlib
|
||||
numpy
|
||||
Reference in New Issue
Block a user