Почему в WebAssembly терпит неудачу ALLOW_MEMORY_GROWTH = 1, а TOTAL_MEMORY = 512MB успешно? - PullRequest
0 голосов
/ 27 апреля 2019

У меня есть проект 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 голосов
/ 02 мая 2019

Я понял, что происходит. Это было из-за моей собственной наивности предмета. 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]);

Я ненавижу отвечать на свои вопросы ... но, надеюсь, это избавит кого-то от головной боли, если они столкнутся с этим. Любой, возможно, кто-то с большим опытом в этом вопросе может прояснить любую часть этого ответа, которая может понадобиться.

...