Skip to content

Instantly share code, notes, and snippets.

@ukyo
Last active November 13, 2018 08:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ukyo/62a38733f9c0093456a0e003774593d5 to your computer and use it in GitHub Desktop.
Save ukyo/62a38733f9c0093456a0e003774593d5 to your computer and use it in GitHub Desktop.
mallocとfreeだけ使いたい

wasmのメモリ管理つらい問題

とりあえずmallocとfreeがあればokなんだが・・・。

解決策1 emscripten使う

  • pros: 何も考えなくてもmallocとfreeが手に入る。
  • cons: でかい。

解決策2 https://github.com/dcodeIO/webassembly から取り出す

  • pros: ランタイムが小さい。
  • cons: あんまり無い気がするぞ。
#include <webassembly.h>

// とりあえず一回でも使えばexportされる(もっとマシな方法あるかもね)
void* m = malloc;
void* f = free;
$ wa-compile -O -o malloc.wasm malloc.c

これで9989B。いいかんじ。これをJSから使ってみる。

const env = {
  memory:  new WebAssembly.Memory({
    initial: 1,
    maximum: 1024,
  }),
  _abort: () => {},
  _grow: () => {},
};

async function initWasm(path, importObject) {
  const r = await fetch(path);
  const b = await r.arrayBuffer();
  const m = await WebAssembly.compile(b);
  return await WebAssembly.instantiate(m, importObject);
}

(async () => {
  const libmalloc = await("malloc.wasm", {env});
  const ptr = libmalloc.exports.malloc(1024);
  libmalloc.exports.free(ptr);
})();

試しにクイックソートで試してみる。

// wikipedia 参照
#include <webassembly.h>

int med3(int x, int y, int z) {
  if (x < y) {
    if (y < z) return y; else if (z < x) return x; else return z;
    } else {
    if (z < y) return y; else if (x < z) return x; else return z;
  }
}

void quicksort(int a[], int left, int right) {
  if (left < right) {
    int i = left, j = right;
    int tmp, pivot = med3(a[i], a[i + (j - i) / 2], a[j]); /* (i+j)/2 ではオーバーフローしてしまう */
    while (1) { /* a[] を pivot 以上と以下の集まりに分割する */
      while (a[i] < pivot) i++; /* a[i] >= pivot となる位置を検索 */
      while (pivot < a[j]) j--; /* a[j] <= pivot となる位置を検索 */
      if (i >= j) break;
      tmp = a[i]; a[i] = a[j]; a[j] = tmp; /* a[i], a[j] を交換 */
      i++; j--;
    }
    quicksort(a, left, i - 1);  /* 分割した左を再帰的にソート */
    quicksort(a, j + 1, right); /* 分割した右を再帰的にソート */
    }
}
$ wa-compile -O -o sort.wasm sort.c

サイズは457B。JSコードは以下。

(async () => {
  const libmalloc = await initWasm("malloc.wasm", {env});
  const instance = await initWasm("sort.wasm", {env});
  const ptr = libmalloc.exports.malloc(16 * 4);
  console.log("ptr:", ptr);
  const i32arr = new Int32Array(env.memory.buffer);
  i32arr.set([10, 2, 3, 7, 4, 5, 11, 15, 12, 0, 9, 8, 13, 1, 6, 14], ptr >> 2);
  instance.exports.quicksort(ptr, 0, 15);
  console.log(i32arr.subarray(ptr >> 2, (ptr >> 2) + 16));
  libmalloc.exports.free(ptr);
})();
@petamoriken
Copy link

guybedford/wasm-stdlib-hack のように自前で musl, dlmalloc をコンパイルする方法もありますね。
提示されている dcodeIO/webassembly の方が汎用性があって良さそうですが……。

このリポジトリの詳細(使用例)は以下の解説動画です。以前は無料で見れたのですが、今は有料になっています……。
https://egghead.io/lessons/javascript-allocate-dynamic-memory-in-webassembly-with-malloc

@ukyo
Copy link
Author

ukyo commented Jun 30, 2017

コマンド1発でいけて、ランタイムが最小限なのは助かります。
あと、コンパイル時に内部で使っているコマンドがオプション付きで表示されるので、個別に使いたいときの参考になるのが良いところですね。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment