You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让JS函数支持多参数类型且不被V8引擎反优化?

类型化数组向量运算函数的V8反优化问题

我需要实现一个用于向量运算的函数,接收**类型化数组(Typed Array)**和整数索引作为参数。但如果运行时向同一个函数传入不同类型的Typed Array(比如Float32Array、Float64Array等),V8引擎会对该函数进行反优化,导致运行速度大幅下降。

复现代码

以下代码在Chrome中可复现该行为:

function Add(Array1, Index1, Array2, Index2){
  Array1[Index1 << 2 | 0] += Array2[Index2 << 2 | 0];
  Array1[Index1 << 2 | 1] += Array2[Index2 << 2 | 1];
  Array1[Index1 << 2 | 2] += Array2[Index2 << 2 | 2];
  Array1[Index1 << 2 | 3] += Array2[Index2 << 2 | 3];
}

const Int8 = new Int8Array(4194304);
const Int16 = new Int16Array(4194304);
const Int32 = new Int32Array(4194304);
const Float32 = new Float32Array(4194304);
const Float64 = new Float64Array(4194304);

const Iterations = 1;

let Start = window.performance.now();
for(let h = 0; h < Iterations; ++h) for(let i = 0; i < 4194304; ++i) Add(Int8, 0, Int8, i);
console.log("Int8: " + (window.performance.now() - Start));

Start = window.performance.now();
for(let h = 0; h < Iterations; ++h) for(let i = 0; i < 4194304; ++i) Add(Int16, 0, Int16, i);
console.log("Int16: " + (window.performance.now() - Start));

Start = window.performance.now();
for(let h = 0; h < Iterations; ++h) for(let i = 0; i < 4194304; ++i) Add(Int32, 0, Int32, i);
console.log("Int32: " + (window.performance.now() - Start));

Start = window.performance.now();
for(let h = 0; h < Iterations; ++h) for(let i = 0; i < 4194304; ++i) Add(Float32, 0, Float32, i);
console.log("Float32: " + (window.performance.now() - Start));

Start = window.performance.now();
for(let h = 0; h < Iterations; ++h) for(let i = 0; i < 4194304; ++i) Add(Float64, 0, Float64, i);
console.log("Float64: " + (window.performance.now() - Start));

性能表现

在Chrome 108环境中运行时,第一个测试(Int8)速度最快,第二到第四个测试速度约慢一倍,第五个测试(Float64)速度比第一个慢近20倍。调整测试顺序后,执行速度变慢的模式基本一致,说明性能差异并非由数值类型不同导致,而是函数被反优化的结果。

尝试的解决方案

我理解函数被反优化的原因,但不确定最优解决方法。目前仅能通过为每种Typed Array类型手动复制一份函数来解决问题,代码如下:

function Add_i8(Array1, Index1, Array2, Index2){
  Array1[Index1 << 2 | 0] += Array2[Index2 << 2 | 0];
  Array1[Index1 << 2 | 1] += Array2[Index2 << 2 | 1];
  Array1[Index1 << 2 | 2] += Array2[Index2 << 2 | 2];
  Array1[Index1 << 2 | 3] += Array2[Index2 << 2 | 3];
}
function Add_i16(Array1, Index1, Array2, Index2){
  Array1[Index1 << 2 | 0] += Array2[Index2 << 2 | 0];
  Array1[Index1 << 2 | 1] += Array2[Index2 << 2 | 1];
  Array1[Index1 << 2 | 2] += Array2[Index2 << 2 | 2];
  Array1[Index1 << 2 | 3] += Array2[Index2 << 2 | 3];
}
function Add_i32(Array1, Index1, Array2, Index2){
  Array1[Index1 << 2 | 0] += Array2[Index2 << 2 | 0];
  Array1[Index1 << 2 | 1] += Array2[Index2 << 2 | 1];
  Array1[Index1 << 2 | 2] += Array2[Index2 << 2 | 2];
  Array1[Index1 << 2 | 3] += Array2[Index2 << 2 | 3];
}
function Add_f32(Array1, Index1, Array2, Index2){
  Array1[Index1 << 2 | 0] += Array2[Index2 << 2 | 0];
  Array1[Index1 << 2 | 1] += Array2[Index2 << 2 | 1];
  Array1[Index1 << 2 | 2] += Array2[Index2 << 2 | 2];
  Array1[Index1 << 2 | 3] += Array2[Index2 << 2 | 3];
}
function Add_f64(Array1, Index1, Array2, Index2){
  Array1[Index1 << 2 | 0] += Array2[Index2 << 2 | 0];
  Array1[Index1 << 2 | 1] += Array2[Index2 << 2 | 1];
  Array1[Index1 << 2 | 2] += Array2[Index2 << 2 | 2];
  Array1[Index1 << 2 | 3] += Array2[Index2 << 2 | 3];
}

const i8 = new Int8Array(4194304);
const i16 = new Int16Array(4194304);
const i32 = new Int32Array(4194304);
const f32 = new Float32Array(4194304);
const f64 = new Float64Array(4194304);

const Iterations = 1;

let Start = window.performance.now();
for(let h = 0; h < Iterations; ++h) for(let i = 0; i < 4194304; ++i) Add_i8(i8, 0, i8, i);
console.log("Int8: " + (window.performance.now() - Start));

Start = window.performance.now();
for(let h = 0; h < Iterations; ++h) for(let i = 0; i < 4194304; ++i) Add_i16(i16, 0, i16, i);
console.log("Int16: " + (window.performance.now() - Start));

Start = window.performance.now();
for(let h = 0; h < Iterations; ++h) for(let i = 0; i < 4194304; ++i) Add_i32(i32, 0, i32, i);
console.log("Int32: " + (window.performance.now() - Start));

Start = window.performance.now();
for(let h = 0; h < Iterations; ++h) for(let i = 0; i < 4194304; ++i) Add_f32(f32, 0, f32, i);
console.log("Float32: " + (window.performance.now() - Start));

Start = window.performance.now();
for(let h = 0; h < Iterations; ++h) for(let i = 0; i < 4194304; ++i) Add_f64(f64, 0, f64, i);
console.log("Float64: " + (window.performance.now() - Start));

这种方法虽然有效,但代码冗余度极高。我尝试过通过工厂函数返回目标函数,但所有返回的函数仍会被反优化——即便每个函数仅用于一种Typed Array类型。难道手动复制函数是让V8将它们视为独立函数的唯一方法吗?


内容的提问来源于stack exchange,提问作者Permille

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 15:40:29