百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

JavaScript矩阵快速计算方法(js矩阵怎么输入输出)

myzbx 2025-07-01 22:12 5 浏览

有很多方法可以在 JS 中表示矩阵数学。有些方法可读性强,有些方法速度快。我想探索一下这些差异。某些技术实际上能为我节省多少时间?

为此,我将只研究一个操作:逐元素加法以减少总案例数,但差异操作可能会稍微改变整体值,尤其是像矩阵乘法这样需要稍微复杂一些的应用程序规则的运算。这些状态也在我的计算机上,它是稍旧的 i7 8700K,使用 Deno,其底层是 v8。如果有不同的优化,像 Bun 这样的不同运行时可能会表现得非常不同。

本文相关的代码可以从这里下载。

NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割

1、简单的函数式方法

我想从这里开始,因为这是我为初稿编写它的方式。它的代码非常优化,但我怀疑性能实际上相当糟糕。不变性对于避免错误非常有用,但对于性能却很糟糕,尤其是当 JS 没有智能副本时。

//mat.js
export function addMatrixFunc(a, b) {
    return a.map((row, ri) => row.map((val, ci) => b[ri][ci] + val));
}

矩阵表示是数组的数组。外部数组是行,内部数组是列。

使用 Deno 的内置基准测试工具,我们可以看到它在不同大小矩阵上的表现。

import { addMatrixFunc } from "./mat.js";
import { mat100A, mat100B } from "./data/mat-data.js";

Deno.bench("Add 1x1", () => {
    addMatrixFunc([[44]], [[65]]);
});

Deno.bench("Add 2x2", () => {
    addMatrixFunc([[44]], [[65]]);
});

Deno.bench("Add 4x4", () => {
    addMatrixFunc([[44]], [[65]]);
});

/* ... */

Deno.bench("Add 100x100", () => {
    addMatrixFunc(mat100A, mat100B);
});

mat100A 和 mat100B 是预先生成的 100x100 矩阵,太大了,无法放入测试文件中。

需要注意的是,我认为 Deno 至少不再允许您设置迭代或预热迭代。我认为它只是寻找数字的收敛。实际运行次数显示在 JSON 输出中,并且每个测试略有不同。

以下是我们的操作方式:

Name

min

max

avg

p75

p99

p995

Add 1x1 (Func)

63ns

180ns

70ns

74ns

113ns

124ns

Add 2x2 (Func)

144ns

208ns

152ns

158ns

184ns

196ns

Add 4x4 (Func)

312ns

373ns

329ns

335ns

370ns

373ns

Add 8x8 (Func)

694ns

930ns

724ns

731ns

930ns

930ns

Add 16x16 (Func)

1798ns

1942ns

1836ns

1843ns

1942ns

1942ns

Add 32x32 (Func)

5274ns

6599ns

5495ns

5605ns

6599ns

6599ns

Add 64x64 (Func)

13000ns

2331200ns

17451ns

16300ns

41900ns

60700ns

Add 100x100 (Func)

30800ns

512800ns

40269ns

38200ns

105700ns

218300ns

2、循环

所以我认为我们可以改进的第一个方法是循环。函数有开销,所以如果我们去掉它,并且更具有命令性,我们就可以更快一些。

export function addMatrixLoop(a, b) {
    const out = [];
    for (let row = 0; row < a.length; row++) {
        const arrayRow = [];
        for (let col = 0; col < a[0].length; col++) {
            arrayRow.push(a[row][col] + b[row][col])
        }
        out.push(arrayRow);
    }
    return out;
}

请注意,我不会进行严格的边界检查,我们只是假设 a 和 b 的大小相同,因为边界检查只会增加开销。

Name

min

max

avg

p75

p99

p995

Add 1x1 (Loop)

28ns

210ns

46ns

47ns

142ns

168ns

Add 2x2 (Loop)

55ns

163ns

71ns

76ns

125ns

143ns

Add 4x4 (Loop)

122ns

227ns

143ns

151ns

195ns

225ns

Add 8x8 (Loop)

360ns

807ns

411ns

422ns

744ns

807ns

Add 16x16 (Loop)

1179ns

1246ns

1208ns

1217ns

1246ns

1246ns

Add 32x32 (Loop)

5031ns

5216ns

5090ns

5105ns

5216ns

5216ns

Add 64x64 (Loop)

14300ns

362400ns

20651ns

19200ns

52900ns

110500ns

Add 100x100 (Loop)

38200ns

425400ns

54401ns

54100ns

227700ns

256300ns

循环开始时速度更快,但一旦达到 32x32 左右,它们就等于 .map,并且大于 .map 时速度更快。非常令人惊讶!

3、预分配数组

我的下一个想法是预分配数组,因为推入数组可能会导致重新调整大小,也许这就是速度较慢的原因。

