Недостаточно памяти при создании экземпляра WebAssembly в Cloudflare Workers

Я создаю модуль WebAssembly в Rust для использования в Cloudflare Workers через wasm-bindgen. Модуль в целом очень простой; у него есть единственная функция с именем process, которая принимает в качестве входных данных два двоичных файла (представленных двумя Uint8BitArrays) и json_value (для интерпретации serde) и возвращает либо None, либо строку, обычно что-то вроде этого.

#[wasm_bindgen]
pub fn process(
    binary_a: &[u8],
    binary_b: &[u8],
    json_value: &JsValue,
) -> Option<String> {
   Some(String::new())
}

Склеивающий код для создания экземпляра модуля WebAssembly почти идентичен команде wasm-bindgen --no-modules, я только изменил условие инициализации в строке 93 на true:

self = {};

(function() {
    var wasm;
    const __exports = {};


    let cachegetUint8Memory = null;
    function getUint8Memory() {
        if (cachegetUint8Memory === null || cachegetUint8Memory.buffer !== wasm.memory.buffer) {
            cachegetUint8Memory = new Uint8Array(wasm.memory.buffer);
        }
        return cachegetUint8Memory;
    }

    let WASM_VECTOR_LEN = 0;

    function passArray8ToWasm(arg) {
        const ptr = wasm.__wbindgen_malloc(arg.length * 1);
        getUint8Memory().set(arg, ptr / 1);
        WASM_VECTOR_LEN = arg.length;
        return ptr;
    }

    const heap = new Array(32);

    heap.fill(undefined);

    heap.push(undefined, null, true, false);

    let stack_pointer = 32;

    function addBorrowedObject(obj) {
        if (stack_pointer == 1) throw new Error('out of js stack');
        heap[--stack_pointer] = obj;
        return stack_pointer;
    }

    let cachedTextDecoder = new TextDecoder('utf-8');

    function getStringFromWasm(ptr, len) {
        return cachedTextDecoder.decode(getUint8Memory().subarray(ptr, ptr + len));
    }

    let cachedGlobalArgumentPtr = null;
    function globalArgumentPtr() {
        if (cachedGlobalArgumentPtr === null) {
            cachedGlobalArgumentPtr = wasm.__wbindgen_global_argument_ptr();
        }
        return cachedGlobalArgumentPtr;
    }

    let cachegetUint32Memory = null;
    function getUint32Memory() {
        if (cachegetUint32Memory === null || cachegetUint32Memory.buffer !== wasm.memory.buffer) {
            cachegetUint32Memory = new Uint32Array(wasm.memory.buffer);
        }
        return cachegetUint32Memory;
    }
    /**
    * @param {Uint8Array} arg0
    * @param {Uint8Array} arg1
    * @param {any} arg2
    * @returns {string}
    */
    __exports.process = function(arg0, arg1, arg2) {
        const ptr0 = passArray8ToWasm(arg0);
        const len0 = WASM_VECTOR_LEN;
        const ptr1 = passArray8ToWasm(arg1);
        const len1 = WASM_VECTOR_LEN;
        const retptr = globalArgumentPtr();
        try {
            wasm.process(retptr, ptr0, len0, ptr1, len1, addBorrowedObject(arg2));
            const mem = getUint32Memory();
            const rustptr = mem[retptr / 4];
            const rustlen = mem[retptr / 4 + 1];
            if (rustptr === 0) return;
            const realRet = getStringFromWasm(rustptr, rustlen).slice();
            wasm.__wbindgen_free(rustptr, rustlen * 1);
            return realRet;


        } finally {
            wasm.__wbindgen_free(ptr0, len0 * 1);
            wasm.__wbindgen_free(ptr1, len1 * 1);
            heap[stack_pointer++] = undefined;

        }

    };

    function init(path_or_module) {
        let instantiation;
        const imports = { './phototaxon_worker_utils': __exports };
        if (true) {
            instantiation = WebAssembly.instantiate(path_or_module, imports)
            .then(instance => {
            return { instance, module: path_or_module }
        });
    } else {
        const data = fetch(path_or_module);
        if (typeof WebAssembly.instantiateStreaming === 'function') {
            instantiation = WebAssembly.instantiateStreaming(data, imports);
        } else {
            instantiation = data
            .then(response => response.arrayBuffer())
            .then(buffer => WebAssembly.instantiate(buffer, imports));
        }
    }
    return instantiation.then(({instance}) => {
        wasm = init.wasm = instance.exports;

    });
};
self.wasm_bindgen = Object.assign(init, __exports);
})();

self.wasm_bindgen(MODULE).then(() => { XXX }).catch(error => console.log(error));

Я использовал cloudworker, чтобы опробовать весь сценарий, и он работал без проблем. Затем я попробовал тот же сценарий с API службы предварительного просмотра, и он работал нормально для нескольких попыток, пока не запустился. кидать ошибки:

RangeError: WebAssembly Instantiation: Out of memory: wasm memory
    at init (worker.js:1200:35)
    at Module.<anonymous> (worker.js:1878:15)
    at __webpack_require__ (worker.js:20:30)
    at worker.js:84:18
    at worker.js:87:10

Это происходит при создании экземпляра, независимо от отправленного запроса (то, что происходит после инициализации, - нет.

Я пытался уменьшить размер своего скрипта Webassembly, но даже функция типа hello-world была отклонена. Я не знаю, как отлаживать, связано ли это с клеевым кодом, кодом ржавчины или службой предварительного просмотра Cloudflare?


person Edouard    schedule 01.02.2019    source источник
comment
Вы используете распределитель по умолчанию? IIRC, вы можете использовать wee_alloc, чтобы значительно уменьшить двоичный размер.   -  person w.brian    schedule 01.02.2019


Ответы (1)


Проблема заключалась в отсутствии емкости String (в функции process), так что модуль WebAssembly не мог выделить необходимую память для работы (вероятно, избыточное выделение). Установив ограничение с String::with_capacity, модуль будет работать правильно без проблем с памятью.

person Edouard    schedule 01.02.2019
comment
Привет, @Edouard, на самом деле, увидев ваш вопрос, мы заметили, что служба предварительного просмотра Workers выдает ошибку для всех вызовов new WebAssembly.Memory(...). Я попытался перезапустить службу и ... это исправило. Очевидно, есть более глубокая ошибка, которую мы будем исследовать. Просто хотел сообщить вам, что когда вы увидели, что он начал работать, возможно, вы ничего не изменили, а потому, что я перезапустил службу предварительного просмотра. (Насколько мы можем судить, эта проблема затрагивала только предварительную версию, а не рабочих, развернутых в производственной среде.) - person Kenton Varda; 02.02.2019
comment
Это имеет смысл. Я отменил изменения, и он снова работал ... пока я не попытался сделать еще несколько запросов, и он больше не работает. Я снова заменил код параметром емкости, и он тоже не работает. Я предполагаю, что мой модуль каким-то образом вылетает из строя всей штукой WebAssembly.Memory? В качестве побочного вопроса, каковы ограничения ресурсов ЦП службы предварительного просмотра? (эквивалент уровня бесплатного пользования 5 мс?) - person Edouard; 02.02.2019
comment
Мне самому не удалось воспроизвести проблему. Не могли бы вы прислать мне по электронной почте название вашей зоны и подробности, чтобы я мог отследить это? Кентон в облачной вспышке. Чтобы ответить на ваш второй вопрос, предел ЦП, при котором мы завершаем запрос, варьируется, но всегда составляет не менее 50 мс, независимо от уровня плана, как в производственной, так и в предварительной версии. - person Kenton Varda; 04.02.2019