Emscripten Uncaught RangeError: Source is too large, multiple Float32Arrays
Posted
技术标签:
【中文标题】Emscripten Uncaught RangeError: Source is too large, multiple Float32Arrays【英文标题】: 【发布时间】:2015-07-21 21:47:56 【问题描述】:我正在尝试为 4x4 矩阵相乘运行 C++ 函数。 2天后,它终于起作用了,但不如预期的那样。
通常参数被反馈给函数,然后是这一行:
dataHeap2.set( new Uint8Array(data2.buffer) );
产生错误“Uncaught RangeError: Source is too large”
乍一看,它看起来只是一个普通的 Float32Array,有 16 个元素,但在查看它的缓冲区大小后,它似乎有所不同
console.log(data2.buffer.bufferLength);
结果不是 64 字节,而是像 3342345 这样的巨大数字。这是问题所在吗?我通过手动复制值(如下所示)找到了一种解决方法,将其返回,然后问题就消失了。不幸的是,它使我的代码比直接在缓冲区上运行要慢得多。
// bad solution - which works
for(var i = 0; i < 16; i++)
dataTarget[i] = result[i];
希望今晚我能找到一个更好的解决方案,并且我想继续使用这个函数,因为它使用 ASM.JS + SIMD 编译的 C++ 代码来动画多个字符。在纯 javascript 中它仍然太慢。这是整个功能。我很确定 data2 正在使用 Emscripten 的 HEAP,我想避免它。
matrix4multiply = function(data, data2)
// Import function from Emscripten generated file
var mmul_vec4 = Module.cwrap(
'mmul_vec4', 'number', ['number', 'number', 'number']
);
var dataTarget = new Float32Array(16);
// Get data byte size, allocate memory on Emscripten heap, and get pointer
var nDataBytes = dataTarget.length * dataTarget.BYTES_PER_ELEMENT;
// First matrix copy data to Emscripten heap
var dataPtr = Module._malloc(nDataBytes);
var dataPtr2 = Module._malloc(nDataBytes);
var dataPtr3 = Module._malloc(nDataBytes);
var dataHeap = new Uint8Array(Module.HEAPU8.buffer, dataPtr, nDataBytes);
dataHeap.set( new Uint8Array(data.buffer) );
// second matrix allocate and copy to emscripten's heap
var dataHeap2 = new Uint8Array(Module.HEAPU8.buffer, dataPtr2, nDataBytes);
dataHeap2.set( new Uint8Array(data2.buffer) );
// target heap
var targetHeap = new Uint8Array(Module.HEAPU8.buffer, dataPtr3, nDataBytes);
targetHeap.set( new Uint8Array(dataTarget.buffer) );
// Call the function by passing a number pointing to the byte location of
// the array of pointers on the Emscripten heap. Emscripten knows what to do!
mmul_vec4(dataHeap.byteOffset, dataHeap2.byteOffset, targetHeap.byteOffset);
// get result
var result = new Float32Array(targetHeap.buffer, targetHeap.byteOffset, 16);
// bad solution - which works
//for(var i = 0; i < 16; i++)
// dataTarget[i] = result[i];
//
// Free memory
Module._free(dataHeap.byteOffset);
Module._free(dataHeap2.byteOffset);
Module._free(targetHeap.byteOffset);
return result;
编辑:简化版不用担心 malloc 等
new Uint8Array(Module.HEAPU8.buffer, this.dataPtr, 64).set( new Uint8Array(data.buffer, data.byteOffset, 64) );
// second matrix allocate and copy to emscripten's heap
new Uint8Array(Module.HEAPU8.buffer, this.dataPtr + 72, 64).set( new Uint8Array(data2.buffer, data2.byteOffset, 64) );
// multiply first two parameters and return in the last one
this.mmul_vec4(this.dataPtr, this.dataPtr + 72, this.dataPtr + 144);
// like that it works, unfotunately copying is being made here
return new Float32Array(Module.HEAPU8.buffer.slice(this.dataPtr + 144, this.dataPtr + 208));
// with this version (if uncommented) there's just white screen(but it looks like the game is working.
//return new Float32Array(Module.HEAPU8.buffer, this.dataPtr + 144, 16);
【问题讨论】:
Asm.js 似乎需要单个堆缓冲区。可能需要复制或 malloc/free 管理。 asmjs.org/spec/latest/#validatemodule-f @zakki 好的,所以我认为复制到传递的 targetFloat32 会起作用,但我不知道如何在不创建现有 Float32Array 的情况下将此 Module.HEAPU8.buffer、this.dataPtr + 144、16像这样的新实例:new Float32Array(Module.HEAPU8.buffer, this.dataPtr + 144, 16)。太慢了。 有类似的问题原来是缓冲区初始化为小于设置的数据大小的值。 【参考方案1】:要使用 data2
的头部 64 字节,请指定偏移量和长度。
dataHeap2.set( new Uint8Array(data2.buffer, data2.byteOffset, nDataBytes) );
【讨论】:
谢谢,我已经试过了。获取“RangeError:起始偏移量太大:” 其实没有错误。我的游戏中只有白屏。【参考方案2】:这似乎解决了问题,但恐怕 .set 函数只复制数据,这不是一个真正的解决方案。至少比for循环克隆更优雅一点(但它比“Module.HEAPU8.buffer.slice”更好)?
matrix4multiplyBlocking = function(data, data2, dataTarget)
//console.log(new Float32Array(data2.buffer, data2.byteOffset, 16));
// Copy data to Emscripten heap
new Uint8Array(Module.HEAPU8.buffer, this.dataPtr, 64).set( new Uint8Array(data.buffer, data.byteOffset, 64) );
// second matrix allocate and copy to emscripten's heap
new Uint8Array(Module.HEAPU8.buffer, this.dataPtr + 72, 64).set( new Uint8Array(data2.buffer, data2.byteOffset, 64) );
// multiply first two parameters and return in the last one
this.mmul_vec4(this.dataPtr, this.dataPtr + 72, this.dataPtr + 144);
// Free memory
//Module._free(this.dataPtr);
dataTarget.set(new Float32Array(Module.HEAPU8.buffer, this.dataPtr + 144, 16));
;
我也讨厌创建新的 Float32Array 实例,因为它会减慢一切
【讨论】:
以上是关于Emscripten Uncaught RangeError: Source is too large, multiple Float32Arrays的主要内容,如果未能解决你的问题,请参考以下文章