export function addMatrixLoopPreAlloc(a, b) {
    const out = new Array(a.length);
    for (let row = 0; row < a.length; row++) {
        const arrayRow = new Array(a[0].length);
        for (let col = 0; col < a[0].length; col++) {
            arrayRow[col] = a[row][col] + b[row][col];
        }
        out[row] = arrayRow;
    }
    return out;
}

Name

min

max

avg

p75

p99

p995

Add 1x1 (Loop Prealloc)

13ns

137ns

18ns

20ns

56ns

73ns

Add 2x2 (Loop Prealloc)

25ns

65ns

28ns

27ns

45ns

53ns

Add 4x4 (Loop Prealloc)

61ns

152ns

73ns

78ns

124ns

129ns

Add 8x8 (Loop Prealloc)

203ns

444ns

228ns

232ns

348ns

434ns

Add 16x16 (Loop Prealloc)

710ns

942ns

762ns

768ns

942ns

942ns

Add 32x32 (Loop Prealloc)

2648ns

2769ns

2700ns

2716ns

2769ns

2769ns

Add 64x64 (Loop Prealloc)

9500ns

372100ns

10926ns

10100ns

25000ns

35800ns

Add 100x100 (Loop Prealloc)

24500ns

515800ns

28392ns

26300ns

62100ns

204400ns

成功了!我们比开始时快了 1.5 倍!

4、展开循环

如果我们删除所有循环并用手写出来会怎么样?

export function addMatrix4x4(a, b) {
    return [
        [a[0][0] + b[0][0], a[0][1] + b[0][1], a[0][2] + b[0][2], a[0][3] + b[0][3]],
        [a[1][0] + b[1][0], a[1][1] + b[1][1], a[1][2] + b[1][2], a[1][3] + b[1][3]],
        [a[2][0] + b[2][0], a[2][1] + b[2][1], a[2][2] + b[2][2], a[2][3] + b[2][3]],
        [a[3][0] + b[3][0], a[3][1] + b[3][1], a[3][2] + b[3][2], a[3][3] + b[3][3]]
    ];

}

这不太灵活,因为需要为要添加的每种矩阵形状都提供一个函数。但是,在某些情况下(例如 3D),情况还不算太糟,因为我们拥有的东西数量非常有限,通常只有 4x4。在机器学习中,这可能会导致问题。

这是一个为展开循环生成 javascript 文本的函数:

export function genMatAddBody(rows, cols) {
    let funcBody = "return [\n";

    for (let r = 0; r < rows; r++) {
        funcBody += "\t\t["
        for (let c = 0; c < cols; c++) {
            funcBody += `a[${r}][${c}] + b[${r}][${c}]${c < cols - 1 ? ", " : ""}`
        }
        funcBody += `]${r < rows - 1 ? ", " : ""}\n`
    }

    funcBody += `\t];\n`
    return funcBody;
}
export function genMatAddFunc(rows, cols) {
    rows = Number(rows);
    cols = Number(cols);
    const body = genMatAddBody(rows, cols);
    return new Function("a", "b", body);
}

我也很好奇这种动态生成是否会带来很大的变化:

export function genMatAddFunc(rows, cols) {
    rows = Number(rows); //prevents code injection
    cols = Number(cols);
    const body = genMatAddBody(rows, cols);
    return new Function("a", "b", body);
}

由于我们使用 eval,所以我们应该确保清理输入:

const addMatrix1x1Dyn = genMatAddFunc(1,1);
const addMatrix2x2Dyn = genMatAddFunc(2,2);
const addMatrix4x4Dyn = genMatAddFunc(4,4);
// etc.
const addMatrix100x100Dyn = genMatAddFunc(100,100);

Name

min

max

avg

p75

p99

p995

Add 1x1 (unrolled)

7ns

34ns

8ns

8ns

19ns

20ns

Add 1x1 (unrolled dynamic)

7ns

40ns

8ns

7ns

19ns

20ns

Add 2x2 (unrolled)

11ns

46ns

13ns

12ns

26ns

29ns

Add 2x2 (unrolled dynamic)

11ns

39ns

12ns

12ns

27ns

29ns

Add 4x4 (unrolled)

36ns

159ns

59ns

72ns

124ns

130ns

Add 4x4 (unrolled dynamic)

36ns

236ns

67ns

84ns

156ns

181ns

Add 8x8 (unrolled)

92ns

243ns

130ns

142ns

235ns

242ns

Add 8x8 (unrolled dynamic)

89ns

262ns

113ns

119ns

186ns

209ns

Add 16x16 (unrolled)

500ns

672800ns

734ns

600ns

3400ns

10500ns

Add 16x16 (unrolled dynamic)

