gpt4 book ai didi

javascript - TypedArray 集合与展开循环 (Javascript)

转载 作者:行者123 更新时间:2023-11-30 10:25:58 26 4
gpt4 key购买 nike

在尝试为自己构建一个 WebGL 3D 库时(主要是学习目的),我遵循了从各种来源找到的文档,这些文档指出 TypedArray 函数 set() (专门针对 Float32Array ),在 C 中应该“和”memcpy 一样快(显然是开玩笑),根据 html5rocks 字面意思是最快的.看起来似乎是正确的(在 javascript 中没有循环设置,消失在一些超快类型的数组中,纯 C 废话,等等)。

我看了一下 glMatrix (顺便说一句,干得好!),并注意到他(作者)说他展开了所有的循环以提高速度。这显然是一个 javascript 大师通常会以尽可能快的速度做的事情,但是,根据我之前的阅读,我认为我对这个库有 1-up,具体来说,他创建了他的库,使其可以同时使用数组和类型数组,因此我认为使用 set() 可以获得更快的速度,因为我只对保留 TypedArray 类型感兴趣。

为了检验我的理论,我设置了这个 jsperf .不仅 set() 相对缺乏速度,而且我尝试过的所有其他技术(在 jsperf 中)都比它好。这是迄今为止最慢的。

最后,我的问题是:为什么?理论上我可以理解一个循环展开在 spidermonkey 或 chrome V8 js 引擎中变得高度优化,但是输给一个 for 循环似乎很荒谬(jsperf 中的 copy2),特别是如果它的意图在理论上是为了加速复制,因为原始连续内存数据类型 (TypedArray)。无论哪种方式,感觉 set() 函数都被破坏了。

这是我的代码吗?我的浏览器? (我使用的是 Firefox 24)还是我缺少其他一些优化理论?任何有助于理解与我的想法和理解相反的结果的帮助都会非常有帮助。

最佳答案

这是一个老问题,但如果您有特定需要优化一些性能不佳的代码,则有理由使用 TypedArray。了解 JavaScript 中的 TypedArray 对象的重要一点是,它们是 View ,表示 ArrayBuffer< 内的字节范围/。底层 ArrayBuffer 实际上表示要操作的连续二进制数据 block ,但我们需要一个 View 才能访问和操作该二进制数据的窗口。

同一 ArrayBuffer 中的独立(甚至重叠)范围可以被多个不同的 TypedArray 对象查看。当您有两个共享相同 ArrayBufferTypedArray 对象时,set 操作非常快。这是因为机器正在使用连续的内存块。

这是一个例子。我们将创建一个 32 字节的 ArrayBuffer,一个长度为 16 的 Uint8Array 代表缓冲区的前 16 个字节,另一个长度为 16 的 Uint8Array 表示最后 16 个字节:

var buffer = new ArrayBuffer(32);
var array1 = new Uint8Array(buffer, 0, 16);
var array2 = new Uint8Array(buffer, 16, 16);

现在我们可以在缓冲区的前半部分初始化一些值:

for (var i = 0; i < 16; i++) array1[i] = i;
console.log(array1); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
console.log(array2); // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

然后非常有效地将这 8 个字节复制到缓冲区的后半部分:

array2.set(array1);
console.log(array1); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
console.log(array2); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

我们可以通过用另一个 View 查看缓冲区来确认这两个数组实际上共享同一个缓冲区。例如,我们可以使用长度为 8 的 Uint32Array,它跨越缓冲区的整个 32 字节:

var array3 = new Uint32Array(buffer)
console.log(array3); // [50462976, 117835012, 185207048, 252579084,
// 50462976, 117835012, 185207048, 252579084]

我修改了一个 JSPerf 测试,我发现它展示了在同一缓冲区上复制的巨大性能提升:

http://jsperf.com/typedarray-set-vs-loop/3

我们在 Chrome 和 Firefox 上的性能提高了一个数量级,甚至比采用双倍长度的普通数组并将前半部分复制到后半部分要快得多。但是我们必须在这里考虑周期/内存权衡。只要我们有对 ArrayBuffer任何单个 View 的引用,缓冲区的其余数据就不会被垃圾收集。一个ArrayBuffer.transfer函数是为 ES7 Harmony 提出的,它可以解决这个问题,让我们无需等待垃圾收集器即可显式释放内存,以及无需复制即可动态增长 ArrayBuffer 的能力。

关于javascript - TypedArray 集合与展开循环 (Javascript),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19553448/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com