为什么TypedArrays不比通常的数组快?我想为CLZ使用precalc值(计算前导零函数)。而且我不希望他们像往常一样解释对象?
http://jsperf.com/array-access-speed-2/2
准备代码:
Benchmark.prototype.setup = function() {
var buffer = new ArrayBuffer(0x10000);
var Uint32 = new Uint32Array(buffer);
var arr = [];
for(var i = 0; i < 0x10000; ++i) {
Uint32[i] = (Math.random() * 0x100000000) | 0;
arr[i] = Uint32[i];
}
var sum = 0;
};
测试1:
sum = arr[(Math.random() * 0x10000) | 0];
测试2:
sum = Uint32[(Math.random() * 0x10000) | 0];
PS可能是我的性能测试无效随时纠正我。
答案 0 :(得分:33)
现代引擎将在幕后使用真正的数组,即使你认为他们可以使用Array
,如果你做了让他们认为不能使用true的东西,那么就回到属性映射“数组”阵列。
另请注意,作为radsoc points out,var buffer = new ArrayBuffer(0x10000)
然后var Uint32 = new Uint32Array(buffer)
生成一个Uint32数组,其大小为0x4000(0x10000 / 4),而不是0x10000,因为您提供的值{{1}以字节为单位,但当然每个Uint32Array条目有四个字节。以下所有内容都使用ArrayBuffer
代替(并且始终在此编辑之前)将苹果与苹果进行比较。
让我们从那里开始,new Uint32Array(0x10000)
: http://jsperf.com/array-access-speed-2/11 (遗憾的是,JSPerf已丢失此测试及其结果,现在完全脱机)
这表明,由于您以简单,可预测的方式填充阵列,现代引擎继续使用真正的阵列(具有其性能优势)而不是转移。我们看到两者的表现基本相同。速度的差异可能与采用new Uint32Array(0x10000)
值的类型转换相关,并将其作为Uint32
分配给sum
(尽管如果不推迟类型转换,我会感到惊讶... )。
添加一些混乱,但是:
number
...所以引擎必须依赖于老式的属性映射“数组”,你会发现类型化的数组明显优于传统类型: http://jsperf.com/array-access-speed-2/3 (遗憾的是,JSPerf已经失去了这个测试及其结果)
聪明,这些JavaScript引擎工程师......
尽管如此,您对var Uint32 = new Uint32Array(0x10000);
var arr = [];
for (var i = 0x10000 - 1; i >= 0; --i) {
Uint32[Math.random() * 0x10000 | 0] = (Math.random() * 0x100000000) | 0;
arr[Math.random() * 0x10000 | 0] = (Math.random() * 0x100000000) | 0;
}
var sum = 0;
数组的非数组特性所做的具体事情很重要;考虑:
Array
这仍然可以预测地填充数组,但是我们向它添加了非元素属性(var Uint32 = new Uint32Array(0x10000);
var arr = [];
arr.foo = "bar"; // <== Non-element property
for (var i = 0; i < 0x10000; ++i) {
Uint32[i] = (Math.random() * 0x100000000) | 0;
arr[i] = (Math.random() * 0x100000000) | 0;
}
var sum = 0;
)。 http://jsperf.com/array-access-speed-2/4 (遗憾的是,JSPerf已经失去了这个测试及其结果)显然,引擎非常聪明,并且将非元素属性保留在一边继续为元素属性使用true数组:
与上面的第一次测试相比,我有点无法解释为什么标准数组应该更快。测量误差? foo
中的Vagaries?但我们仍然非常确定Math.random
中的数组特定数据仍然是真正的数组。
然而,如果我们做同样的事情,但填写相反的顺序:
Array
...我们回到输入的类型数组 - 除了IE11: http://jsperf.com/array-access-speed-2/9 (遗憾的是,JSPerf已经失去了这个测试及其结果)
答案 1 :(得分:32)
var buffer = new ArrayBuffer(0x10000);
var Uint32 = new Uint32Array(buffer);
与以下内容不同:
var Uint32 = new Uint32Array(0x10000);
不是因为新的ArrayBuffer(你总是得到一个数组缓冲区:在两种情况下都参见Uint32.buffer),但由于长度参数:对于ArrayBuffer,每个元素有1个字节,Uint32Array每个元素有4个字节。
因此,在第一种情况下(在您的代码中),Uint32.length = 0x1000 / 4并且您的循环超出了4次中的3次。但遗憾的是,你永远不会得到错误,只有糟糕的表现。
使用&#39;新的ArrayBuffer&#39;,您必须像这样声明Uint32:
var buffer = new ArrayBuffer(0x10000 * 4);
var Uint32 = new Uint32Array(buffer);
答案 2 :(得分:-1)
在你的情况下,性能不佳的原因是你在使用Uint32Array时尝试在数组外部读取,因为数组长度有问题。
但如果它不是真正的原因那么:
尝试使用Int32Array而不是Uint32Array。我认为在V8变量中不能有uint32类型但可以有int32 / double / pointer。因此,当您将uint32类型分配给变量时,它将转换为较慢的double。
如果使用32位版本的V8,则变量可以具有int31 / double / pointer类型。因此int32也将被转换为double。但是如果你使用普通数组并且所有值都是int31,则不需要转换,所以通常的数组可以更快。
同样使用int16可能需要一些转换来获取int32(因为符号和一些补码)。 Uint16不需要转换,因为V8只能在左边添加零。
PS。你可能感兴趣的是指针和int31(或x64上的int32)在V8中是相同的东西。这也意味着int32在x64上需要8个字节。这也就是为什么在x86上没有int32类型的原因:因为如果我们使用所有32位来存储整数,我们将不再有任何空间来保存指针。