试用
$:~/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函数
函数的实现代码
#ifdef __cplusplus
extern "C" {
#endif
int 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 bounds
createExportWrapper http://127.0.0.1:8000/mnist.js:876
ccall http://127.0.0.1:8000/mnist.js:4875
predict http://127.0.0.1:8000/:46 # predict函数报错
onRandom http://127.0.0.1:8000/:69
onclick 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 functions
var 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
- Dynamically import WebAssembly from variable
const code = `\
const wasmCode = new Uint8Array([
0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127,
3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0,
5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145,
128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97,
105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0,
65, 42, 11
]);
const wasmModule = new WebAssembly.Module(wasmCode);
const wasmInstance = new WebAssembly.Instance(wasmModule);
const main = wasmInstance.exports.main as CallableFunction;
console.log(main().toString());
`;
import("data:text/typescript;base64," + btoa(code));