500ns

2052000ns

799ns

600ns

6400ns

10600ns

Add 32x32 (unrolled)

73800ns

562500ns

83976ns

85200ns

136400ns

160600ns

Add 32x32 (unrolled dynamic)

73000ns

908200ns

90772ns

90900ns

137900ns

162600ns

Add 64x64 (unrolled)

328700ns

737300ns

350104ns

343900ns

574500ns

587000ns

Add 64x64 (unrolled dynamic)

327600ns

698800ns

349201ns

345400ns

573900ns

592400ns

Add 100x100 (unrolled)

829600ns

1250900ns

876580ns

873700ns

1143900ns

1157500ns

Add 100x100 (unrolled dynamic)

816900ns

1416300ns

891844ns

894500ns

1227700ns

1288200ns

对于较小的值,这是一个很大的改进,比预分配的循环快了大约 1.5 到 2 倍,但对于较大的值,速度要慢得多,这可不是件好事。我不确定为什么会这样,也许这与函数本身的大小有关?生成的代码非常庞大。此外,动态生成与写出它们基本相同。因此,如果想节省有效载荷(并且不受 CSP 的限制),你可以动态创建它们而不会受到任何惩罚。

5、展平数组

我认为我们可以节省的另一件事是数组。从技术上讲,我们不需要有很多嵌套数组,它们会增加一些创建开销。所以现在一个 2x2 数组看起来像这样:

[
    4, 7,
    10, 5
]

但是现在你需要知道尺寸才能实现这个功能,因为不同的矩形可以有相同数量的元素。所以也许我们可以把它变成一个对象。

{
    shape: [2,2],
    data: [
        4, 7,
        10, 5
    ]
}

形状是一个数组而不是属性,因为我们可以将这个想法扩展到 N 维张量。事实上,这就是 tensorflowjs 等库所做的。为了方便起见,让我们构建一些函数来在格式之间进行转换。

export function nestedArrayToFlat(nested){
    return {
        shape: [nested.length, nested[0].length],
        data: nested.flat(Infinity)
    }
}

export function flatToNestedArray(flat){
    const data = new Array(flat.shape[0]);
    for(let row = 0; row < flat.shape[0]; row++){
        const rowArray = new Array(flat.shape[1]);
        for(let col = 0; col < flat.shape[1]; col++){
            rowArray[col] = flat.data[row * flat.shape[1] + col]; 
        }
        data[row] = rowArray;
    }
    return data;
}

到目前为止,我认为预分配数组和循环在扩展到较大值时具有最佳的总体性能,因此我们暂时坚持这一点。这也意味着我将省略平面和循环,因为它们在任何类别中都没有获胜,以及动态,因为它与展开相同。

export function addMatrixFlat(a, b) {
    const out = {
        shape: a.shape,
        data: new Array(a.data.length)
    };
    for (let row = 0; row < a.shape[0]; row++) {
        for (let col = 0; col < a.shape[1]; col++) {
            const index = (row * a.shape[1]) + col;
            out.data[index] = a.data[index] + b.data[index];
        }
    }
    return out;
}

Name

min

max

avg

p75

p99

p995

Add 1x1 (flat)

9ns

53ns

10ns

10ns

24ns

29ns

Add 2x2 (flat)

14ns

49ns

15ns

15ns

29ns

30ns

Add 4x4 (flat)

32ns

107ns

40ns

46ns

86ns

94ns

Add 8x8 (flat)

97ns

167ns

110ns

113ns

143ns

157ns

Add 16x16 (flat)

400ns

548ns

436ns

447ns

517ns

548ns

Add 32x32 (flat)

1985ns

2900ns

2222ns

2276ns

2900ns

2900ns

Add 64x64 (flat)

8512ns

10514ns

8775ns

8715ns

10514ns

10514ns

Add 100x100 (flat)

15500ns

701100ns

23261ns

21800ns

54200ns

194800ns

在处理较大的矩阵时,它比我们之前的最佳结果快了约 20%,但在处理 1x1 和 2x2 时,它比展开时慢了 20%。由于这些并不是太重要,我认为这是一个巨大的胜利。

6、行优先还是列优先

如果我们遍历行还是列,这有关系吗?有人可能会怀疑,当涉及到 CPU 缓存时,这可能会有关系,但让我们测试一下。

export function addMatrixFlatColMajor(a, b) {
    const out = {
        shape: a.shape,
        data: new Array(a.data.length)
    };
    for (let col = 0; col < a.shape[1]; col++) {
        for (let row = 0; row < a.shape[0]; row++) {
            const index = (row * a.shape[1]) + col;
            out.data[index] = a.data[index] + b.data[index];
        }
    }
    return out;
}

Name

min

max

avg

p75

p99

p995

