I released QjsIpc 🎉

Learning WASM 4 ~ 8 の成果をライブラリ化して公開した。



JS側では今回作成したQuickJSの拡張モジュールでる`ipc`モジュールを使用し、プロシージャを登録して、最終的に`listen`メソッドを実行する


import * as ipc from 'ipc';

ipc.register('echo', function (payload) {
  return payload;
});

ipc.listen();


.NET側では、QjsIpcEngineのインスタンスを作成し、JSファイルと実行フォルダを指定してQuickJSを実行する。  

実行した後は、`InvokeAsync`メソッドでJS側に登録したプロシージャを実行できる。


using QjsIpc;

await using var engine = new QjsIpcEngine();

engine.Start(new QjsIpcOptions
{
    ScriptFileName = "main.js",
    AllowedDirectoryPath = Environment.CurrentDirectory,
});

var message = await engine.InvokeAsync<string>("echo", "Hello, World!");

Console.WriteLine(message);


.NET側にプロシージャを用意し、JS側から呼び出すこともできる。  

.NET側にプロシージャホストとなるクラスを作成し、そのインスタンスをMethodHostオプションに設定する。

生やすメソッドは、値でもTaskでもValueTaskでも大丈夫。


public class Host
{
	public int HostAdd(int a, int b) => a + b;

	public Task<string> HostStringTask() => Task.FromResult("host value");
	
	public async Task<string> HostStringAsync() {
		await Task.Delay(100);
		return "host value";
	}

	public async ValueTask<string> HostStringValueAsync() {
		await Task.Delay(100);
		return "host value";
	}
}

public class Program
{
	public static async Task Main(string[] args)
	{
		await using var engine = new QjsIpcEngine();

		engine.Start(new QjsIpcOptions
		{
		  ScriptFileName = "main.js",
		  AllowedDirectoryPath = Environment.CurrentDirectory,
		  MethodHost = new Host()
		});

		await engine.InvokeAsync('createMessage', "iwate");
	}
}


JS側では`ipc.invoke`メソッドで呼び出せる。戻り値は`Promise`。


import * as ipc from 'ipc';

ipc.register('createMessage', async function (name) {
  const value = ipc.invoke('HostAdd', 100, 200);
  return Hi, ${name}. The result of HostAdd(100, 200) is ${value};
});

ipc.listen();


ベンチマークもとってみたが、想像通り速くはない。けど使えないほどではない。


自分の実装の中で遅い部分の検討はついてて、.NET<->JS間を1バイトずつやり取りしてるのでまあ遅い。  

wasmのmemory使ってページ(64kb)ごとにやり取りすればもっと早くなるかな。今後に課題。


//  Summary 

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000
11th Gen Intel Core i7-1165G7 2.80GHz, 1 CPU, 8 logical and 4 physical cores
.NET SDK=6.0.200-preview.21617.4
  [Host]     : .NET 6.0.1 (6.0.121.56705), X64 RyuJIT  [AttachedDebugger]
  DefaultJob : .NET 6.0.1 (6.0.121.56705), X64 RyuJIT


|           Method |       Mean |     Error |    StdDev |
|----------------- |-----------:|----------:|----------:|
|             Echo |   303.7 us |  28.22 us |  81.42 us |
|        HostValue |   456.2 us |  41.25 us | 120.97 us |
|              Ejs | 1,414.2 us |  91.87 us | 269.43 us |
| EjsWithHostValue | 1,499.9 us | 121.33 us | 340.22 us |