Skip to content
Closed
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
41 changes: 41 additions & 0 deletions .github/workflows/codspeed.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CodSpeed

on:
push:
branches:
- "master"
pull_request:
workflow_dispatch:

permissions:
contents: read
id-token: write

jobs:
codspeed:
name: Run benchmarks
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- name: Install GCC 14
run: |
sudo apt-get update
sudo apt-get install -y g++-14

- name: Build benchmarks
run: |
cmake -B build \
-DCMAKE_C_COMPILER=gcc-14 \
-DCMAKE_CXX_COMPILER=g++-14 \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DENABLE_TESTS=OFF \
-DENABLE_BENCHMARKS=ON \
-DCODSPEED_MODE=simulation
cmake --build build --config RelWithDebInfo -j

- name: Run benchmarks
uses: CodSpeedHQ/action@v4
with:
mode: simulation
run: ./build/pxkorka_benchmarks
42 changes: 38 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ set(CMAKE_CXX_EXTENSIONS OFF)

# --- OPTIONS ---
option(ENABLE_TESTS "Build tests" ON)
option(ENABLE_BENCHMARKS "Build benchmarks" OFF)

# Enable all warnings
# Strict warning flags for project targets only (not third-party dependencies)
if (MSVC)
add_compile_options(/W4 /WX /permissive-)
set(PROJECT_WARNING_FLAGS /W4 /WX /permissive-)
else ()
add_compile_options(-Wall -Wextra -Wpedantic -Werror -Wshadow -Wunused)
set(PROJECT_WARNING_FLAGS -Wall -Wextra -Wpedantic -Werror -Wshadow -Wunused)
endif ()

# Dependencies
Expand All @@ -29,6 +30,19 @@ if (ENABLE_TESTS)

endif ()

if (ENABLE_BENCHMARKS)
set(BENCHMARK_DOWNLOAD_DEPENDENCIES ON)

include(FetchContent)
FetchContent_Declare(
google_benchmark
GIT_REPOSITORY https://github.com/CodSpeedHQ/codspeed-cpp
SOURCE_SUBDIR google_benchmark
GIT_TAG main
)
FetchContent_MakeAvailable(google_benchmark)
endif ()

