Skip to content
Open
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
38 changes: 34 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
cmake_minimum_required(VERSION 3.25)
project(pxkorka
VERSION 0.1.0
LANGUAGES CXX
LANGUAGES C CXX
)


set(CMAKE_CXX_STANDARD 26)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_EXTENSIONS ON)

# --- OPTIONS ---
option(ENABLE_TESTS "Build tests" ON)
Expand All @@ -16,7 +16,7 @@ option(ENABLE_TESTS "Build tests" ON)
if (MSVC)
add_compile_options(/W4 /WX /permissive-)
else ()
add_compile_options(-Wall -Wextra -Wpedantic -Werror -Wshadow -Wunused)
# add_compile_options(-Wall -Wextra -Wpedantic -Werror -Wshadow -Wunused)
endif ()

# Dependencies
Expand Down Expand Up @@ -88,4 +88,34 @@ target_compile_options(korka_lib PUBLIC -Wno-error=unused-but-set-variable -Wno-
# )
#
# catch_discover_tests(pxkorka_tests)
#endif ()
#endif ()


# --- BENCH ---
CPMAddPackage(
NAME lua
GIT_REPOSITORY https://github.com/lua/lua.git
VERSION 5.3.5
DOWNLOAD_ONLY YES
)

if (lua_ADDED)
# lua has no CMake support, so we create our own target

FILE(GLOB lua_sources ${lua_SOURCE_DIR}/*.c)
list(REMOVE_ITEM lua_sources "${lua_SOURCE_DIR}/lua.c" "${lua_SOURCE_DIR}/luac.c")
add_library(lua STATIC ${lua_sources})

target_include_directories(lua
PUBLIC
$<BUILD_INTERFACE:${lua_SOURCE_DIR}>
)
endif()


find_package(Python3 COMPONENTS Development REQUIRED)

add_executable(bench bench.cpp)
target_link_libraries(bench PUBLIC lua korka_lib)
target_link_libraries(bench PRIVATE Python3::Python)
target_include_directories(bench PRIVATE ${Python3_INCLUDE_DIRS})
153 changes: 120 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
<img width="64" src="./icon.svg" align="left" alt="KorkaVM icon">
<img width="64" src="media/icon.svg" align="left" alt="Korka icon">

# KorkaVM
# Korka

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

---

### What is this

KorkaVM is a project where I'm trying to create a tool that allows to embed logic
Korka is a project where I'm trying to create a tool that allows to embed logic
without runtime overhead of parsing or loading external files. You write C-like code
right inside C++, and the compiler transforms it into internal bytecode before your
program even starts.

### Status

| Component | Stage | Execution context |
|:----------------:|:-------:|:-----------------:|
| Lexer | Done | constexpr |
| Bytecode builder | Done | constexpr |
| Parser | Done | constexpr |
| Compiler | Partially done | constexpr |
| VM runner | Partially done | runtime |
| Component | Stage | Execution context |
|:----------------:|:-----:|:-----------------:|
| Lexer | Done | constexpr |
| Bytecode builder | Done | constexpr |
| Parser | Done | constexpr |
| Compiler | Done | constexpr |
| VM runner | Done | runtime |

### Features

+ **Full interop** between Korka & C++, you can bind a C++ function into the script and vice versa.
Type safety guaranteed.
+ **Fast VM**: in my `fib` benchmark Korka surpasses Lua and Python by 30-40%

What's done:
```cpp
constexpr char code[] = R"(
int main() {
Expand All @@ -40,7 +45,12 @@ int foo(int a, int b) {
}
)";

constexpr auto compile_result = korka::compile<code>();

constexpr auto bindings = korka::make_bindings(
korka::wrap<fib>("cpp_fib"),
korka::wrap<print_n>("print_n")
);
constexpr auto compile_result = korka::compile<code, &bindings>();

// Extracting function types from code
// It returns a pointer, bc you can't return a type ._.
Expand All @@ -51,39 +61,116 @@ auto foo_func = compile_result.function<"foo">();
static_assert(std::is_same_v<decltype(foo_func), long (*)(long, long)>);
```


## Example context
### Code example

```cpp
// Functions that are interoped into Korka
auto fib(std::int64_t n) -> std::int64_t {
if (n == 0) return 0;
if (n == 1) return 1;

return fib(n - 1) + fib(n - 2);
}

auto foo(int a) -> int {
return a * 2 + 5;
auto print_n(std::int64_t n) -> void {
std::cout << n << '\n';
}

// C-like code
constexpr char code[] = R"(
int fib(int n) {
if (n == 0) return 0;
if (n == 1) return 1;

return fib(n-1) + cpp_fib(n-2);
}

void print_fib(int n) {
int result = fib(n);

print_n(result);
return;
}
)";

constexpr auto bindings = korka::make_bindings(
"foo", &foo
korka::wrap<fib>("cpp_fib"),
korka::wrap<print_n>("print_n")
);

constexpr auto my_script = korka::compile(bindings, R"(
int calculate(int x) {
if (x <= 0) return 0;
constexpr auto compile_result = korka::compile<code, &bindings>();

return foo(x) / 3;
}
)");
constexpr auto script_fib = compile_result.function<"fib">();
constexpr auto script_print_fib = compile_result.function<"print_fib">();

// Simple usage
int main() {
korka::runtime vm;
int result = vm.execute(my_script)
korka::vm::context ctx{
compile_result.bytes, bindings
};

// result will have int64_t type
auto result = ctx.call(script_fib, 12L);
std::cout << result << '\n';

ctx.call(script_print_fib, 16L);
}
```

// Not so simple usage
int main() {
korka::runtime vm;

// Byte code gets inserted right into the native instruction flow
// and executed by vm right there
korka::run_embed<my_script>(vm);
## Benchmark

See the [bench.cpp](https://github.com/PyXiion/pxkorka/blob/master/bench.cpp) for full code.


<details>

<summary>Code</summary>

---

### Python code

```python
def fib(n):
if n == 0: return 0
if n == 1: return 1
return fib(n - 1) + fib(n - 2)
```

### Lua code

```lua
function fib(n)
if n == 0 then return 0 end
if n == 1 then return 1 end
return fib(n-1) + fib(n-2)
end
```

### C code (Korka)

```c
int fib(int n) {
if (n==0) return 0;
if (n==1) return 1;
return fib(n-1) + fib(n-2);
}
```

---

</details>

### Results:

| n | Iterations | Korka (ms) | Lua (ms) | Python (ms) | Korka Speedup (vs. Python) |
|---:|------------:|-----------:|----------:|------------:|:--------------------------:|
| 10 | 2,000,000 | 6,503.03 | 9,686.56 | 8,988.40 | 1.38x |
| 15 | 200,000 | 7,113.14 | 10,899.20 | 9,996.17 | 1.41x |
| 20 | 20,000 | 8,546.09 | 11,765.60 | 10,961.60 | 1.28x |
| 23 | 4,500 | 7,964.48 | 11,491.50 | 10,667.10 | 1.34x |
| 25 | 2,000 | 8,900.08 | 12,980.40 | 12,277.30 | 1.38x |
| 28 | 400 | 7,506.88 | 11,101.50 | 10,820.10 | 1.44x |
| 30 | 200 | 9,910.64 | 14,334.40 | 13,697.50 | 1.38x |

Cool graph here:
![Execution time graph](img.png)
Loading
Loading