Add 1x1 (flat col major)

9ns

41ns

10ns

9ns

21ns

22ns

Add 2x2 (flat col major)

14ns

41ns

15ns

14ns

29ns

32ns

Add 4x4 (flat col major)

32ns

79ns

37ns

37ns

61ns

67ns

Add 8x8 (flat col major)

101ns

156ns

114ns

116ns

147ns

153ns

Add 16x16 (flat col major)

423ns

532ns

453ns

465ns

513ns

532ns

Add 32x32 (flat col major)

2047ns

3228ns

2199ns

2258ns

3228ns

3228ns

Add 64x64 (flat col major)

7500ns

413800ns

10417ns

10200ns

26200ns

37000ns

Add 100x100 (flat col major)

19800ns

575300ns

25090ns

23500ns

63000ns

198500ns

事实证明,列主遍历实际上比行主遍历慢一点。这可能是因为缓存行的读取方式更优化。

但是,由于逐元素添加非常简单,我们实际上可以放弃循环结构,只需使用单个循环线性添加所有元素即可。

export function addMatrixFlatSimple(a, b) {
    const out = {
        shape: a.shape,
        data: new Array(a.data.length)
    };
    for(let i = 0; i < a.data.length; i++){
        out.data[i] = a.data[i] + b.data[i];
    }
    return out;
}

Name

min

max

avg

p75

p99

p995

Add 1x1 (flat simple)

7ns

46ns

8ns

8ns

18ns

20ns

Add 2x2 (flat simple)

9ns

54ns

10ns

10ns

23ns

26ns

Add 4x4 (flat simple)

18ns

77ns

24ns

28ns

51ns

56ns

Add 8x8 (flat simple)

55ns

159ns

73ns

78ns

125ns

136ns

Add 16x16 (flat simple)

276ns

405ns

315ns

335ns

393ns

405ns

Add 32x32 (flat simple)

1387ns

1682ns

1490ns

1547ns

1682ns

1682ns

Add 64x64 (flat simple)

6381ns

7219ns

6602ns

6675ns

7219ns

7219ns

Add 100x100 (flat simple)

9000ns

598000ns

17166ns

15700ns

49400ns

178400ns

这比原先快了 20% 以上。

7、展开

我们也可以展开这些,看看会发生什么,也许更简单的结构会有所帮助?使用此代码:

export function genMatAddFlatBody(rows, cols){
    let funcBody = "return [\n";

    for (let r = 0; r < rows; r++) {
        for (let c = 0; c < cols; c++) {
            funcBody += `a[${r * cols + c}] + b[${r * cols + c}]${(c * r) < ((rows - 1) * (cols - 1)) ? ", " : ""}`
        }
    }

    funcBody += `];\n`
    return funcBody;
}

我们可以生成如下函数:

export function addMatrixFlat2x2(a, b) {
    return [
        a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]];

}

我们可以使用 eval 像这样动态创建它们:

export function genMatAddFlatFunc(rows, cols) {
    rows = Number(rows);
    cols = Number(cols);
    const body = genMatAddFlatBody(rows, cols);
    return new Function("a", "b", body);
}

Name

min

max

avg

p75

p99

p995

Add 1x1 (flat unrolled)

6ns

53ns

7ns

7ns

19ns

22ns

Add 2x2 (flat unrolled)

7ns

62ns

8ns

8ns

21ns

23ns

Add 4x4 (flat unrolled)

24ns

136ns

37ns

41ns

84ns

93ns

Add 8x8 (flat unrolled)

61ns

185ns

81ns

86ns

131ns

144ns

Add 16x16 (flat unrolled)

300ns

564700ns

508ns

400ns

1000ns

6100ns

Add 32x32 (flat unrolled)

63600ns

826700ns

74574ns

75200ns

133000ns

162600ns

Add 64x64 (flat unrolled)

263500ns

788800ns

286503ns

280600ns

502900ns

528900ns

Add 100x100 (flat unrolled)

706400ns

1760300ns

764369ns

758900ns

1102800ns

1118900ns

它只是在 1x1 和 2x2 时超越了简单循环,而在此之后,它会在较大尺寸时丢失并变得更糟。

8、类型化数组

因此,我能看到下一个可能的优化领域是实际使用类型。我们可以在 Javascript 中使用类型化数组来实现这一点。这将使我们能够分配一个内存块并减少任何数组结构的开销。但这实际上更重要一些。

通过使用类型化数组,我们实际上可以减少转换。WASM、WebGL 和 WebGPU 等 API 处理内存块,我们需要转换的越少,我们结束的速度就越快。所以我认为即使结果有点慢,仍然有充分的理由去追求它。

虽然我们最终得到了不同的路径,一个用于浮点数,一个用于整数,即使如此,如果我们选择不同的位宽,也可能很重要。

