为什么在WebAssembly中ALLOW_MEMORY_GROWTH = 1失败,而TOTAL_MEMORY = 512MB成功?

时间:2019-04-27 19:56:20

标签: emscripten webassembly

我有一个图像处理Wasm项目,该项目将不同的二值化算法应用于给定图像。这些算法之一是在我运行该错误时产生的:

Uncaught abort("Cannot enlarge memory arrays to size 17100800 bytes (OOM). Either
(1) compile with  -s TOTAL_MEMORY=X  with X higher than the current value 16777216,
(2) compile with  -s ALLOW_MEMORY_GROWTH=1  which allows increasing the size at runtime, or
(3) if you want malloc to return NULL (0)
instead of this abort, compile with  -s ABORTING_MALLOC=0 ") at Error

使用"-s ALLOW_MEMORY_GROWTH=1"进行编译后,该算法在Chrome中没有出错,但是将图像变为黑色。连续第二次运行出现此错误:

  

Uncaught RuntimeError: memory access out of bounds

第一次在Edge 42.17134.1.0中运行时,出现此错误:

  

SCRIPT5147: The ArrayBuffer is detached.

足够有趣,删除该选项并将其替换为"-s TOTAL_MEMORY=512MB"即可解决问题。

所以我的问题是:ALLOW_MEMORY_GROWTH=1是否应该在运行时自动为我扩展堆内存,并且几乎可以代替TOTAL_MEMORY?我讨厌在编译时锁定我的应用程序可以使用的最大内存的想法。经过Chrome 73.0.3683.103 / 74.0.3729.108 (Official Build) (64-bit)和Firefox 66.0.3 (64-bit)的测试。

修改 我可以使用以下代码找出问题所在。

为调试而编译:

em++ -O0 -o ./dist/doxaWasm.js doxaWasm.cpp -std=c++1z -s WASM=1 -s NO_EXIT_RUNTIME=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['writeAsciiToMemory']" -g4 --source-map-base http://localhost:8080/ -s ALLOW_MEMORY_GROWTH=1

C ++代码段:

extern "C"
{
    void EMSCRIPTEN_KEEPALIVE Initialize(uint8_t* data, const int width, const int height)
    {
        // Large enough to force a reallocation.
        // If there is no reallocation, everything works.
        std::vector<int64_t> integral_image1(width*height*10);

        data[0] = 123;
    }
}

JavaScript代码段:

...
var size = width * height;
var heapPtr = Module._malloc(size);
var data = new Uint8ClampedArray(Module.HEAPU8.buffer, heapPtr, size);
... // Populate 'data' with 8bit grayscale based on canvas ImageData
Module._Initialize(data.byteOffset, width, height);
console.log(data[0]); // Equals 123 if there is no reallocation

1 个答案:

答案 0 :(得分:0)

我知道发生了什么事。这是由于我对这个主题的天真。 我正在创建的Uint8ClampedArray充当基础内存的窗口,将其整形为我可以访问的形式。但是,一旦重新分配,HEAPU8缓冲区就会发生变化,导致其“脱离”我的阵列。

然后将在Wasm方法调用之前创建映射数组,以将数据传递给函数,或者在Wasm调用之后创建映射数组,以从函数读取数据。在上面的示例中,我需要两个,一个在之前,一个在之后,因为之前创建的数组可能会分离。

...
var size = width * height;
var heapPtr = Module._malloc(size);
var input = new Uint8ClampedArray(Module.HEAPU8.buffer, heapPtr, size);
... // Populate 'data' with 8bit grayscale based on canvas ImageData
Module._Initialize(heapPtr, width, height);
var output = new Uint8ClampedArray(Module.HEAPU8.buffer, heapPtr, size);
console.log(output[0]);

我讨厌回答我自己的问题...但是希望这可以避免某些人遇到麻烦。也许任何对此主题有更多专业知识的人都可以澄清此答案中可能需要的任何部分。