为何Uint8Array拷贝到Uint8ClampedArray比拷贝到新Uint8Array更慢?
性能差异原因分析
两种写法性能差距的核心是底层执行逻辑完全不同:
- 直接传入
Uint8Array构造Uint8ClampedArray的逻辑
当TypedArray构造器接收另一个TypedArray实例作为参数时,ECMAScript规范要求必须逐元素完成值转换和校验:对于Uint8ClampedArray来说,每一个传入的元素都要先执行边界 clamping 操作(小于0的转0,大于255的转255),再写入新数组。哪怕Uint8Array的所有值天然落在0-255的范围内,不需要做任何值调整,JS引擎也必须严格按规范执行逐元素判断的流程,无法直接做整块内存拷贝,多出了一轮O(n)的运算开销。
对应测试代码:
const foo = new Uint8Array(0x10000000); // 256MiB console.time('Copy into Uint8ClampedArray'); const bar = new Uint8ClampedArray(foo); console.timeEnd('Copy into Uint8ClampedArray');
该测试在Chrome v96 + MacBook Pro环境下耗时约160ms。
- 克隆
Uint8Array后传入ArrayBuffer的逻辑
第一步new Uint8Array(foo)是同类型TypedArray的拷贝,不需要做任何值校验或转换,JS引擎可以直接调用底层高度优化的内存拷贝函数(如memcpy)完成整块内存的复制,速度非常快。第二步new Uint8ClampedArray(...)接收的是ArrayBuffer实例,此时构造器仅需要创建一个新的数组视图包装现有内存块,不需要做任何元素级别的操作,几乎没有额外开销。整体耗时基本等价于一次纯内存拷贝的耗时,远低于逐元素判断的开销。
对应测试代码:
const foo = new Uint8Array(0x10000000); // 256MiB console.time('Clone, then use ArrayBuffer'); const bar = new Uint8ClampedArray(new Uint8Array(foo).buffer); console.timeEnd('Clone, then use ArrayBuffer');
该测试在同环境下耗时约70ms,Firefox测试结果类似(140ms vs 60ms),Safari差异更明显(550ms vs 30ms)。
浏览器性能差异原因
Safari的性能差距远大于Chrome和Firefox,是因为其JS引擎JavaScriptCore对逐元素clamping操作的优化程度远低于V8(Chrome)和SpiderMonkey(Firefox),但对纯内存块拷贝的优化水平和另外两个引擎基本一致,因此两者的耗时差被进一步放大。
内容的提问来源于stack exchange,提问作者Phil Kang
相关产品推荐
相关产品推荐

