{"id":17220885,"url":"https://github.com/kojix2/cry-wasm","last_synced_at":"2025-04-13T22:32:56.762Z","repository":{"id":64921035,"uuid":"573751549","full_name":"kojix2/cry-wasm","owner":"kojix2","description":"cry-wasm speeds up Ruby code using Crystal","archived":false,"fork":false,"pushed_at":"2025-01-17T03:37:01.000Z","size":524,"stargazers_count":29,"open_issues_count":2,"forks_count":0,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-27T13:11:13.549Z","etag":null,"topics":["crystal","ruby","wasm","wasmer","webassembly"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kojix2.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-03T10:13:53.000Z","updated_at":"2025-01-17T03:36:47.000Z","dependencies_parsed_at":"2024-10-15T04:03:42.752Z","dependency_job_id":null,"html_url":"https://github.com/kojix2/cry-wasm","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kojix2%2Fcry-wasm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kojix2%2Fcry-wasm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kojix2%2Fcry-wasm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kojix2%2Fcry-wasm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kojix2","download_url":"https://codeload.github.com/kojix2/cry-wasm/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248790809,"owners_count":21162092,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crystal","ruby","wasm","wasmer","webassembly"],"created_at":"2024-10-15T03:53:31.309Z","updated_at":"2025-04-13T22:32:54.620Z","avatar_url":"https://github.com/kojix2.png","language":"Ruby","readme":"# cry-wasm\n\n[![CI](https://github.com/kojix2/cry-wasm/actions/workflows/ci.yml/badge.svg)](https://github.com/kojix2/cry-wasm/actions/workflows/ci.yml)\n[![Docs Latest](https://img.shields.io/badge/docs-latest-blue.svg)](https://kojix2.github.io/cry-wasm/)\n\n:zap: cry-wasm speeds up [Ruby](https://github.com/ruby/ruby) code.\n\nBy applying simple type restrictions to Ruby code, convert it to [Crystal](https://github.com/crystal-lang/crystal) code, compile it to [WebAssembly](https://webassembly.org/), and call it with [Wasmer](https://github.com/wasmerio/wasmer) or [Wasmtime](https://github.com/bytecodealliance/wasmtime).\n\n\u003cdiv align=\"center\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/kojix2/cry-wasm/main/doc/overview.drawio.png\" width=50% height=50%\u003e\u003c/div\u003e\n\n:space_invader: experimental\n\n## Quick Start\n\n```ruby\nrequire 'cry/wasm'\n\nclass Fibonacci\n  extend Cry::Wasm            # (1) Extend your class\n\n  cry [:Int32], :Int32        # (2) Write type signatures\n  def fib(n)\n  　return 1 if n \u003c= 2\n    fib(n - 1) + fib(n - 2)\n  end\n\n  cry_build                   # (3) Compile Wasm\nend\n\nFibonacci.new.fib(40)         # (4) Call Wasm Function\n```\n\n1. Extend Cry::Wasm module to your Ruby class.\n2. Write Crystal type signatures for Ruby methods. The syntax is `[arg_t1, arg_t2], ret_t` (Symbol or String).\n3. Crystal compiler compile the Ruby methods into WebAssembly as Crystal functions.\n4. Finally, call the wasm function!\n\n## Benchmark\n\n[fib_bench.rb](https://github.com/kojix2/cry-wasm/blob/main/examples/fib_bench.rb) - 10 x faster on the [Fibonacci benchmark](https://crystal-lang.org/2016/07/15/fibonacci-benchmark/).\n\n```\n                       user     system      total        real\nruby     fib(40)   5.305503   0.000000   5.305503 (  5.305696)\nwasmtime fib(40)   0.462232   0.000000   0.462232 (  0.462247)\nwasmer   fib(40)   0.381384   0.000000   0.381384 (  0.381401)\n```\n\n\u003cimg src=\"https://raw.githubusercontent.com/kojix2/cry-wasm/main/doc/benchmark.svg\" width=\"40%\" height=\"40%\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/kojix2/cry-wasm/main/doc/benchmark_plot.png\" width=25% height=\"25%\"\u003e\n\n- In this benchmark, Wasmer is about 10% faster than Wasmtime as of December 2022.\n- Both Wasmer and Wasmtime tend to take a little longer for the first call. (see line graph at n=1)\n- Wasm is only about twice as slow as native functions, making it highly efficient. (according to my measurements)\n\n## How does this work?\n\n```mermaid\nflowchart LR\nstyle id1 fill:#c5c,stroke:#f66,stroke-width:1px,color:#fff\nstyle id2 fill:#555,stroke:#3ff,stroke-width:1px,color:#fff\nstyle id3 fill:#66f,stroke:#f66,stroke-width:1px,color:#fff\nstyle id4 fill:#c5c,stroke:#ff1,stroke-width:1px,color:#fff\n    id1(Ruby Methods) -- Ripper + Sorcerer --\u003e id2(Crystal Functions) -- Crystal Compiler --\u003e id3[WebAssembly]\n    id4(Ruby Code) \u003c-- Wasmer/Wasmtime --\u003e id3[WebAssembly]\n```\n\n1. Extend the Cry::Wasm module to the target class.\n1. Write the type information just before the method.\n   1. Use the `cry` method to restrict argument types and return types.\n1. Once the method is defined, Cry::Wasm captures the source code.\n   1. [Ripper](https://ruby-doc.org/stdlib-3.1.2/libdoc/ripper/rdoc/Ripper.html) converts source code to [S-expression](https://en.wikipedia.org/wiki/S-expression).\n   1. Extracts the S-expression of the target method from the S-expression.\n   1. [Sorcerer](https://github.com/rspec-given/sorcerer) recovers the Ruby source code of the target method from the S-expression.\n   1. Add Crystal type restrictions to the Ruby source code to generate a Crystal code block.\n   1. Cry::Wasm stores the Crystal code block.\n1. The Crystal compiler and wasm-ld compile the Crystal code into WebAssembly.\n   1. Call the `cry_build` method to build the crystal code blocks.\n1. The compiled byte_code is read, and an instance of Wasmer/Wasmtime is created.\n1. The target methods are dynamically redefined to call Wasmer/Wasmtime functions.\n\n## Usage\n\n### It define crystal functions, not Crystal methods\n\n- Default arguments, keyword arguments, and block arguments are not available.\n- Instance variables and class variables are not available on the top level function.\n- To use your own Crystal class, use `cry_load(path)` to pre-load your crystal source code.\n\n### Type conversion\n\n#### Arguments ( Ruby --\u003e Crystal )\n\n| Ruby class       | Crystal class                                                                                                             |\n| ---------------- | ------------------------------------------------------------------------------------------------------------------------- |\n| `Integer`        | `UInt8` `Int8` `UInt16` `Int16` `UInt32` `Int32` `UInt64` `Int64`                                                         |\n| `Float`          | `Float32` `Float64`                                                                                                       |\n| `Array\u003cInteger\u003e` | `UInt8*` `Int8*` `UInt16*` `Int16*` `UInt32*` `Int32*` `UInt64*` `Int64*`                                                 |\n| `Array\u003cInteger\u003e` | `Array(UInt8)` `Array(Int8)` `Array(UInt16)` `Array(Int16)` `Array(UInt32)` `Array(Int32)` `Array(UInt64)` `Array(Int64)` |\n| `Array\u003cFloat\u003e`   | `Float32*` `Float64*`                                                                                                     |\n| `Array\u003cFloat\u003e`   | `Array(Float32)` `Array(Float32)`                                                                                         |\n| `String`         | `String`                                                                                                                  |\n\n#### Return values ( Crystal --\u003e Ruby )\n\n| Crystal class                                                                                                             | Ruby class                          |\n| ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |\n| `UInt8` `Int8` `UInt16` `Int16` `UInt32` `Int32` `UInt64` `Int64`                                                         | `Integer`                           |\n| `Float32` `Float64`                                                                                                       | `Float`                             |\n| `UInt8*` `Int8*` `UInt16*` `Int16*` `UInt32*` `Int32*`                                                                    | View object of Wasmer (wasmer only) |\n| `Array(UInt8)` `Array(Int8)` `Array(UInt16)` `Array(Int16)` `Array(UInt32)` `Array(Int32)` `Array(UInt64)` `Array(Int64)` | `Array\u003cInteger\u003e`                    |\n| `Array(Float32)` `Array(Float32)`                                                                                         | `Array\u003cFloat\u003e`                      |\n| `String`                                                                                                                  | `String`                            |\n| `Void`                                                                                                                    | `Nil`                               |\n\n#### Why is `Symbol` not supported?\n\nIn the Crystal language, Symbol is converted to an integer at compile time, so there is no way to get Symbol from a String; use `String` instead of `Symbol`.\n\n#### Cry::Numeric uses Refinement to add methods to Ruby's numeric classes\n\n`Cry::Numeric` can use Refinements to add methods such as `to_i8`, `to_u8`, and `to_f32` to Ruby's numeric classes. These methods are the same as `to_i` and `to_f` (the range of values is not checked). These are useful if you want to prevent errors when running your code as Ruby and get the same results as if you had run it as Crystal.\n\n#### Why is it very slow to return arrays?\n\nCurrently reading memory in wasm and converting it to Ruby arrays takes quite a bit of time. As a result, it may take longer to run with cry-wasm than when run as pure Ruby. Also note that currently (2022/12) wasmtime-rb is faster than wasmer-ruby when it comes to reading memory. If you are interested in improving these issues, please consider contributing to wasmer-ruby or wasmtime-rb.\n\n## Installation\n\nRequirements\n\n1. [Crystal](https://github.com/crystal-lang/crystal) - Follow the installation instructions [here](https://crystal-lang.org/install/) for your platform.\n1. [Rust](https://www.rust-lang.org/) - Rust is required to compile the [wasmer-ruby](https://github.com/wasmerio/wasmer-ruby) or [wasmtime-rb](https://github.com/bytecodealliance/wasmtime-rb).\n1. [LLVM](https://llvm.org/) for macOS:\n   1. Install LLVM by running `brew install llvm`\n   1. Find the path to wasm-ld by running `brew ls llvm | grep wasm-ld`.\n   1. Set the PATH environment variable so that `wasm-ld` can be called.\n1. [LLD](https://lld.llvm.org/) for Ubuntu:\n   1. Install LLD by running `sudo apt install lld`.\n   1. Find the path to wasm-ld by running `dpkg -L lld | grep wasm-ld`.\n   1. If necessary, create a symbolic link for `wasm-ld-9` or `wasm-ld-10`.\n1. [WebAssembly Libs for WASI](https://github.com/lbguilherme/wasm-libs)\n   1. Use the `rake vendor:wasi_libs` task to download the libs to the vendor directory.\n   1. If you install the libs outside the given directory, set the `CRYSTAL_LIBRARY_PATH` environment variable.\n\nInstallation\n\n```\nbundle install\nbundle exec rake vendor:wasi_libs\nbundle exec rake install\n```\n\nPlease note that cry-wasm depends on the latest API of wasmer-ruby and wasmtime-rb, so we have to use the GitHub master rather than the stable version.\n\nTested on macOS and Ubuntu using [Github Actions](https://github.com/kojix2/cry-wasm/blob/main/.github/workflows/ci.yml). Windows is not yet supported.\n\n## Development\n\n```\ngit clone https://github.com/kojix2/cry-wasm\ncd cry-wasm\nbundle install\nbundle exec rake vendor:wasi_libs\nbundle exec rake spec\n```\n\n- [Trying out WASM Support](https://forum.crystal-lang.org/t/trying-out-wasm-support/4508) - A thread in the Crystal Forum on how to compile a wasm from crystal.\n- [wasm-libs](https://github.com/lbguilherme/wasm-libs) - WebAssembly Libs for WASI. You need to download the compiled wasm library.\n\nEven small improvements like fixing typos are welcome! Please feel free to send us your PR.\n\n## license\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkojix2%2Fcry-wasm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkojix2%2Fcry-wasm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkojix2%2Fcry-wasm/lists"}