VM.
## So what do we have?
Let's look how the API of my poor lib looks (let's call it Korka).
This example uses bindings (100% type safe, I swear on the standard):
```cpp
// Our native C++ functions
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 print_n(std::int64_t n) -> void {
std::cout << n << '\n';
}
// Our not-so-native script
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;
}
)";
// We create bindings
constexpr auto bindings = korka::make_bindings(
korka::wrap<fib>("cpp_fib"),
korka::wrap<print_n>("print_n")
);
// Compile at compile-time, yay
constexpr auto compile_result = korka::compile<code, &bindings>();
// Extract function adressess + their types
constexpr auto script_fib = compile_result.function<"fib">();
constexpr auto script_print_fib = compile_result.function<"print_fib">();
int main() {
// Init VM
korka::vm::context ctx{compile_result.bytes, bindings};
// Call fib that returns int64_t
auto result = ctx.call(script_fib, 12L);
std::cout << "fib(12) = " << result << '\n'; // prints 144
// Call print_fib
ctx.call(script_print_fib, 16L); // prints 987
return 0;
}
```
Ta da! It works.
## Small analysis
Out of curiosity, I decided to compare Korka with other scripting languages. A pretty API is great, sure, but was it worth the effort performance-wise? So, let's pit Korka head-to-head against Python and Lua.
For the benchmark, I used the recursive calculation of the $N$-th Fibonacci number, an excellent test to fairly evaluate overhead on function calls, stack management, and overall runtime efficiency (the first thing that came to my head).
I tested everything on a franken-server put together from spare parts, powered by an Intel Xeon E5-2689 (3.6 GHz).
I measured two stages:
- **Initialization time** from runtime startup to being ready to execute the first instruction,
- **Execution time** of the algorithm itself.
### Stage 1: Initialization
|**Language / Library**|**Initialization time**|
|---|---|
|**Korka**|**1.5 µs**|
|**Lua**|152.6 µs|
|**Python**|25,097.0 µs|
Korka takes the lead: it starts 100 times faster than Lua and over 15,000 times faster than Python.
The explanation is simple: while Lua and Python are busy reading the script at startup, parsing it, compiling it into their byte-codes, and spinning up heavy infrastructure (including the GC), Korka does not. All the virtual machine has to do is grab the pre-compiled output (and allocate a tiny bit of memory).
### Stage 2: Runtime
After a series of optimizations, the results turned out pretty solid (I know comparing statically typed and dynamic languages isn't entirely fair, but who's gonna stop me?):
| **N** | **Iterations** | **Korka (ms)** | **Lua (ms)** | **Python (ms)** | **vs. Python** |
| ------ | -------------- | -------------- | ------------ | --------------- | -------------- |
| **10** | 100 000 | **1 055,67** | 1 534,98 | 1 591,10 | **1,51x** |
| **15** | 50 000 | **5 492,40** | 8 149,17 | 8 753,77 | **1,59x** |
| **20** | 20 000 | **24 263,39** | 36 268,86 | 38 459,88 | **1,59x** |
| **23** | 10 000 | **51 367,20** | 76 494,22 | 82 257,71 | **1,60x** |
| **25** | 5 000 | **67 441,69** | 100 850,20 | 108 836,54 | **1,61x** |
| **28** | 2 000 | **114 341,21** | 169 626,06 | 184 869,57 | **1,62x** |
| **30** | 1 000 | **149 190,02** | 223 229,86 | 241 838,28 | **1,62x** |
### Conclusion
I built a (mostly) full-fledged C compiler that runs entirely in `constexpr`. Why? No idea. Especially considering it's been done before, you can check out [constexpr-8cc](https://github.com/keiichiw/constexpr-8cc). But that one lacks C++ bindings and cross-platform support.
The source code is
Post #25692
12