此外,由于我们已经表明平面结构总体上表现更好,所以我们不需要考虑嵌套数组。

为了简洁起见,我不会测试所有类型数组组合,因为我们将开始看到一个一般模式。

Float 64

Name

min

max

avg

p75

p99

p995

Add 1x1 (F64)

330ns

1600ns

400ns

397ns

663ns

1600ns

Add 2x2 (F64)

329ns

598ns

393ns

409ns

493ns

598ns

Add 4x4 (F64)

393ns

1786ns

490ns

503ns

662ns

1786ns

Add 8x8 (F64)

490ns

778ns

621ns

664ns

778ns

778ns

Add 16x16 (F64)

1024ns

5425ns

1311ns

1334ns

5425ns

5425ns

Add 32x32 (F64)

3346ns

4707ns

3772ns

4115ns

4707ns

4707ns

Add 64x64 (F64)

8000ns

2309700ns

14203ns

12700ns

35300ns

44800ns

Add 100x100 (F64)

23200ns

3328400ns

35026ns

33300ns

82400ns

231000ns

JavaScript 数字是 64 位浮点数。因此,它们的表现比普通的 JavaScript 数组慢确实令人惊讶。处理小数组实际上比 array.map 慢。我猜这与引擎处理它们的方式有关。随着矩阵变大,它们会变得更快,但即使在 100x100 个项目时,它仍然比普通平面数组慢很多。

Float 32

Name

min

max

avg

p75

p99

p995

Add 1x1 (F32)

324ns

554ns

380ns

391ns

506ns

554ns

Add 2x2 (F32)

324ns

594ns

391ns

408ns

520ns

594ns

Add 4x4 (F32)

396ns

658ns

463ns

489ns

569ns

658ns

Add 8x8 (F32)

508ns

822ns

620ns

673ns

822ns

822ns

Add 16x16 (F32)

1148ns

1784ns

1345ns

1422ns

1784ns

1784ns

Add 32x32 (F32)

3258ns

3840ns

3344ns

3337ns

3840ns

3840ns

Add 64x64 (F32)

10500ns

1101800ns

18473ns

21600ns

66500ns

101200ns

Add 100x100 (F32)

25800ns

1797500ns

37062ns

35800ns

99800ns

245400ns

F32 数组与 Float64 数组存在同样的问题。尽管更小,但性能几乎相同,因此单纯从速度角度考虑,选择它们毫无意义。事实上,在 100x100 时,F64 数组的速度相当快。我们获得的唯一好处是内存减少一半,这可能是选择它们的一个原因。

Int 32

Name

min

max

avg

p75

p99

p995

Add 1x1 (I32)

321ns

1015ns

390ns

398ns

704ns

1015ns

Add 2x2 (I32)

324ns

570ns

390ns

403ns

501ns

570ns

Add 4x4 (I32)

372ns

530ns

426ns

443ns

488ns

530ns

Add 8x8 (I32)

455ns

621ns

539ns

575ns

616ns

621ns

Add 16x16 (I32)

784ns

1202ns

913ns

966ns

1202ns

1202ns

Add 32x32 (I32)

2111ns

2704ns

2182ns

2182ns

2704ns

2704ns

Add 64x64 (I32)

8742ns

9569ns

9138ns

9305ns

9569ns

9569ns

Add 100x100 (I32)

12600ns

2578300ns

22470ns

21600ns

50300ns

72200ns

I32 再次表现出类似的行为,但在较大的矩阵中开始看到更大的收益。事实上,在 100x100 时,I32 矩阵大约等于平面矩阵。这并不令人惊讶,但如果你正在处理大型整数矩阵,这可能是你的最佳选择。

9、结束语

对于简单的单线程 javascript,我们观察到了一些事情(在 Deno/V8 @ 2023-03-31):

  • 循环的性能大多优于 .map,除非值非常大并且只使用嵌套数组(我尝试了一个平面数组,但它不足以复制粘贴数据)。
  • 定制的展开函数在 4x4 或更小的非常小的尺寸上效果很好,但无法击败简单的循环并且会非常非常快地下降。
  • 减少结构会带来很大的不同。
  • 预分配数组会带来巨大的不同,如果可以的话,请始终这样做。
  • 类型化数组没有速度优势(但我们可能会获得更少的转换开销和空间节省)。

我们可以用更多方式来处理矩阵,我可能想看看 WASM 和 WebGPU 是什么样子的,它们开销很大,但由于并行性,实际计算速度可能会大幅提高。Web Workers 也是如此。不同的操作也可能有很大差异。矩阵乘法使用左侧和右侧结构的方式不同,可能需要一些不同的策略。但我认为最大的收获是:

