crystal_wamr v0.1.0
crystal_wamr
TODO: Write a description here
Tested with WAMR 1.2.3 and 2.1.0
No WASI Support No Imports Support Pure WASM32 a.k.a. Browser Custom Native Functions Implementation not compatible with other runtimes
If you need extensions like WASI/WASIX check out wasmer-crystal better.
Installation
-
Add the dependency to your
shard.yml
:dependencies: crystal_wamr: github: onliniak/crystal_wamr
-
Run
shards install
Replit
#replit.nix
{ pkgs }: {
deps = [
pkgs.crystal
pkgs.shards
pkgs.openssl
pkgs.pkg-config
pkgs.pcre2
pkgs.wamr
];
}
#.replit
run = "crystal run main.cr"
entrypoint = "main.cr"
[nix]
channel = "stable-24_05"
FreeBSD
git clone https://github.com/bytecodealliance/wasm-micro-runtime.git
cd wasm-micro-runtime/product-mini/platforms/freebsd
mkdir build && cd build
cmake ..
make
mv iwasm /usr/local/bin/iwasm
mv libiwasm.so /usr/local/lib/libiwasm.so
mv libvmlib.a /usr/local/lib/libvmlib.a
crystal init app myapp
cd myapp # && shards install
cd lib/crystal_wamr
iwasm --version # check if iwasm is installed
crystal spec # check if libiwasm.so is installed
Server
You must add -Wl,-rpath=ABSOLUTE_PATH to gcc.
rm /usr/local/lib/libiwasm.so /usr/local/lib/libvmlib.a /usr/local/bin/iwasm
cd wasm-micro-runtime/product-mini/platforms/freebsd/build && make
mv iwasm.so /server/iwasm.so && mv libvmlib.a /server/libvmlib.a && mv /usr/local/bin/crystal /server/crystal && mv /usr/local/bin/shards /server/shards
- Build on server.
chmod +x crystal
chmod +x shards
mkdir lib/crystal # Crystal will look for .so files in this directory by default. Problem is, the executable itself does not use this directory.
cp libiwasm.so lib/crystal/libiwasm.so
cp libvmlib.a lib/crystal/libvmlib.a
./crystal init app test
mkdir test/extra
cp libiwasm.so test/extra/libiwasm.so
cp libvmlib.a test/extra/libvmlib.a
mv crystal test/crystal
mv shards test/shards
cd test
./shards install
nano lib/crystal_wamr/src/crystal_wamr.cr # add new line before @[Link("iwasm")] => @[Link(ldflags: "-Wl,-rpath=/usr/home/onliniak/domains/test.onliniak.ct8.pl/public_html/test/extra")] # !!! MUST BE ABSOLUTE PATH
touch main.cr
./crystal build main.cr
./main # check if linking works
Usage
The shard is divided into 3 parts:
-
cd lib/crystal_wamr && crystal spec
=> create example wasm files in spec directory -
exec_once runs the WASM file and returns its result
require "crystal_wamr"
wasm = CrystalWamr::WASM.new
p wasm.exec_once(File.read("lib/crystal_wamr/spec/fib.wasm"), "fib", [8]) # => 21
- exec loads the WASM file, runs several functions and closes the WASM file
wasm.exec(File.read("lib/crystal_wamr/array.wasm"), {
"newarr" => [5],
"newarra" => [10],
"newarrb" => [15],
"count" => [0],
"first" => [0],
"last" => [0]
})
p wasm.return_hash # {"newarr" => 5, "newarra" => 10, "newarrb" => 15, "count" => 3, "first" => 5, "last" => 15}
extern int array[0] = 0;
extern int size = 0;
extern void newarr(int a){
array[size] = a;
size++;
}
extern void newarra(int a){
array[size] = a;
size++;
}
extern void newarrb(int a){
array[size] = a;
size++;
}
extern int count(){
return size;
}
extern int first(){
return array[0];
}
extern int last(){
return array[size - 1];
}
2.1. Read strings
crystal_wamr_return_string(File.read("string.wasm"), "string") # "abcd"
const char* word = "abcd";
extern int string(int s){
int r = word[s];
return r;
}
- exec_json lets you configure the library's operation with a json file.
require "http/server"
require "crystal_wamr"
require "./config"
wasm = CrystalWamr::WASM.new
ENV["PORT"] = "8080"
ENV["HOST"] = "0.0.0.0"
config = CrystalWamr::WamrConfig.from_json(%({
"file": "lib/crystal_wamr/spec/math.wasm",
"func": [
{
"name": "add",
"input": [
{
"argv": {
"int": 2
}
},
{
"argv": {
"var": "$URL",
"sys": {
"name": "cbrt",
"argv": []
}
}
}
]
},
{
"name": "mul",
"input": [
{
"argv": {
"int": 2
}
},
{
"argv": {
"var": "add"
}
}
]
}
]
}))
server = HTTP::Server.new do |context|
wasm.exec_json(CrystalWamr::WamrConfig.from_json(File.read("config.json")), context.request.path)
context.response.content_type = "text/plain"
context.response.print wasm.return_hash.to_s
end
address = server.bind_tcp ENV["HOST"], ENV["PORT"].to_i
server.listen
# config.cr
module CrystalWamr
class WASM
def native_functions(sys, functions, index, url_path : Array(Int32))
argv = [] of Int32
if sys.argv.size == 0
argv = url_path
elsif sys.argv.size > 0
argv = sys.argv
else
argv = url_path
end
if sys.name == "cbrt"
functions[index] << Math.cbrt(argv[0]).to_i
end
if sys.name == "hypot"
functions[index] << Math.hypot(argv[0], argv[1]).to_i
end
end
end
end
JSON
{
"file": "lib/crystal_wamr/spec/math.wasm",
file = filename with extension .aot or .wasm
"func": [
{
"name": "add",
"input": [
{
"argv": {
"int": 2
}
},
{
"argv": {
"var": "$URL",
"sys": {
"name": "cbrt",
"argv": []
}
}
}
]
},
func
name = name of the WASM function
input = array of Int32 function parameters
argv
int = const int32
var = The result from the execution of the WASM function
$URL # curl myweb.eu/27/17/10
argv[0] = 27
argv[1] = 17
argv[2] = 10
sys # looking for line: if sys.name == "cbrt" inside config.cr
name
argv = array of arguments for native crystal function. Leave empty when $URL in use.
]
})
The add function retrieves the web address. For example, myweb.eu/27 => $URL = 27.
It then passes $URL to the Math.cbrt function and we have 3. Finally, it adds the result to 2.
The mul function multiplies the result of the add function (5 in the example) by 2.
TODO: Write usage instructions here
Strings
Tip: Crystal will remove blank characters.
crystal_wamr_return_string(File.read("string.wasm"), "string") # "Lorem ipsum dolor"
crystal_wamr_return_string(File.read("string.wasm"), "string", x) # Returns x characters. Default = 16.
const char* word = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
extern int string(int s){
int r = word[s];
return r;
}
Compilation
Clang
- https://github.com/WebAssembly/wasi-sdk/releases/latest
./wasi-sdk-23.0-x86_64-linux/bin/clang --target=wasm32 -nostdlib -Wl,--no-entry -Wl,--export-all -o main.wasm main.c
C4WA
- https://github.com/kign/c4wa/releases/latest
./c4wa-compile-0.5/bin/c4wa-compile main.c
Expected performance
Wizard mode
extern int add(int a, int b){
return a + b;
}
hyperfine --warmup 3 './single_aot' './hash_aot' 'iwasm -f add add.wasm 8 12' -i -N
Benchmark 1: ./single_aot
Time (mean ± σ): 14.1 ms ± 4.1 ms [User: 1.4 ms, System: 10.5 ms]
Range (min … max): 10.2 ms … 36.4 ms 131 runs
Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
Benchmark 2: ./hash_aot
Time (mean ± σ): 13.5 ms ± 3.0 ms [User: 1.1 ms, System: 10.6 ms]
Range (min … max): 9.7 ms … 28.8 ms 202 runs
Benchmark 3: iwasm -f add add.wasm 8 12
Time (mean ± σ): 16.5 ms ± 6.3 ms [User: 0.7 ms, System: 10.9 ms]
Range (min … max): 9.9 ms … 59.4 ms 260 runs
Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
Summary
./hash_aot ran
1.05 ± 0.38 times faster than ./single_aot
1.22 ± 0.54 times faster than iwasm -f add add.wasm 8 12
Another example
cd lib/crystal_wamr && crystal spec --tag "aot-benchmark"
cd lib/crystal_wamr && crystal spec --tag "native-benchmark"
[on server] The identical code in the crystal and C4WA -> AOT has a similar speed. However, it is quite possible that I made some serious mistakes in the code and the speed of this library will be lower than iwasm. If it bothers you PR welcome.
Known Issues
Invalid memory access (signal 11) at address 0x0
- If you are using AOT make sure wamrc is in the same version as iwasm.
- This shard don't support WASI and Imports.
- The function you are trying to run returns an array, char or any other type that is not int, double, float, long
Stack guard pages
Wrong iwasm installed Try cd lib/crystal_wamr && crystal spec
Reinstall shard: rm shard.lock && shards install
Failed to init stack guard pages
[10:07:17:747 - 2781C4C18000]: wasm_runtime_malloc failed: memory hasn't been initialized.
Invalid memory access (signal 11) at address 0x0
[0x417699] ???
[0x417668] ???
[0x7def2f] ???
[0x7de4f4] ???
Development
WAMR API → https://bytecodealliance.github.io/wamr.dev/docs/html/wasm__export_8h.html
TODO: Write development instructions here
Contributing
- Fork it (https://github.com/onliniak/crystal_wamr/fork)
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
Contributors
- Rafael Pszenny - creator and maintainer
crystal_wamr
- 0
- 0
- 0
- 0
- 0
- 2 months ago
- June 13, 2024
MIT License
Wed, 06 Nov 2024 16:23:51 GMT