试用
$:~/ggml/ggml$cd examples/mnist
$:~/ggml/ggml/examples/mnist$ emcc -I../../include -I../../include/ggml -I../../examples ../../src/ggml.c main.cpp -o web/mnist.js -s EXPORTED_FUNCTIONS='["_wasm_eval","_wasm_random_digit","_malloc","_free"]' -s EXPORTED_RUNTIME_METHODS='["ccall"]' -s ALLOW_MEMORY_GROWTH=1 --preload-file models/mnist
cache:INFO: generating system asset: symbol_lists/a262e86b2699be8569b00ee0af7e34a0a05c5b5e.json... (this will be cached in "/home/pdd/Downloads/emsdk/upstream/emscripten/cache/symbol_lists/a262e86b2699be8569b00ee0af7e34a0a05c5b5e.json" for subsequent builds)
cache:INFO: - ok
- 启动一个服务
$:~/ggml/ggml/examples/mnist/web$ python -m http.server
与js代码交互
使用ccall 从JavaScript调用已编译的C函数
- 从JavaScript调用已编译的C函数的最简单方法是使用ccall()或cwrap()。ccall()用指定的参数调用一个编译后的C函数并返回结果,而cwrap()则“包装”一个编译过的C函数,并返回一个可以正常调用的JavaScript函数。因此,如果您计划多次调用已编译的函数,cwrap()会更有用。
函数的实现代码
#ifdef __cplusplus
extern "C" {
#endifint wasm_eval(uint8_t * digitPtr) {mnist_model model;if (!mnist_model_load("models/mnist/ggml-model-f32.bin", model)) {fprintf(stderr, "error loading model\n");return -1;}std::vector<float> digit(digitPtr, digitPtr + 784);int result = mnist_eval(model, 1, digit, nullptr);ggml_free(model.ctx);return result;
}int wasm_random_digit(char * digitPtr) {auto fin = std::ifstream("models/mnist/t10k-images.idx3-ubyte", std::ios::binary);if (!fin) {fprintf(stderr, "failed to open digits file\n");return 0;}srand(time(NULL));// Seek to a random digit: 16-byte header + 28*28 * (random 0 - 10000)fin.seekg(16 + 784 * (rand() % 10000));fin.read(digitPtr, 784);return 1;
}#ifdef __cplusplus
}
#endif
js中调用代码
// Call C from JavaScript
var result = Module.ccall('int_sqrt', // name of C function'number', // return type['number'], // argument types[28]); // arguments
调试
// 在chrome edge运行都没有问题,但是在火狐运行会报错(官方的网页在火狐能够正常运行)
Uncaught RuntimeError: index out of boundscreateExportWrapper http://127.0.0.1:8000/mnist.js:876ccall http://127.0.0.1:8000/mnist.js:4875predict http://127.0.0.1:8000/:46 # predict函数报错onRandom http://127.0.0.1:8000/:69onclick http://127.0.0.1:8000/:1
mnist.wasm:360562:1
- 生成的minist.js
/*** @param {string|null=} returnType* @param {Array=} argTypes* @param {Arguments|Array=} args* @param {Object=} opts*/var ccall = function(ident, returnType, argTypes, args, opts) {// For fast lookup of conversion functionsvar toC = {'string': (str) => {var ret = 0;if (str !== null && str !== undefined && str !== 0) { // null string// at most 4 bytes per UTF-8 code point, +1 for the trailing '\0'ret = stringToUTF8OnStack(str);}return ret;},'array': (arr) => {var ret = stackAlloc(arr.length);writeArrayToMemory(arr, ret);return ret;}};function convertReturnValue(ret) {if (returnType === 'string') {return UTF8ToString(ret);}if (returnType === 'boolean') return Boolean(ret);return ret;}var func = getCFunc(ident);var cArgs = [];var stack = 0;assert(returnType !== 'array', 'Return type should not be "array".');if (args) {for (var i = 0; i < args.length; i++) {var converter = toC[argTypes[i]];if (converter) {if (stack === 0) stack = stackSave();cArgs[i] = converter(args[i]);} else {cArgs[i] = args[i];}}}var ret = func.apply(null, cArgs);function onDone(ret) {if (stack !== 0) stackRestore(stack);return convertReturnValue(ret);}ret = onDone(ret);return ret;};
- 可以打断点
- 然后运行到wasm代码
同一段代码在chrome , firefox对比
我在minist.js加入了一个输出,firefox好像给我优化掉了,并且对于同一个wasm的解释方式也有不同,截图如下
- chrome
- firefox
CG
- WASM throws memory access out of bounds in Chrome but not Firefox/Edge