JavaScript中Typed Arrays的优点是它们在C中的工作方式相同或相似吗?

ale*_*lex 23 javascript c memory arrays typed-arrays

我一直在使用JavaScript中的Typed Arrays.

var buffer = new ArrayBuffer(16);
var int32View = new Int32Array(buffer);
Run Code Online (Sandbox Code Playgroud)

我想象普通数组([1, 257, true])在JavaScript中性能较差,因为它们的值可以是任何类型,因此,在内存中达到偏移量并非易事.

我原本以为JavaScript数组下标的工作方式与对象相同(因为它们有许多相似之处),并且基于哈希映射,需要基于散列的查找.但我没有找到太多可信的信息来证实这一点.

所以,我假设Typed Arrays表现如此出色的原因是因为它们像C中的普通数组一样工作,它们总是被输入.给出上面的初始代码示例,并希望获得类型化数组中的第10个值...

var value = int32View[10];
Run Code Online (Sandbox Code Playgroud)
  • 类型是Int32,因此每个值必须由32位或4字节组成.
  • 下标是10.
  • 因此,该值的内存中的位置是<array offset> + (4 * 10),然后读取4字节以获得总值.

我基本上只想确认我的假设.我的想法是否正确,如果没有,请详细说明.

我检查了V8源代码,看看我是否可以自己回答,但我的C生锈了,我对C++不太熟悉.

Ash*_*ain 44

由于性能原因,TypeGL Arrays由WebGL标准委员会设计.通常Javascript数组是通用的,可以保存对象,其他数组等等 - 并且元素在内存中不一定是顺序的,就像它们在C中一样.WebGL要求缓冲区在内存中是顺序的,因为这就是底层C API所期望的他们.如果不使用Typed Arrays,将普通数组传递给WebGL函数需要大量工作:必须检查每个元素,检查类型,如果它是正确的(例如浮点数),则将其复制到单独的顺序类C缓冲区,然后将该顺序缓冲区传递给C API.哎哟 - 很多工作!对于性能敏感的WebGL应用程序,这可能会导致帧速率大幅下降.

另一方面,正如您在问题中所建议的那样,Typed Arrays在其幕后存储中已经使用了顺序C类缓冲区.当您写入类型化数组时,您确实在后台分配了一个类似C的数组.出于WebGL的目的,这意味着缓冲区可以由相应的C API直接使用.

请注意,您的内存地址计算还不够:浏览器还必须检查数组,以防止超出范围的访问.这必须发生在任何类型的Javascript数组中,但在许多情况下,聪明的Javascript引擎可以省略检查,因为它可以证明索引值已经在边界内(例如从0循环到数组的长度).它还必须检查数组索引是否真的是一个数字而不是字符串或其他东西!但它本质上就像你描述的那样,使用类似C的寻址.

但是......那还不是全部!在某些情况下,聪明的Javascript引擎也可以推断出普通Javascript数组的类型.在像V8这样的引擎中,如果你创建一个普通的Javascript数组并且只存储浮点数,V8可以乐观地决定它是一个浮点数组并优化它为它生成的代码.然后,性能可以等同于类型化数组.因此,实际上不需要类型化数组来达到最大性能:只是可预测地使用数组(每个元素都是相同类型),并且一些引擎也可以优化它.

那么为什么类型化数组仍然需要存在?

  • 推断数组类型的优化非常复杂.如果V8推断出一个普通数组只有浮点数,那么你将一个对象存储在一个元素中,它必须去优化并重新生成使该数组再次通用的代码.所有这一切都是透明的,这是一项非常成就的成就.类型化数组更加简单:它们保证是一种类型,并且您无法在其中存储其他对象.
  • 永远不会保证优化发生; 您可以只在普通阵列中存储浮动,但引擎可能会出于各种原因决定不对其进行优化.
  • 它们更简单的事实意味着其他不太复杂的JavaScript引擎可以轻松实现它们.他们不需要所有先进的去优化支持.
  • 即使使用非常先进的发动机,也可以使用证明优化是非常困难的,有时甚至是不可能的.类型化阵列显着简化了引擎需要围绕它进行优化的证明级别.从类型化数组返回的值肯定是某种类型,并且引擎可以针对该类型的结果进行优化.从普通数组返回的值理论上可以是任何类型,并且引擎可能无法证明它总是具有相同的类型结果,因此生成效率较低的代码.因此,更容易优化类型化数组周围的代码.
  • 键入的数组删除了出错的机会.你不能不小心存放一个物体,突然间性能会更差.

因此,简而言之,普通数组在理论上可以与类型数组一样快.但是,类型化阵列可以更轻松地达到最佳性能.


ree*_*ece 7

是的,你大多是正确的.使用标准的JavaScript数组,JavaScript引擎必须假设数组中的数据是所有对象.它仍然可以将其存储为类似C的数组/向量,其中对内存的访问仍然与您描述的一样.问题是数据不是值,而是引用该值(对象)的东西.

因此,执行a[i] = b[i] + 2需要引擎:

  1. 访问索引为i的b中的对象;
  2. 检查对象的类型;
  3. 从对象中提取值;
  4. 将值加2;
  5. 使用4中新计算的值创建一个新对象;
  6. 将步骤5中的新对象分配到at索引i.

使用类型化数组,引擎可以:

  1. 访问索引i的b中的值(包括将其放入CPU寄存器);
  2. 将值增加2;
  3. 将步骤2中的新对象分配到at索引i.

注意:这些不是JavaScript引擎将执行的确切步骤,因为这取决于正在编译的代码(包括周围代码)和相关引擎.

这允许得到的计算更有效.此外,类型化数组具有内存布局保证(n字节值的数组),因此可用于直接与数据(音频,视频等)接口.

  • 你是对的,但是现在大多数普通数组也是类型化的,以避免自动装箱的成本。如果你只存储 int,那么你会得到一个 int 数组(在现代 JS 引擎中)。如果您尝试将非数字存储为一,则类型化数组也可能需要类型检查。 (2认同)