Axe*_*hra 5 javascript c++ webassembly
我正在尝试使用 WebSssembly 在浏览器上运行一些 C++ 函数。我正在关注本教程。我想知道:
emcc一种避免内存泄漏的方式重置/重新启动由生成的 WebAssembly 模块?添加异常捕获功能 ( DISABLE_EXCEPTION_CATCHING=0) 似乎会过多地增加文件大小。
任何帮助将不胜感激。
示例 C++ 代码如下:
// C++ source code (fib.cc)
#include <stdexcept>
#include <emscripten.h>
extern "C" {
EMSCRIPTEN_KEEPALIVE
int fib(int n) {
if (n > 12) {
throw std::out_of_range("input out of range");
}
int i, t, a = 0, b = 1;
for (i = 0; i < n; i++) {
t = a + b;
a = b;
b = t;
}
return b;
}
// >>
// other functions with allocations/deallocations
} // end of extern C
Run Code Online (Sandbox Code Playgroud)
它是使用以下命令构建的:
emcc -O3 -s WASM=1 -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap"]' fib.cc
Run Code Online (Sandbox Code Playgroud)
它是用网页测试的:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WASM Test Page</title>
</head>
<body>
<script src="a.out.js"></script>
<script>
"use strict";
Module.onRuntimeInitialized = _ => {
const fib = Module.cwrap('fib', 'number', ['number']);
console.log(fib(10));
console.log(fib(14)); // causes exception
};
</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)
如何(在 JS 端)检测来自 C++ 代码的“未捕获的异常”?
您必须使用try-catch块捕获异常(教程)。
例子:
try {
console.log( fib(14) );
}
catch ( e ) {
console.error( e );
}
Run Code Online (Sandbox Code Playgroud)
但是,到目前为止,它作为指针传播,因此您将在控制台中看到一些数字:
5249672
Run Code Online (Sandbox Code Playgroud)
如果您想在 JS 中获得正确的错误消息,那么您必须在 C++ 代码中编写绑定:
#include <emscripten/bind.h>
std::string getExceptionMessage(int eptr)
{
return reinterpret_cast<std::exception*>(eptr)->what();
}
EMSCRIPTEN_BINDINGS(getExceptionMessageBinding)
{
emscripten::function("getExceptionMessage", &getExceptionMessage);
};
Run Code Online (Sandbox Code Playgroud)
这将通过对象在 JS 代码中公开Module。您可以在 JS 代码中使用它,如下所示:
try {
console.log( fib(14) );
}
catch ( e ) {
console.error( Module.getExceptionMessage(e) );
}
Run Code Online (Sandbox Code Playgroud)
输出(抛出异常):
input out of range
Run Code Online (Sandbox Code Playgroud)
我编译了这段代码,并使用 C++11 启用了异常并进行了如下绑定:
~/emsdk/upstream/emscripten$ ./em++ -std=c++11 -Os -fexceptions --bind
-s WASM=1
-s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap"]'
-s DISABLE_EXCEPTION_CATCHING=0
fib.cc
Run Code Online (Sandbox Code Playgroud)
这是另一个类似的 GitHub问题,讨论了另一种方法。
添加异常捕获功能(
DISABLE_EXCEPTION_CATCHING=0)似乎会导致文件大小增加太多。
如果您担心输出文件的大小增加,则可以完全禁用异常处理,并利用函数返回的无效值或错误代码进行错误检查,例如,如果输入无效,则返回 -1。
然而,这里有一个观察结果:
先前构建的文件大小为:
110K - a.out.js
187K - a.out.wasm
Run Code Online (Sandbox Code Playgroud)
绑定的异常处理和 RTTI 是其中的一部分。
我剥离了代码并使用内联 JS 使用EM_ASM在以下代码片段中抛出 JS 错误:
#include <emscripten.h>
extern "C" {
EMSCRIPTEN_KEEPALIVE
int fib(int n) {
if (n > 12) {
EM_ASM(
throw Error("out_of_range"); // JS error with EM_ASM
);
}
int a {0}, b {1};
for ( int i {0}; i < n; ++i ) {
const auto t = a + b;
a = b;
b = t;
}
return b;
}
}
Run Code Online (Sandbox Code Playgroud)
编译时禁用异常:
$ ./em++ -std=c++11 -Os
-fno-exceptions
-s WASM=1
-s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap"]'
fib1.cc
Run Code Online (Sandbox Code Playgroud)
这是 HTML 文件 ( fib1.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WASM Test Page</title>
</head>
<body>
<script src="a.out.js"></script>
<script>
"use strict";
Module.onRuntimeInitialized = _ => {
const fib = Module.cwrap('fib', 'number', ['number']);
try {
console.log(fib(10));
console.log(fib(14));
}
catch ( e ) {
console.error(e);
}
};
</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)
控制台输出(捕获异常):
89
fib1.html:21 Error: out_of_range
at Array.ASM_CONSTS (a.out.js:1)
at _emscripten_asm_const_i (a.out.js:1)
at wasm-function[1]:0x6b
at Module._fib (a.out.js:1)
at Object.Module.onRuntimeInitialized (fib1.html:18)
at doRun (a.out.js:1)
at run (a.out.js:1)
at runCaller (a.out.js:1)
at removeRunDependency (a.out.js:1)
at receiveInstance (a.out.js:1)
Run Code Online (Sandbox Code Playgroud)
并且,文件大小为:
15K - a.out.js
246 - a.out.wasm (bytes)
Run Code Online (Sandbox Code Playgroud)
在禁用异常的情况下抛出 JS 错误仍然有效,并且生成的文件大小要小得多。您可能想进一步探索这一点。也许,创建一些继承自 Error 并具有扩展功能的类。但是,从标准 API 抛出的异常(例如)std::vector::at()将不起作用并导致终止。因此,您在禁用异常时需要考虑这些因素。
如何重置/重新启动 WebAssembly 模块生成以
emcc避免内存泄漏?
截至目前,还没有这样的 API 来重置/重启模块。当模块本身不再被引用时,它会自动被垃圾回收。在这种情况下你不必关心内存泄漏。JS 运行时负责此操作。
但是,如果 JS 代码创建的 C++ 对象Module.destroy正在管理资源(内存、文件句柄等),则应该使用它来销毁它。垃圾收集器 (GC) 在收集对象时不会调用析构函数,这会导致内存/资源泄漏。调用Module.destory会调用析构函数,不会有任何内存泄漏。现在,你的问题没有这样的对象。所以,打电话时要小心Module.destory在需要时致电。
至于 C++ 代码中的分配/释放,您自己负责释放所分配的资源。在这方面,以下几点可能会对您有所帮助:
避免未定义的行为。
虔诚地遵循三/五/零的规则。
使用基于RAII的 C++ 标准库工具进行自动内存管理,例如std::unique_ptr/std::shared_ptr和std::make_unique/ std::make_shared。
寻找 STL 容器(例如std::vector、std::map等)来存储和管理集合,而不是自行编写。标准的东西经过了充分的测试,因此更少担心错误。
请务必参阅您计划使用的 API 的文档。验证 API 是否分配了您可能需要在使用后以某种方式取消分配的资源。
这是有关加载 WASM 模块的线程:高效加载 WebAssembly 模块
| 归档时间: |
|
| 查看次数: |
773 次 |
| 最近记录: |