对于广义的元素矩阵操作,最好的选择是对普通 JS 数组进行单一平面循环,因为它速度快,扩展性好

完整的对比记录:

Name

min

max

avg

p75

p99

p995

Add 1x1 (Func)

63ns

180ns

70ns

74ns

113ns

124ns

Add 1x1 (Loop)

28ns

210ns

46ns

47ns

142ns

168ns

Add 1x1 (Loop Prealloc)

13ns

137ns

18ns

20ns

56ns

73ns

Add 1x1 (unrolled)

7ns

34ns

8ns

8ns

19ns

20ns

Add 1x1 (unrolled dynamic)

7ns

40ns

8ns

7ns

19ns

20ns

Add 1x1 (flat)

9ns

53ns

10ns

10ns

24ns

29ns

Add 1x1 (flat col major)

9ns

41ns

10ns

9ns

21ns

22ns

Add 1x1 (flat simple)

7ns

46ns

8ns

8ns

18ns

20ns

Add 1x1 (flat unrolled)

6ns

53ns

7ns

7ns

19ns

22ns

Add 1x1 (F64)

330ns

1600ns

400ns

397ns

663ns

1600ns

Add 1x1 (F32)

324ns

554ns

380ns

391ns

506ns

554ns

Add 1x1 (I32)

321ns

1015ns

390ns

398ns

704ns

1015ns

Add 2x2 (Func)

144ns

208ns

152ns

158ns

184ns

196ns

Add 2x2 (Loop)

55ns

163ns

71ns

76ns

125ns

143ns

Add 2x2 (Loop Prealloc)

25ns

65ns

28ns

27ns

45ns

53ns

Add 2x2 (unrolled)

11ns

46ns

13ns

12ns

26ns

29ns

Add 2x2 (unrolled dynamic)

11ns

39ns

12ns

12ns

27ns

29ns

Add 2x2 (flat)

14ns

49ns

15ns

15ns

29ns

30ns

Add 2x2 (flat col major)

14ns

41ns

15ns

14ns

29ns

32ns

Add 2x2 (flat simple)

9ns

54ns

10ns

10ns

23ns

26ns

Add 2x2 (flat unrolled)

7ns

62ns

8ns

8ns

21ns

23ns

Add 2x2 (F64)

329ns

598ns

393ns

409ns

493ns

598ns

Add 2x2 (F32)

324ns

594ns

391ns

408ns

520ns

594ns

Add 2x2 (I32)

324ns

570ns

390ns

403ns

501ns

570ns

Add 4x4 (Func)

312ns

373ns

329ns

335ns

370ns

373ns

Add 4x4 (Loop)

122ns

227ns

143ns

151ns

195ns

225ns

Add 4x4 (Loop Prealloc)

61ns

152ns

73ns

78ns

124ns

129ns

Add 4x4 (unrolled)

36ns

159ns

59ns

72ns

124ns

130ns

Add 4x4 (unrolled dynamic)

36ns

236ns

67ns

84ns

156ns

181ns

Add 4x4 (flat)

32ns

107ns

40ns

46ns

86ns

94ns

Add 4x4 (flat col major)

32ns

79ns

37ns

37ns

61ns

67ns

Add 4x4 (flat simple)

18ns

77ns

24ns

28ns

51ns

56ns

Add 4x4 (flat unrolled)

24ns

136ns

37ns

41ns

84ns

93ns

Add 4x4 (F64)

393ns

1786ns

490ns

503ns

662ns

1786ns

Add 4x4 (F32)

396ns

658ns

463ns

489ns

569ns

658ns

Add 4x4 (I32)

372ns

530ns

426ns

443ns

488ns

530ns

Add 8x8 (Func)

694ns

930ns

724ns

731ns

930ns

930ns

Add 8x8 (Loop)

360ns

807ns

411ns

422ns

744ns

807ns

Add 8x8 (Loop Prealloc)

203ns

444ns

228ns

232ns

348ns

434ns

Add 8x8 (unrolled)

92ns

243ns

130ns

142ns

235ns

242ns

Add 8x8 (unrolled dynamic)

89ns

262ns

113ns

119ns

186ns

209ns

Add 8x8 (flat)

97ns

167ns

110ns

113ns

143ns

157ns

Add 8x8 (flat col major)

101ns

156ns

114ns

116ns

147ns

153ns

Add 8x8 (flat simple)

55ns

159ns

73ns

78ns

125ns

136ns

Add 8x8 (flat unrolled)

61ns

185ns

81ns

86ns

131ns

144ns

Add 8x8 (F64)

490ns

778ns

621ns

664ns

778ns

778ns

Add 8x8 (F32)

508ns

822ns

620ns

673ns

822ns

822ns

Add 8x8 (I32)

455ns

621ns

539ns