add_library(korka_lib
include/korka/vm/vm_runtime.hpp src/vm/vm_runtime.cpp
include/korka/vm/op_codes.hpp
Expand All @@ -44,6 +58,8 @@ add_library(korka_lib
include/korka/compiler/compiler.hpp
)

target_compile_options(korka_lib PRIVATE ${PROJECT_WARNING_FLAGS})

target_include_directories(korka_lib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
Expand All @@ -55,6 +71,7 @@ target_include_directories(korka_lib

add_executable(pxkorka main.cpp)
target_link_libraries(pxkorka PRIVATE korka_lib)
target_compile_options(pxkorka PRIVATE ${PROJECT_WARNING_FLAGS})

# --- TESTS ---
if (ENABLE_TESTS)
Expand All @@ -72,5 +89,22 @@ if (ENABLE_TESTS)
Catch2WithMain
)

target_compile_options(pxkorka_tests PRIVATE ${PROJECT_WARNING_FLAGS})

catch_discover_tests(pxkorka_tests)
endif ()
endif ()

# --- BENCHMARKS ---
if (ENABLE_BENCHMARKS)
add_executable(pxkorka_benchmarks
bench/lexer_bench.cpp
bench/parser_bench.cpp
bench/bytecode_builder_bench.cpp
)

target_link_libraries(pxkorka_benchmarks
PRIVATE
korka_lib
benchmark::benchmark
)
endif ()
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

A Virtual Machine where lexing and compilation happen entirely at **compile-time**.

[![CodSpeed](https://img.shields.io/endpoint?url=https://codspeed.io/badge/github/PyXiion/pxkorka)](https://codspeed.io/PyXiion/pxkorka?utm_source=badge)

---

### What is this
Expand Down
58 changes: 58 additions & 0 deletions bench/bytecode_builder_bench.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include <benchmark/benchmark.h>
#include "korka/vm/bytecode_builder.hpp"
#include "korka/vm/op_codes.hpp"
#include "korka/vm/options.hpp"

static void BM_EmitArithmeticOps(benchmark::State &state) {
for (auto _ : state) {
korka::vm::bytecode_builder b{};
b.emit_add(0, 1, 2);
b.emit_sub(3, 4, 5);
b.emit_mul(6, 7, 8);
b.emit_div(9, 10, 11);
auto bytes = b.build();
benchmark::DoNotOptimize(bytes);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_EmitArithmeticOps);

static void BM_EmitJumps(benchmark::State &state) {
for (auto _ : state) {
korka::vm::bytecode_builder b{};
auto target = b.make_label();
b.emit_jmp(target);
b.emit_add(0, 1, 2);
b.bind(target);
b.emit_add(3, 4, 5);
auto bytes = b.build();
benchmark::DoNotOptimize(bytes);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_EmitJumps);

static void BM_BuildComplexBytecode(benchmark::State &state) {
for (auto _ : state) {
korka::vm::bytecode_builder b{};

auto loop_start = b.make_label();
auto loop_end = b.make_label();

b.emit_load_imm(0, korka::vm::stack_value_t{0});
b.emit_load_imm(1, korka::vm::stack_value_t{100});
b.emit_load_imm(2, korka::vm::stack_value_t{1});

b.bind(loop_start);
b.emit_cmp_lt(3, 0, 1);
b.emit_jmp_if(loop_end, 3);
b.emit_add(0, 0, 2);
b.emit_jmp(loop_start);

b.bind(loop_end);
auto bytes = b.build();
benchmark::DoNotOptimize(bytes);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_BuildComplexBytecode);
70 changes: 70 additions & 0 deletions bench/lexer_bench.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include <benchmark/benchmark.h>
#include "korka/compiler/lexer.hpp"

static const char *simple_program =
"int main() {\n"
" return 0;\n"
"}\n";

static const char *medium_program =
"int main() {\n"
" int i = 0;\n"
" while (i < 100) {\n"
" i = i + 1;\n"
" }\n"
" return i;\n"
"}\n";

static const char *complex_program =
"int fibonacci(int n) {\n"
" if (n <= 1) return n;\n"
" return fibonacci(n - 1) + fibonacci(n - 2);\n"
"}\n"
"\n"
"int sum(int a, int b) {\n"
" return a + b;\n"
"}\n"
"\n"
"int main() {\n"
" int x = 10;\n"
" int y = 20;\n"
" int z = sum(x, y);\n"
" int fib = fibonacci(z);\n"
" if (fib > 1000) {\n"
" return 1;\n"
" } else {\n"
" return 0;\n"
" }\n"
"}\n";

static void BM_LexSimpleProgram(benchmark::State &state) {
for (auto _ : state) {
korka::lexer lexer{simple_program};
auto tokens = lexer.lex();
benchmark::DoNotOptimize(tokens);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_LexSimpleProgram);

static void BM_LexMediumProgram(benchmark::State &state) {
for (auto _ : state) {
korka::lexer lexer{medium_program};
auto tokens = lexer.lex();
benchmark::DoNotOptimize(tokens);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_LexMediumProgram);

static void BM_LexComplexProgram(benchmark::State &state) {
for (auto _ : state) {
korka::lexer lexer{complex_program};
auto tokens = lexer.lex();
benchmark::DoNotOptimize(tokens);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_LexComplexProgram);

BENCHMARK_MAIN();
83 changes: 83 additions & 0 deletions bench/parser_bench.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#include <benchmark/benchmark.h>
#include "korka/compiler/lexer.hpp"
#include "korka/compiler/parser.hpp"

static const char *simple_program =
"int main() {\n"
" return 0;\n"
"}\n";

static const char *medium_program =
"int main() {\n"
" int i = 0;\n"
" while (i < 100) {\n"
" i = i + 1;\n"
" }\n"
" return i;\n"
"}\n";

static const char *complex_program =
"int fibonacci(int n) {\n"
" if (n <= 1) return n;\n"
" return fibonacci(n - 1) + fibonacci(n - 2);\n"
"}\n"
"\n"
"int sum(int a, int b) {\n"
" return a + b;\n"
"}\n"
"\n"
"int main() {\n"
" int x = 10;\n"
" int y = 20;\n"
" int z = sum(x, y);\n"
" int fib = fibonacci(z);\n"
" if (fib > 1000) {\n"
" return 1;\n"
" } else {\n"
" return 0;\n"
" }\n"
"}\n";

static void BM_ParseSimpleProgram(benchmark::State &state) {
auto tokens = korka::lexer{simple_program}.lex();
for (auto _ : state) {
korka::parser parser{tokens.value()};
auto result = parser.parse();
benchmark::DoNotOptimize(result);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_ParseSimpleProgram);

static void BM_ParseMediumProgram(benchmark::State &state) {
auto tokens = korka::lexer{medium_program}.lex();
for (auto _ : state) {
korka::parser parser{tokens.value()};
auto result = parser.parse();
benchmark::DoNotOptimize(result);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_ParseMediumProgram);

static void BM_ParseComplexProgram(benchmark::State &state) {
auto tokens = korka::lexer{complex_program}.lex();
for (auto _ : state) {
korka::parser parser{tokens.value()};
auto result = parser.parse();
benchmark::DoNotOptimize(result);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_ParseComplexProgram);

static void BM_LexAndParseComplexProgram(benchmark::State &state) {
for (auto _ : state) {
auto tokens = korka::lexer{complex_program}.lex();
korka::parser parser{tokens.value()};
auto result = parser.parse();
benchmark::DoNotOptimize(result);
benchmark::ClobberMemory();
}
}
BENCHMARK(BM_LexAndParseComplexProgram);
Loading