Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
403 changes: 0 additions & 403 deletions Benchmarks/Benchmark.cpp

This file was deleted.

103 changes: 84 additions & 19 deletions Benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,103 @@ cmake_minimum_required(VERSION 3.10)

include(FetchContent)

set(LUABRIDGE_BENCHMARK_WITH_SOL2 OFF CACHE BOOL "Whether to benchmark against sol2")
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_SOURCES
Benchmark.cpp
../Tests/Lua/LuaLibrary5.4.8.cpp)
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")

add_executable(LuaBridgeBenchmarks ${LUABRIDGE_BENCHMARK_SOURCES})
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")

target_include_directories(LuaBridgeBenchmarks PRIVATE
${CMAKE_CURRENT_LIST_DIR}/..
${CMAKE_CURRENT_LIST_DIR}/../Source
${CMAKE_CURRENT_LIST_DIR}/../Tests
${CMAKE_CURRENT_LIST_DIR}/../Tests/Lua/Lua.5.4.8/src)
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)

target_compile_definitions(LuaBridgeBenchmarks PRIVATE
LUABRIDGE_BENCHMARK_LUA54=1
LUABRIDGE_TEST_LUA_VERSION=504)

if (LUABRIDGE_BENCHMARK_WITH_SOL2)
if (LUABRIDGE_BENCHMARK_WITH_SOL3)
FetchContent_Declare(
sol2
GIT_REPOSITORY ${LUABRIDGE_SOL2_GIT_REPOSITORY}
GIT_TAG ${LUABRIDGE_SOL2_GIT_TAG})
FetchContent_MakeAvailable(sol2)
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()

target_include_directories(LuaBridgeBenchmarks PRIVATE
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(LuaBridgeBenchmarks PRIVATE
LUABRIDGE_BENCHMARK_WITH_SOL2=1
target_compile_definitions(Sol3Benchmark PRIVATE
SOL_ALL_SAFETIES_ON=1
SOL_NO_EXCEPTIONS=1
SOL_LUA_VERSION=504)
endif()
84 changes: 54 additions & 30 deletions Benchmarks/README.md
Original file line number Diff line number Diff line change
@@ -1,60 +1,84 @@
# LuaBridge Benchmarks
# Lua Binding Benchmarks

This directory contains a standalone benchmark executable focused on LuaBridge call and property access overhead. The selected cases mirror the style of the sol2 benchmark categories (Lua -> C++ calls, C++ -> Lua calls, member calls, and property access).
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 --target LuaBridgeBenchmarks --config Release
cmake --build Build --config Release --target LuaBridge3Benchmark LuaBridgeVanillaBenchmark
```

## Run
To also build Sol3 benchmark target:

```bash
./Build/Benchmarks/LuaBridgeBenchmarks
# optional iterations
./Build/Benchmarks/LuaBridgeBenchmarks 5000000
cmake -S . -B Build -DCMAKE_BUILD_TYPE=Release -DLUABRIDGE_BENCHMARKS=ON -DLUABRIDGE_BENCHMARK_WITH_SOL3=ON
cmake --build Build --config Release --target Sol3Benchmark
```

The executable prints:
- `ns/op` (lower is better)
- `Mop/s` (higher is better)
## Dependency Sources (FetchContent)

Defaults:

## Optional sol2 Side-by-Side
- 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`)

To compare with sol2 in the same executable, enable the option below. The build will download sol2 through CMake FetchContent:
You can override these at configure time:

```bash
cmake -S . -B Build \
-DCMAKE_BUILD_TYPE=Release \
-DLUABRIDGE_BENCHMARKS=ON \
-DLUABRIDGE_BENCHMARK_WITH_SOL2=ON
cmake --build Build --target LuaBridgeBenchmarks --config Release
-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
```

By default, FetchContent uses:
- Repository: `https://github.com/ThePhD/sol2.git`
- Tag: `v3.3.0`
## Run Benchmarks

You can override these if needed:
Each executable supports standard Google Benchmark CLI flags.

```bash
cmake -S . -B Build \
-DCMAKE_BUILD_TYPE=Release \
-DLUABRIDGE_BENCHMARKS=ON \
-DLUABRIDGE_BENCHMARK_WITH_SOL2=ON \
-DLUABRIDGE_SOL2_GIT_REPOSITORY=https://github.com/ThePhD/sol2.git \
-DLUABRIDGE_SOL2_GIT_TAG=v3.3.0
./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
```

When enabled, the benchmark prints LuaBridge and sol2 rows for the same cases and iteration count.
## 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 for Fair Comparison
## Notes

- Use Release mode and the same compiler for all libraries.
- Pin CPU frequency/governor where possible and avoid background load.
- Run each benchmark multiple times and compare medians.
- Keep Lua version identical between LuaBridge and sol2 runs.
- 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.
31 changes: 31 additions & 0 deletions Benchmarks/benchmark_common.cpp
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading