专业编程基础技术教程

网站首页 > 基础教程 正文

JavaScript矩阵快速计算方法

ccvgpt 2024-08-12 14:54:03 基础教程 10 ℃

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

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

JavaScript矩阵快速计算方法

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

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

Tags:

最近发表
标签列表