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

JavaScript矩阵快速计算方法(如何用java计算矩阵相乘)

zhezhongyun 2025-05-11 03:04 29 浏览

有很多方法可以在 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

相关推荐

一篇文章带你了解SVG 渐变知识(svg动画效果)

渐变是一种从一种颜色到另一种颜色的平滑过渡。另外,可以把多个颜色的过渡应用到同一个元素上。SVG渐变主要有两种类型:(Linear,Radial)。一、SVG线性渐变<linearGradie...

Vue3 实战指南:15 个高效组件开发技巧解析

Vue.js作为一款流行的JavaScript框架,在前端开发领域占据着重要地位。Vue3的发布,更是带来了诸多令人兴奋的新特性和改进,让开发者能够更高效地构建应用程序。今天,我们就来深入探讨...

CSS渲染性能优化(低阻抗喷油器阻值一般为多少欧)

在当今快节奏的互联网环境中,网页加载速度直接影响用户体验和业务转化率。页面加载时间每增加100毫秒,就会导致显著的流量和收入损失。作为前端开发的重要组成部分,CSS的渲染性能优化不容忽视。为什么CSS...

前端面试题-Vue 项目中,你做过哪些性能优化?

在Vue项目中,以下是我在生产环境中实践过且用户反馈较好的性能优化方案,整理为分类要点:一、代码层面优化1.代码分割与懒加载路由懒加载:使用`()=>import()`动态导入组件,结...

如何通过JavaScript判断Web页面按钮是否置灰?

在JavaScript语言中判断Web页面按钮是否置灰(禁用状态),可以通过以下几种方式实现,其具体情形取决于按钮的禁用方式(原生disabled属性或CSS样式控制):一、检查原生dis...

「图片显示移植-1」 尝试用opengl/GLFW显示图片

GLFW【https://www.glfw.org】调用了opengl来做图形的显示。我最近需要用opengl来显示图像,不能使用opencv等库。看了一个glfw的官网,里面有github:http...

大模型实战:Flask+H5三件套实现大模型基础聊天界面

本文使用Flask和H5三件套(HTML+JS+CSS)实现大模型聊天应用的基本方式话不多说,先贴上实现效果:流式输出:思考输出:聊天界面模型设置:模型设置会话切换:前言大模型的聊天应用从功能...

ae基础知识(二)(ae必学知识)

hi,大家好,我今天要给大家继续分享的还是ae的基础知识,今天主要分享的就是关于ae的路径文字制作步骤(时间关系没有截图)、动态文字的制作知识、以及ae特效的扭曲的一些基本操作。最后再次复习一下ae的...

YSLOW性能测试前端调优23大规则(二十一)---避免过滤器

AlphalmageLoader过滤器是IE浏览器专有的一个关于图片的属性,主要是为了解决半透明真彩色的PNG显示问题。AlphalmageLoader的语法如下:filter:progid:DX...

Chrome浏览器的渲染流程详解(chrome预览)

我们来详细介绍一下浏览器的**渲染流程**。渲染流程是浏览器将从网络获取到的HTML、CSS和JavaScript文件,最终转化为用户屏幕上可见的、可交互的像素画面的过程。它是一个复杂但高度优...

在 WordPress 中如何设置背景色透明度?

最近开始写一些WordPress专业的知识,阅读数奇低,然后我发一些微信昵称技巧,又说我天天发这些小学生爱玩的玩意,写点文章真不容易。那我两天发点专业的东西,两天发点小学生的东西,剩下三天我看着办...

manim 数学动画之旅--图形样式(数学图形绘制)

manim绘制图形时,除了上一节提到的那些必需的参数,还有一些可选的参数,这些参数可以控制图形显示的样式。绘制各类基本图形(点,线,圆,多边形等)时,每个图形都有自己的默认的样式,比如上一节的图形,...

Web页面如此耗电!到了某种程度,会是大损失

现在用户上网大多使用移动设备或者笔记本电脑。对这两者来说,电池寿命都很重要。在这篇文章里,我们将讨论影响电池寿命的因素,以及作为一个web开发者,我们如何让网页耗电更少,以便用户有更多时间来关注我们的...

11.mxGraph的mxCell和Styles样式(graph style)

3.1.3mxCell[翻译]mxCell是顶点和边的单元对象。mxCell复制了模型中可用的许多功能。使用上的关键区别是,使用模型方法会创建适当的事件通知和撤销,而使用单元进行更改时没有更改记...

按钮重复点击:这“简单”问题,为何难住大半面试者与开发者?

在前端开发中,按钮重复点击是一个看似不起眼,实则非常普遍且容易引发线上事故的问题。想象一下:提交表单时,因为网络卡顿或手抖,重复点击导致后端创建了多条冗余数据…这些场景不仅影响用户体验,更可能造成实...