Upgrade to Pro — share decks privately, control downloads, hide ads and more …

mruby on C#: From VM Implementation to Game (Ru...

mruby on C#: From VM Implementation to Game (RubyKaigi 2026)Scripting

RubyKaigi 2026 Day1
https://rubykaigi.org/2026/presentations/hadashiA.html

What if mruby could run anywhere—without any C compilation? I built MRubyCS, a complete mruby VM implementation in pure C#. This makes embedding mruby in cross-platform applications far easier. Cross-platform distribution means juggling build targets, static/dynamic linking, and diverse host machines—a constant hassle (iOS, Web, Win, Mac, consoles, etc.). And on closed platforms like game consoles (Ps, Switch, Xbox..), it's often impractical without SDK access—now it just works. Unity and .NET already handle platform abstraction for you. This also opens new doors for the Ruby community. Game development has long been a space where Ruby has limited presence. With mruby running in Unity, Ruby developers can bring their language into one of the world's most popular game engines—for scripting game logic, building tools, and more. Beyond the VM implementation story, I'll show practical game scripting with VitalRouter.MRuby and Fiber-based coroutines!

Avatar for hadashiA

hadashiA

April 22, 2026

Other Decks in Programming

Transcript

  1. mruby is the lightweight implementation of the Ruby language can

    be linked and embedded within your application.
  2. mruby is the lightweight implementation of the Ruby language can

    be linked and embedded within your application.
  3. Application embedded Embedding scripts into standard applications with sufficient resources

    Application Language Embedded Script Langage k6 Go Js WezTerm Rust Lua NeoVim C Lua Blender C/C++ Python Github Actions C# YAML Godot C++ GDScript (original) Nix C++ Nix lang (original) Terraform Go HCL (original)
  4. Application embedded Embedding scripts into standard applications with sufficient resources

    Application Language Embedded Script Langage k6 Go Js WezTerm Rust Lua NeoVim C Lua Blender C/C++ Python Github Actions C# YAML Godot C++ GDScript (original) Nix C++ Nix lang (original) Terraform Go HCL (original) Where is mruby?
  5. In fact, Ruby excels at DSLs for configurations / sequences

    E.g. Vagrant E.g. Fastlane E.g. Homebrew
  6. App embedded Vagrant — Single language app Weztern — 2-layer

    language app Lua Rust Ruby Configuration Configuration Core features Core features
  7. ٱอాཽࣗ/ @hadashiA • OSS Developer (C#/Unity) • Experience working as

    an architect for large-scale online games / web services • Interested in performance optimization About Speaker
  8. As an OSS author hadashiA/VContainer ˒2.8k The extra fast, minimum

    code size, GC-free DI (Dependency Injection) library running on Unity Game Engine. hadashiA/VYaml˒450 The extra fast, low memory footprint YAML library for C#, focued on .NET and Unity. hadashiA/VitalRouter˒350 A fast, zero-allocation, in-memory messaging library. Declarative async pipeline with source generator for Unity and .NET. hadashiA/DryDB ˒100 An ultra fast read-only embedded B+Tree based key/ value database, implemented pure C#. ͏͍ͬ͢ hadashiA/Unio ˒200 Unio (short for unity native I/O) is a small utility set of I/O using native memory areas.
  9. As an Contributor/Committer (2024) Cysharp/UniTask ˒10.7k Provides an efficient allocation

    free async/await integration for Unity. Cysharp/ZLogger ˒1.6k Zero Allocation Text/Structured Logger for .NET with StringInterpolation and Source Generator, built on top of a Microsoft.Extensions.Logging. Cysharp/MemoryPack ˒4.4k Zero encoding extreme performance binary serializer for C# and Unity. Cysharp/R3 ˒3.7k The new future of dotnet/reactive and UniRx. ΋ͬ͢΋ͬ͢ Cysharp/Ulid ˒1.6k Fast .NET C# Implementation of ULID for .NET and Unity.
  10. Game development is 2-layer •Web services — Users generate content

    dynamically at runtime. •Games — Massive amounts of content are authored upfront and shipped with the product.
  11. Game development is 2-layer.. The primary focus is on "data

    work," which is not hard-coded into the application system. • Assets w ֆɺԻʜ • Spatial configuration • What exists where in the scene • What happens when player interact with it • Temporal configuration • Cutscene • Character performance • Scenario • Emergent property • Skill effect / Item effect / etc … https://unityroom.com/games/hibana
  12. Game development is 2-layer.. • Without affecting the game system

    • Can be operated by non-programmers • Allows for focusing on content “Data work” is hard
  13. Game development is 2-layer.. • GUI • Timeline Editor •

    Graph Node Editor • Scene Editor • Database / Data sheet / Spreadsheet • Hot-reloadable, Human-readable Text • YAML / TOML / cue / pkl • Bytecode machine “Data work” is hard
  14. “Data work” patterns • GUI • Timeline Editor • Graph

    Node Editor • Scene Editor • Database / Data sheet / Spreadsheet • Hot-reloadable, Human-readable Text • YAML / TOML / cue / pkl • Bytecode machine In some cases, developing internal GUI tools is part of the project itself. Actually, it saves so much effort in tool development! Text is super writer-friendly!
  15. Hot-reloadable, Human-readable scripting The primary focus is on "data work,"

    which is not hard-coded into the application system.
  16. Hot-reloadable, Human-readable scripting The primary focus is on "data work,"

    which is not hard-coded into the application system.
  17. In reality.. 1. Binding boilerplate — C ABI embedding requires

    extensive bindings 2. Portability burden — Separate mruby build needed per target 3. Async friction — Non-trivial integration with async runtimes Using mruby for application embedding involves a high hurdle..
  18. The application is not written in C.. • Apps are

    written in managed languages that hide platform differences • mruby requires a build step • mruby requires C FFI • And Integrate mruby GC managed memory. • For native binaries, modern devs reach for Rust/Zig by default • Their native types aren't C ABI compatible
  19. The application is not written in C.. libmruby.dylib libmruby.dll libmruby.dll

    libmruby.so (x64/arm64) libmruby.dylib (X64/arm64) libmruby.so libmruby.dll In the case of Unity wasm
  20. The real value of mruby lies in making custom builds.

    mruby has a simple build system that is independent of the language toolchain. We need to understand the C build settings for the target platform.
  21. The application is not written in C.. C FFI is

    possible, but writing code that crosses the boundary is cumbersome. Only blittable types can cross the boundary. Risks of memory leaks and segmentation faults when crossing language boundaries.
  22. And.. client-side programming = asynchronous Programming in "per-frame" steps Like

    differentiation (ඍ෼) We want to programming a more abstract sequence! Like Integration (ੵ෼) 00:00 (0f) 00:01 (60f) 00:02 (120f) 00:03 (180f) 00:04 (240f) 00:05 (300f) © Nintendo / ͋ͭ·ΕͲ͏Ϳͭͷ৿ ↓ ↓ ↓ ↓ ↓
  23. And.. client-side programming = asynchronous 2.Display text with a typewriter

    effect 3. Suspend 4. Wait for button click 5. Resume 7. Display next text.. 1. 6.
  24. 1. Portability burden — Separate mruby build needed per target

    2. Binding boilerplate — C ABI embedding requires extensive bindings 3. Async friction — Non-trivial integration with async runtimes
  25. A. One of the answers: Re-implementing the mruby VM in

    a cross-platform, managed, with async ecosystem language
  26. Reimplementing virtual machines in managed languages is common • Lua

    runtime written in C# • nuskey8/Lua-CSharp • MoonSharp • KopiLua • UniLua • Js runtime written in C# • akeit0/okojo • Jint
  27. mruby architecture Compile on the host machine, and deploy to

    the target bytecode machine. mruby (original) mruby/c mruby/edge mruby-compiler (original) picoruby/mruby-compiler2
  28. hadashiA/MRubyCS • Pure C# mruby byte-code machine + picoruby/mruby-compiler2 C#

    bindings. • Designed for seamless integration with C# game engines. • Easily embed Ruby into Unity/.NET
  29. Applications are not written in C.. $ dotnet add MRubyCS

    mruby runs without the need for a custom build. In the case of Unity
  30. The state of C# / .NET • Modern server runtime

    • Linux first • Build OCI container images directly • Top-level low latency and high throughput • Mature Logging / Tracing / Configuration / Testing • Equipped with HTTP/1, 2, and 3 • Cross-platform SDK for Linux, macOS, and Windows • Games and GUI • NativeAOT is becoming practical for real-world use. • Fast compilation • Highest precision static analysis, code fix, and decompiler High-througput, low-latency modern runtime https://github.com/LesnyRumcajs/grpc_bench
  31. hadashiA/MRubyCS • High-throughput GC already built into .C# is available

    for use. • “Exceptions” also exist in C#. • A highly developed async ecosystem is available. • An integrated, all-in-one toolchain Advantage of C# implementation
  32. How do we get people to trust alternative VM implementations?

    1. Compatibility 2. Performance 3. A guide on how to application design
  33. How do we get people to trust alternative VM implementations?

    1. Compatibility 2. Performance 3. A guide on how to application design
  34. 1. Compatibility MRubyCS prioritizes Ruby-level compatibility with mruby •Control-flow •Block

    / Proc • loop / raise / rescue/ ensure / yield / retry / redo / return / break •Built-in classes •Array, Float, Hash, Integer, Nil, Range, Symbol, String, Class, Module, Proc •Time, Random •Fiber •Type, module system •subclass / include / prepend / extend / singleton-class (ಛҟΫϥε) / Class.new / Module.new •instance_eval / class_eval •Method •Keyword arguments / Rest arguments •public / private / protected
  35. 1. Compatibility Strategies for achieving compatibility •The tests for mruby/mruby

    are written in Ruby. •Therefore, •The first goal as getting the simple test framework of mruby/mruby to work. •Next, port the tests and pass the test cases one by one. •Currently, more than 4,200 test cases are passing.
  36. How do we get people to trust alternative VM implementations?

    1. Compatibility 2. Performance 3. A guide on how to application design
  37. 2. Performance MRubyCS is optimized for execution speed •C# VM

    implementations have pros and cons compared to C versions. •C# advantage •Direct Read/Write to C# Struct Memory Layout •Roslyn Compiler Pipeline •Leveraging .NET JIT Tiered Compilation •C advantage •Opcode dispatch via unsafe direct jumps •1-word mrb_value •Unsafe pointer by default
  38. 2. Performance C#’s struct Like Swift(,Rust), C# distinguishes value and

    reference types at the type level. C# value types layout members directly. (With no header, no heap allocation, no write-barrier.)
  39. 2. Performance C#’s struct Reference types are heap-allocated and store

    pointers. Like Swift(,Rust), C# distinguishes value and reference types at the type level.
  40. 2. Performance The unit of data within the mruby VM

    world is the mruby value. The size of mrb_value in mruby's default configuration (WORD_BOXING) is 8 bytes. https://github.com/mruby/mruby/blob/master/doc/internal/boxing.md
  41. 2. Performance C#’s MRubyValue (take 1) — Word boxing =

    8bytes 64bit word boxing impementation
  42. 2. Performance C#’s MRubyValue (take 1) — Word boxing =

    8bytes ! A value within the .NET GC heap's address range but not a real address will crash.
  43. 2. Performance Presym •Ruby symbols are internally unsigned 32-bit integers.

    •Symbols in source code become integers during parsing. •mruby’s “presym" •Precalculates integer values for well-known symbols (e.g., `:=`, `+`, `-`). •C macros compile symbol names into integers.
  44. 2. Performance Directly jump to the address specified by the

    label!!!!!!!! Some advantages of using C
  45. 2. Performance Almost all languages supporting arrays or sequences insert

    checks for language-specific errors •Modern programming languages have "slices," which refer to a part of a contiguous memory region. •C# - Span<T> •Go - Slice •Rust - Slice •Swift - Span<T> •…Yeah. We can read the range of memory with zero-copy, but…, it still performs bounds checks.
  46. 2. Performance Almost all languages supporting arrays or sequences insert

    checks for language-specific errors 10 < Length Throw exception if overflow C# x64 (no optimized)
  47. 2. Performance Read operand — naive implementation If the compiler

    cannot be certain, boundary checks (branches) will be inserted into everything!!!!!!! See: ʲ.NETʳڥքνΣοΫ͕ফ͑Δύλʔϯू
  48. 2. Performance Read operand — optimized implementation Match C# type

    layout to the operand Direct copy (Use “manned pointer” instead of “unmanaged pointer”)
  49. 2. Performance C#'s tiered-compilation •Bounds-check, Overflow-check elimination •Inlining •Method de-virtualization

    •Loop optimizations •Tail-recursion removal •Code layout in memory to optimize processor caches •…and many more
  50. 2. Performance The topic of JIT optimization budgets • Checking

    the assembly, MRubyCS's initial main loop wasn't optimized at all. • Why??
  51. 2. Performance • JIT does not spend an infinite amount

    of time on optimization. • The budget for optimization is determined to some extent based on heuristics. • The main loop of MRubyCS is very large, and the JIT exhausts its optimization budget before it can be sufficiently optimized…………. The topic of JIT optimization budgets
  52. 2. Performance • Reduce the IL size of the main

    loop. • → Extract cold paths into separate methods and intentionally mark them with [NoInlining]. • → Steadily reduce the number of local variables. • → Stop catching arithmetic overflows with granular try/catch blocks. The topic of JIT optimization budgets Optimize by not optimizing
  53. 2. Performance Reduce IL code size results: Metric Before Optimization

    After Optimization Change Stack frame size 1,608 bytes 856 bytes -47% Number of `call` instructions 507 285 -44% MRubyValue accessor calls 105 10 -90% CORINFO_HELP_OVERFLOW 4 0 -100% Number of funclets 8 1 -88% MRrubyValue ctor calls 7 0 -100%
  54. 2. Performance MRubyCS benchmark results v0.61.3 • It's slightly faster

    than original mruby, or at least on par. • Still room for research, of course. • JIT via C# IL Emit should also be possible.
  55. hadashiA/MRubyCS How do we get people to trust alternative VM

    implementations? 1. Compatibility 2. Performance 3. A guide on how to application design
  56. 3. Application design However, it's still not enough….. •We can

    do everything, which is the problem.. •To begin with, game and client-side programming is characterized by extremely complex control flow.
  57. 3. Application design Game development tends to get messy. •An

    event triggers separate events in distant locations, and they form a chain reaction. •“Spaghetti code” is code in which there is no distinction between the controlling side and the controlled side.
  58. 3. Application design Complex client-side applications require one-way data flow.

    Games are no exception. •React / preact / Solid / Svelte •Flutter •SwiftUI •Jetpack Compose
  59. 3. Application design In-memory pub/sub messaging is powerful pattern https://vitalrouter.hadashikick.jp

    Abstract it as a "message" to the application, independent of the input source. By simply spoofing this “message”, we can do anything: esting, auto-play, and demos.
  60. 3. Application design In-memory pub/sub messaging is powerful pattern https://vitalrouter.hadashikick.jp

    Publisher Publisher Subscriber Subscriber Interceptor B Interceptor C Interceptor C Interceptor B Interceptor A Interceptor A Exception Handling Logging Filtering The Interceptor pattern becomes applicable. Asynchronous processing is not just about completing something later!! •Cancellation •Error propagation •Throttling / Dropping / Switching •→ In FP terms, it's basically composition. Throttling / Switching
  61. mruby to C# messaging What is needed to do this

    •Interoperability of values between mruby and C#. •pause and resume mruby scripts at any time.
  62. mruby to C# messaging What is needed to do this

    •Interoperability of values between mruby and C#. •pause and resume mruby scripts at any time. MRubyCS.Serializer Integrating Fiber with the C# async/await ecosystem
  63. MRubyCS.Serializer •Interoperability of values between C# and MRubyValue •Like C#

    - JSON / C# - YAML / C# - msgpack / C# - protbuf wireformat Deserialize — Ruby to C# Serialize — C# to Ruby
  64. 2.Display text with a typewriter effect 3. Fiber.yield 4. Wait

    for button click 5. Fiber#resume 7. Display next text.. 1. 6. 3. Application design mruby to C# messaging