575ns

616ns

621ns

Add 16x16 (Func)

1798ns

1942ns

1836ns

1843ns

1942ns

1942ns

Add 16x16 (Loop)

1179ns

1246ns

1208ns

1217ns

1246ns

1246ns

Add 16x16 (Loop Prealloc)

710ns

942ns

762ns

768ns

942ns

942ns

Add 16x16 (unrolled)

500ns

672800ns

734ns

600ns

3400ns

10500ns

Add 16x16 (unrolled dynamic)

500ns

2052000ns

799ns

600ns

6400ns

10600ns

Add 16x16 (flat)

400ns

548ns

436ns

447ns

517ns

548ns

Add 16x16 (flat col major)

423ns

532ns

453ns

465ns

513ns

532ns

Add 16x16 (flat simple)

276ns

405ns

315ns

335ns

393ns

405ns

Add 16x16 (flat unrolled)

300ns

564700ns

508ns

400ns

1000ns

6100ns

Add 16x16 (F64)

1024ns

5425ns

1311ns

1334ns

5425ns

5425ns

Add 16x16 (F32)

1148ns

1784ns

1345ns

1422ns

1784ns

1784ns

Add 16x16 (I32)

784ns

1202ns

913ns

966ns

1202ns

1202ns

Add 32x32 (Func)

5274ns

6599ns

5495ns

5605ns

6599ns

6599ns

Add 32x32 (Loop)

5031ns

5216ns

5090ns

5105ns

5216ns

5216ns

Add 32x32 (Loop Prealloc)

2648ns

2769ns

2700ns

2716ns

2769ns

2769ns

Add 32x32 (unrolled)

73800ns

562500ns

83976ns

85200ns

136400ns

160600ns

Add 32x32 (unrolled dynamic)

73000ns

908200ns

90772ns

90900ns

137900ns

162600ns

Add 32x32 (flat)

1985ns

2900ns

2222ns

2276ns

2900ns

2900ns

Add 32x32 (flat col major)

2047ns

3228ns

2199ns

2258ns

3228ns

3228ns

Add 32x32 (flat simple)

1387ns

1682ns

1490ns

1547ns

1682ns

1682ns

Add 32x32 (flat unrolled)

63600ns

826700ns

74574ns

75200ns

133000ns

162600ns

Add 32x32 (F64)

3346ns

4707ns

3772ns

4115ns

4707ns

4707ns

Add 32x32 (F32)

3258ns

3840ns

3344ns

3337ns

3840ns

3840ns

Add 32x32 (I32)

2111ns

2704ns

2182ns

2182ns

2704ns

2704ns

Add 64x64 (Func)

13000ns

2331200ns

17451ns

16300ns

41900ns

60700ns

Add 64x64 (Loop)

14300ns

362400ns

20651ns

19200ns

52900ns

110500ns

Add 64x64 (Loop Prealloc)

9500ns

372100ns

10926ns

10100ns

25000ns

35800ns

Add 64x64 (unrolled)

328700ns

737300ns

350104ns

343900ns

574500ns

587000ns

Add 64x64 (unrolled dynamic)

327600ns

698800ns

349201ns

345400ns

573900ns

592400ns

Add 64x64 (flat)

8512ns

10514ns

8775ns

8715ns

10514ns

10514ns

Add 64x64 (flat col major)

7500ns

413800ns

10417ns

10200ns

26200ns

37000ns

Add 64x64 (flat simple)

6381ns

7219ns

6602ns

6675ns

7219ns

7219ns

Add 64x64 (flat unrolled)

263500ns

788800ns

286503ns

280600ns

502900ns

528900ns

Add 64x64 (F64)

8000ns

2309700ns

14203ns

12700ns

35300ns

44800ns

Add 64x64 (F32)

10500ns

1101800ns

18473ns

21600ns

66500ns

101200ns

Add 64x64 (I32)

8742ns

9569ns

9138ns

9305ns

9569ns

9569ns

Add 100x100 (Func)

30800ns

512800ns

40269ns

38200ns

105700ns

218300ns

Add 100x100 (Loop)

38200ns

425400ns

54401ns

54100ns

227700ns

256300ns

Add 100x100 (Loop Prealloc)

24500ns

515800ns

28392ns

26300ns

62100ns

204400ns

Add 100x100 (unrolled)

829600ns

1250900ns

876580ns

873700ns

1143900ns

1157500ns

Add 100x100 (unrolled dynamic)

816900ns

1416300ns

891844ns

894500ns

1227700ns

1288200ns

Add 100x100 (flat)

15500ns

701100ns

23261ns

21800ns

54200ns

194800ns

Add 100x100 (flat col major)

19800ns

575300ns

25090ns

23500ns

63000ns

198500ns

Add 100x100 (flat simple)

9000ns

598000ns

17166ns

15700ns

49400ns

178400ns

Add 100x100 (flat unrolled)

706400ns

1760300ns

764369ns

758900ns

1102800ns

1118900ns

Add 100x100 (F64)

23200ns

3328400ns

35026ns

33300ns

82400ns

231000ns

Add 100x100 (F32)

25800ns

1797500ns

37062ns

35800ns

99800ns

245400ns

Add 100x100 (I32)

12600ns

2578300ns

22470ns

21600ns

50300ns

72200ns


原文链接:JS快速矩阵计算 - BimAnt

相关推荐

C语言速成之数组:C语言数据处理的核心武器,你真的玩透了吗?

程序员Feri一名12年+的程序员,做过开发带过团队创过业,擅长Java、鸿蒙、嵌入式、人工智能等开发,专注于程序员成长的那点儿事,希望在成长的路上有你相伴!君志所向,一往无前!数组:C语言数据处理...

ES6史上最全数JS数组方法合集-02-数组操作

数组生成array.ofletres=Array.of(1,2,3)console.log(res)//[1,2,3]下标定位indexOf用于查找数组中是否存在某个值,如果存...

前端性能拉胯?这 8 个 JavaScript 技巧让你的代码飞起来!

在前端开发的江湖里,JavaScript就是我们手中的“绝世宝剑”。但为啥别人用剑就能轻松斩敌,你的代码却总拖后腿,页面加载慢、交互卡顿?别着急!今天带来8个超实用的JavaScript实...

12种JavaScript中最常用的数组操作整理汇总

数组是最常见的数据结构之一,我们需要绝对自信地使用它。在这里,我将列出JavaScript中最重要的几个数组常用操作片段,包括数组长度、替换元素、去重以及许多其他内容。1、数组长度大多数人都知道可...

手把手教你在Webpack写一个Loader

前言有的时候,你可能在从零搭建Webpack项目很熟悉,配置过各种loader,面试官在Webpack方面问你,是否自己实现过一个loader?如果没有去了解过如果去实现,确实有点尴尬,其...

const关键字到底该什么用?(可以用const关键字定义变量吗)

文|守望先生经授权转载自公众号编程珠玑(id:shouwangxiansheng)前言我们都知道使用const关键字限定一个变量为只读,但它是真正意义上的只读吗?实际中又该如何使用const关键字...

“JavaScript变量声明三兄弟,你真的会用吗?

在JavaScript中,var、let和const是声明变量的关键字,它们在作用域、变量提升、重复声明和重新赋值等方面有显著区别。以下是它们的相同点和不同点,并通过代码示例详细说明。一、相同点声明变...

ES6(二)let 和 const(es6 var let const区别)

let命令let和var差不多,只是限制了有效范围。先定义后使用不管是什么编程语言,不管语法是否允许,都要秉承先定义,然后再使用的习惯,这样不会出幺蛾子。以前JavaScript比较随意,...

js 里面 let 和 const的区别(js中的let)

在JavaScript(包括Vue、Node.js、前端脚本等)中,const和let是用于声明变量的两种方式,它们的主要区别如下:constvslet的区别特性constlet是否...

JDK21新特性:Sequenced Collections

SequencedCollectionsJDK21在JEP431提出了有序集合(SequencedCollections)。引入新的接口来表示有序集合。这样的集合都有一个明确的第一个元素、第二个...

动态编程基础——第 2 部分(动态编程是什么)

有两种方法可以使用动态规划来解决问题。在这篇文章中,我们将了解制表法。请参阅我的动态编程基础——第1部分了解记忆方法。记忆制表什么是动态规划?它是一种简单递归的优化技术。它大大减少了解决给定...

Lambda 函数,你真的的了解吗(lambda函数用法)

什么是lambda函数lambda函数是一个匿名函数,这意味着与其他函数不同,它们没有名称。这是一个函数,它添加两个数字,写成一个命名函数,可以按其名称调用它们:defadd(x,y):...

JavaScript 数组操作方法大全(js数组操作的常用方法有哪些)

数组操作是JavaScript中非常重要也非常常用的技巧。本文整理了常用的数组操作方法(包括ES6的map、forEach、every、some、filter、find、from、of等)...

系列专栏(六):解构赋值(解构赋值默认值)

ES6作为新一代JavaScript标准,已正式与广大前端开发者见面。为了让大家对ES6的诸多新特性有更深入的了解,MozillaWeb开发者博客推出了《ES6InDepth》系列文章。CSDN...

js列表遍历方法解读(js遍历链表)

JavaScript提供了多种遍历数组(或列表)的方法。以下是一些常用的方法及其解读:for循环:vararray=[1,2,3,4,5];for(vari=0;...