大家好,欢迎来到IT知识分享网。
有很多方法可以在 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 是预先生成的 100×100 矩阵,太大了,无法放入测试文件中。
需要注意的是,我认为 Deno 至少不再允许您设置迭代或预热迭代。我认为它只是寻找数字的收敛。实际运行次数显示在 JSON 输出中,并且每个测试略有不同。
以下是我们的操作方式:
Name |
min |
max |
avg |
p75 |
p99 |
p995 |
Add 1×1 (Func) |
63ns |
180ns |
70ns |
74ns |
113ns |
124ns |
Add 2×2 (Func) |
144ns |
208ns |
152ns |
158ns |
184ns |
196ns |
Add 4×4 (Func) |
312ns |
373ns |
329ns |
335ns |
370ns |
373ns |
Add 8×8 (Func) |
694ns |
930ns |
724ns |
731ns |
930ns |
930ns |
Add 16×16 (Func) |
1798ns |
1942ns |
1836ns |
1843ns |
1942ns |
1942ns |
Add 32×32 (Func) |
5274ns |
6599ns |
5495ns |
5605ns |
6599ns |
6599ns |
Add 64×64 (Func) |
13000ns |
ns |
17451ns |
16300ns |
41900ns |
60700ns |
Add 100×100 (Func) |
30800ns |
ns |
40269ns |
38200ns |
ns |
ns |
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 1×1 (Loop) |
28ns |
210ns |
46ns |
47ns |
142ns |
168ns |
Add 2×2 (Loop) |
55ns |
163ns |
71ns |
76ns |
125ns |
143ns |
Add 4×4 (Loop) |
122ns |
227ns |
143ns |
151ns |
195ns |
225ns |
Add 8×8 (Loop) |
360ns |
807ns |
411ns |
422ns |
744ns |
807ns |
Add 16×16 (Loop) |
1179ns |
1246ns |
1208ns |
1217ns |
1246ns |
1246ns |
Add 32×32 (Loop) |
5031ns |
5216ns |
5090ns |
5105ns |
5216ns |
5216ns |
Add 64×64 (Loop) |
14300ns |
ns |
20651ns |
19200ns |
52900ns |
ns |
Add 100×100 (Loop) |
38200ns |
ns |
54401ns |
54100ns |
ns |
ns |
循环开始时速度更快,但一旦达到 32×32 左右,它们就等于 .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 1×1 (Loop Prealloc) |
13ns |
137ns |
18ns |
20ns |
56ns |
73ns |
Add 2×2 (Loop Prealloc) |
25ns |
65ns |
28ns |
27ns |
45ns |
53ns |
Add 4×4 (Loop Prealloc) |
61ns |
152ns |
73ns |
78ns |
124ns |
129ns |
Add 8×8 (Loop Prealloc) |
203ns |
444ns |
228ns |
232ns |
348ns |
434ns |
Add 16×16 (Loop Prealloc) |
710ns |
942ns |
762ns |
768ns |
942ns |
942ns |
Add 32×32 (Loop Prealloc) |
2648ns |
2769ns |
2700ns |
2716ns |
2769ns |
2769ns |
Add 64×64 (Loop Prealloc) |
9500ns |
ns |
10926ns |
10100ns |
25000ns |
35800ns |
Add 100×100 (Loop Prealloc) |
24500ns |
ns |
28392ns |
26300ns |
62100ns |
ns |
成功了!我们比开始时快了 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),情况还不算太糟,因为我们拥有的东西数量非常有限,通常只有 4×4。在机器学习中,这可能会导致问题。
这是一个为展开循环生成 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 1×1 (unrolled) |
7ns |
34ns |
8ns |
8ns |
19ns |
20ns |
Add 1×1 (unrolled dynamic) |
7ns |
40ns |
8ns |
7ns |
19ns |
20ns |
Add 2×2 (unrolled) |
11ns |
46ns |
13ns |
12ns |
26ns |
29ns |
Add 2×2 (unrolled dynamic) |
11ns |
39ns |
12ns |
12ns |
27ns |
29ns |
Add 4×4 (unrolled) |
36ns |
159ns |
59ns |
72ns |
124ns |
130ns |
Add 4×4 (unrolled dynamic) |
36ns |
236ns |
67ns |
84ns |
156ns |
181ns |
Add 8×8 (unrolled) |
92ns |
243ns |
130ns |
142ns |
235ns |
242ns |
Add 8×8 (unrolled dynamic) |
89ns |
262ns |
113ns |
119ns |
186ns |
209ns |
Add 16×16 (unrolled) |
500ns |
ns |
734ns |
600ns |
3400ns |
10500ns |
Add 16×16 (unrolled dynamic) |
500ns |
ns |
799ns |
600ns |
6400ns |
10600ns |
Add 32×32 (unrolled) |
73800ns |
ns |
83976ns |
85200ns |
ns |
ns |
Add 32×32 (unrolled dynamic) |
73000ns |
ns |
90772ns |
90900ns |
ns |
ns |
Add 64×64 (unrolled) |
ns |
ns |
ns |
ns |
ns |
ns |
Add 64×64 (unrolled dynamic) |
ns |
ns |
ns |
ns |
ns |
ns |
Add 100×100 (unrolled) |
ns |
ns |
ns |
ns |
ns |
ns |
Add 100×100 (unrolled dynamic) |
ns |
ns |
ns |
ns |
ns |
ns |
对于较小的值,这是一个很大的改进,比预分配的循环快了大约 1.5 到 2 倍,但对于较大的值,速度要慢得多,这可不是件好事。我不确定为什么会这样,也许这与函数本身的大小有关?生成的代码非常庞大。此外,动态生成与写出它们基本相同。因此,如果想节省有效载荷(并且不受 CSP 的限制),你可以动态创建它们而不会受到任何惩罚。
5、展平数组
我认为我们可以节省的另一件事是数组。从技术上讲,我们不需要有很多嵌套数组,它们会增加一些创建开销。所以现在一个 2×2 数组看起来像这样:
[ 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 1×1 (flat) |
9ns |
53ns |
10ns |
10ns |
24ns |
29ns |
Add 2×2 (flat) |
14ns |
49ns |
15ns |
15ns |
29ns |
30ns |
Add 4×4 (flat) |
32ns |
107ns |
40ns |
46ns |
86ns |
94ns |
Add 8×8 (flat) |
97ns |
167ns |
110ns |
113ns |
143ns |
157ns |
Add 16×16 (flat) |
400ns |
548ns |
436ns |
447ns |
517ns |
548ns |
Add 32×32 (flat) |
1985ns |
2900ns |
2222ns |
2276ns |
2900ns |
2900ns |
Add 64×64 (flat) |
8512ns |
10514ns |
8775ns |
8715ns |
10514ns |
10514ns |
Add 100×100 (flat) |
15500ns |
ns |
23261ns |
21800ns |
54200ns |
ns |
在处理较大的矩阵时,它比我们之前的最佳结果快了约 20%,但在处理 1×1 和 2×2 时,它比展开时慢了 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 1×1 (flat col major) |
9ns |
41ns |
10ns |
9ns |
21ns |
22ns |
Add 2×2 (flat col major) |
14ns |
41ns |
15ns |
14ns |
29ns |
32ns |
Add 4×4 (flat col major) |
32ns |
79ns |
37ns |
37ns |
61ns |
67ns |
Add 8×8 (flat col major) |
101ns |
156ns |
114ns |
116ns |
147ns |
153ns |
Add 16×16 (flat col major) |
423ns |
532ns |
453ns |
465ns |
513ns |
532ns |
Add 32×32 (flat col major) |
2047ns |
3228ns |
2199ns |
2258ns |
3228ns |
3228ns |
Add 64×64 (flat col major) |
7500ns |
ns |
10417ns |
10200ns |
26200ns |
37000ns |
Add 100×100 (flat col major) |
19800ns |
ns |
25090ns |
23500ns |
63000ns |
ns |
事实证明,列主遍历实际上比行主遍历慢一点。这可能是因为缓存行的读取方式更优化。
但是,由于逐元素添加非常简单,我们实际上可以放弃循环结构,只需使用单个循环线性添加所有元素即可。
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 1×1 (flat simple) |
7ns |
46ns |
8ns |
8ns |
18ns |
20ns |
Add 2×2 (flat simple) |
9ns |
54ns |
10ns |
10ns |
23ns |
26ns |
Add 4×4 (flat simple) |
18ns |
77ns |
24ns |
28ns |
51ns |
56ns |
Add 8×8 (flat simple) |
55ns |
159ns |
73ns |
78ns |
125ns |
136ns |
Add 16×16 (flat simple) |
276ns |
405ns |
315ns |
335ns |
393ns |
405ns |
Add 32×32 (flat simple) |
1387ns |
1682ns |
1490ns |
1547ns |
1682ns |
1682ns |
Add 64×64 (flat simple) |
6381ns |
7219ns |
6602ns |
6675ns |
7219ns |
7219ns |
Add 100×100 (flat simple) |
9000ns |
ns |
17166ns |
15700ns |
49400ns |
ns |
这比原先快了 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 1×1 (flat unrolled) |
6ns |
53ns |
7ns |
7ns |
19ns |
22ns |
Add 2×2 (flat unrolled) |
7ns |
62ns |
8ns |
8ns |
21ns |
23ns |
Add 4×4 (flat unrolled) |
24ns |
136ns |
37ns |
41ns |
84ns |
93ns |
Add 8×8 (flat unrolled) |
61ns |
185ns |
81ns |
86ns |
131ns |
144ns |
Add 16×16 (flat unrolled) |
300ns |
ns |
508ns |
400ns |
1000ns |
6100ns |
Add 32×32 (flat unrolled) |
63600ns |
ns |
74574ns |
75200ns |
ns |
ns |
Add 64×64 (flat unrolled) |
ns |
ns |
ns |
ns |
ns |
ns |
Add 100×100 (flat unrolled) |
ns |
ns |
ns |
ns |
ns |
ns |
它只是在 1×1 和 2×2 时超越了简单循环,而在此之后,它会在较大尺寸时丢失并变得更糟。
8、类型化数组
因此,我能看到下一个可能的优化领域是实际使用类型。我们可以在 Javascript 中使用类型化数组来实现这一点。这将使我们能够分配一个内存块并减少任何数组结构的开销。但这实际上更重要一些。
通过使用类型化数组,我们实际上可以减少转换。WASM、WebGL 和 WebGPU 等 API 处理内存块,我们需要转换的越少,我们结束的速度就越快。所以我认为即使结果有点慢,仍然有充分的理由去追求它。
虽然我们最终得到了不同的路径,一个用于浮点数,一个用于整数,即使如此,如果我们选择不同的位宽,也可能很重要。
此外,由于我们已经表明平面结构总体上表现更好,所以我们不需要考虑嵌套数组。
为了简洁起见,我不会测试所有类型数组组合,因为我们将开始看到一个一般模式。
Float 64
Name |
min |
max |
avg |
p75 |
p99 |
p995 |
Add 1×1 (F64) |
330ns |
1600ns |
400ns |
397ns |
663ns |
1600ns |
Add 2×2 (F64) |
329ns |
598ns |
393ns |
409ns |
493ns |
598ns |
Add 4×4 (F64) |
393ns |
1786ns |
490ns |
503ns |
662ns |
1786ns |
Add 8×8 (F64) |
490ns |
778ns |
621ns |
664ns |
778ns |
778ns |
Add 16×16 (F64) |
1024ns |
5425ns |
1311ns |
1334ns |
5425ns |
5425ns |
Add 32×32 (F64) |
3346ns |
4707ns |
3772ns |
4115ns |
4707ns |
4707ns |
Add 64×64 (F64) |
8000ns |
ns |
14203ns |
12700ns |
35300ns |
44800ns |
Add 100×100 (F64) |
23200ns |
ns |
35026ns |
33300ns |
82400ns |
ns |
JavaScript 数字是 64 位浮点数。因此,它们的表现比普通的 JavaScript 数组慢确实令人惊讶。处理小数组实际上比 array.map 慢。我猜这与引擎处理它们的方式有关。随着矩阵变大,它们会变得更快,但即使在 100×100 个项目时,它仍然比普通平面数组慢很多。
Float 32
Name |
min |
max |
avg |
p75 |
p99 |
p995 |
Add 1×1 (F32) |
324ns |
554ns |
380ns |
391ns |
506ns |
554ns |
Add 2×2 (F32) |
324ns |
594ns |
391ns |
408ns |
520ns |
594ns |
Add 4×4 (F32) |
396ns |
658ns |
463ns |
489ns |
569ns |
658ns |
Add 8×8 (F32) |
508ns |
822ns |
620ns |
673ns |
822ns |
822ns |
Add 16×16 (F32) |
1148ns |
1784ns |
1345ns |
1422ns |
1784ns |
1784ns |
Add 32×32 (F32) |
3258ns |
3840ns |
3344ns |
3337ns |
3840ns |
3840ns |
Add 64×64 (F32) |
10500ns |
ns |
18473ns |
21600ns |
66500ns |
ns |
Add 100×100 (F32) |
25800ns |
ns |
37062ns |
35800ns |
99800ns |
ns |
F32 数组与 Float64 数组存在同样的问题。尽管更小,但性能几乎相同,因此单纯从速度角度考虑,选择它们毫无意义。事实上,在 100×100 时,F64 数组的速度相当快。我们获得的唯一好处是内存减少一半,这可能是选择它们的一个原因。
Int 32
Name |
min |
max |
avg |
p75 |
p99 |
p995 |
Add 1×1 (I32) |
321ns |
1015ns |
390ns |
398ns |
704ns |
1015ns |
Add 2×2 (I32) |
324ns |
570ns |
390ns |
403ns |
501ns |
570ns |
Add 4×4 (I32) |
372ns |
530ns |
426ns |
443ns |
488ns |
530ns |
Add 8×8 (I32) |
455ns |
621ns |
539ns |
575ns |
616ns |
621ns |
Add 16×16 (I32) |
784ns |
1202ns |
913ns |
966ns |
1202ns |
1202ns |
Add 32×32 (I32) |
2111ns |
2704ns |
2182ns |
2182ns |
2704ns |
2704ns |
Add 64×64 (I32) |
8742ns |
9569ns |
9138ns |
9305ns |
9569ns |
9569ns |
Add 100×100 (I32) |
12600ns |
ns |
22470ns |
21600ns |
50300ns |
72200ns |
I32 再次表现出类似的行为,但在较大的矩阵中开始看到更大的收益。事实上,在 100×100 时,I32 矩阵大约等于平面矩阵。这并不令人惊讶,但如果你正在处理大型整数矩阵,这可能是你的最佳选择。
9、结束语
对于简单的单线程 javascript,我们观察到了一些事情(在 Deno/V8 @ 2023-03-31):
- 循环的性能大多优于 .map,除非值非常大并且只使用嵌套数组(我尝试了一个平面数组,但它不足以复制粘贴数据)。
- 定制的展开函数在 4×4 或更小的非常小的尺寸上效果很好,但无法击败简单的循环并且会非常非常快地下降。
- 减少结构会带来很大的不同。
- 预分配数组会带来巨大的不同,如果可以的话,请始终这样做。
- 类型化数组没有速度优势(但我们可能会获得更少的转换开销和空间节省)。
我们可以用更多方式来处理矩阵,我可能想看看 WASM 和 WebGPU 是什么样子的,它们开销很大,但由于并行性,实际计算速度可能会大幅提高。Web Workers 也是如此。不同的操作也可能有很大差异。矩阵乘法使用左侧和右侧结构的方式不同,可能需要一些不同的策略。但我认为最大的收获是:
对于广义的元素矩阵操作,最好的选择是对普通 JS 数组进行单一平面循环,因为它速度快,扩展性好
完整的对比记录:
Name |
min |
max |
avg |
p75 |
p99 |
p995 |
Add 1×1 (Func) |
63ns |
180ns |
70ns |
74ns |
113ns |
124ns |
Add 1×1 (Loop) |
28ns |
210ns |
46ns |
47ns |
142ns |
168ns |
Add 1×1 (Loop Prealloc) |
13ns |
137ns |
18ns |
20ns |
56ns |
73ns |
Add 1×1 (unrolled) |
7ns |
34ns |
8ns |
8ns |
19ns |
20ns |
Add 1×1 (unrolled dynamic) |
7ns |
40ns |
8ns |
7ns |
19ns |
20ns |
Add 1×1 (flat) |
9ns |
53ns |
10ns |
10ns |
24ns |
29ns |
Add 1×1 (flat col major) |
9ns |
41ns |
10ns |
9ns |
21ns |
22ns |
Add 1×1 (flat simple) |
7ns |
46ns |
8ns |
8ns |
18ns |
20ns |
Add 1×1 (flat unrolled) |
6ns |
53ns |
7ns |
7ns |
19ns |
22ns |
Add 1×1 (F64) |
330ns |
1600ns |
400ns |
397ns |
663ns |
1600ns |
Add 1×1 (F32) |
324ns |
554ns |
380ns |
391ns |
506ns |
554ns |
Add 1×1 (I32) |
321ns |
1015ns |
390ns |
398ns |
704ns |
1015ns |
Add 2×2 (Func) |
144ns |
208ns |
152ns |
158ns |
184ns |
196ns |
Add 2×2 (Loop) |
55ns |
163ns |
71ns |
76ns |
125ns |
143ns |
Add 2×2 (Loop Prealloc) |
25ns |
65ns |
28ns |
27ns |
45ns |
53ns |
Add 2×2 (unrolled) |
11ns |
46ns |
13ns |
12ns |
26ns |
29ns |
Add 2×2 (unrolled dynamic) |
11ns |
39ns |
12ns |
12ns |
27ns |
29ns |
Add 2×2 (flat) |
14ns |
49ns |
15ns |
15ns |
29ns |
30ns |
Add 2×2 (flat col major) |
14ns |
41ns |
15ns |
14ns |
29ns |
32ns |
Add 2×2 (flat simple) |
9ns |
54ns |
10ns |
10ns |
23ns |
26ns |
Add 2×2 (flat unrolled) |
7ns |
62ns |
8ns |
8ns |
21ns |
23ns |
Add 2×2 (F64) |
329ns |
598ns |
393ns |
409ns |
493ns |
598ns |
Add 2×2 (F32) |
324ns |
594ns |
391ns |
408ns |
520ns |
594ns |
Add 2×2 (I32) |
324ns |
570ns |
390ns |
403ns |
501ns |
570ns |
Add 4×4 (Func) |
312ns |
373ns |
329ns |
335ns |
370ns |
373ns |
Add 4×4 (Loop) |
122ns |
227ns |
143ns |
151ns |
195ns |
225ns |
Add 4×4 (Loop Prealloc) |
61ns |
152ns |
73ns |
78ns |
124ns |
129ns |
Add 4×4 (unrolled) |
36ns |
159ns |
59ns |
72ns |
124ns |
130ns |
Add 4×4 (unrolled dynamic) |
36ns |
236ns |
67ns |
84ns |
156ns |
181ns |
Add 4×4 (flat) |
32ns |
107ns |
40ns |
46ns |
86ns |
94ns |
Add 4×4 (flat col major) |
32ns |
79ns |
37ns |
37ns |
61ns |
67ns |
Add 4×4 (flat simple) |
18ns |
77ns |
24ns |
28ns |
51ns |
56ns |
Add 4×4 (flat unrolled) |
24ns |
136ns |
37ns |
41ns |
84ns |
93ns |
Add 4×4 (F64) |
393ns |
1786ns |
490ns |
503ns |
662ns |
1786ns |
Add 4×4 (F32) |
396ns |
658ns |
463ns |
489ns |
569ns |
658ns |
Add 4×4 (I32) |
372ns |
530ns |
426ns |
443ns |
488ns |
530ns |
Add 8×8 (Func) |
694ns |
930ns |
724ns |
731ns |
930ns |
930ns |
Add 8×8 (Loop) |
360ns |
807ns |
411ns |
422ns |
744ns |
807ns |
Add 8×8 (Loop Prealloc) |
203ns |
444ns |
228ns |
232ns |
348ns |
434ns |
Add 8×8 (unrolled) |
92ns |
243ns |
130ns |
142ns |
235ns |
242ns |
Add 8×8 (unrolled dynamic) |
89ns |
262ns |
113ns |
119ns |
186ns |
209ns |
Add 8×8 (flat) |
97ns |
167ns |
110ns |
113ns |
143ns |
157ns |
Add 8×8 (flat col major) |
101ns |
156ns |
114ns |
116ns |
147ns |
153ns |
Add 8×8 (flat simple) |
55ns |
159ns |
73ns |
78ns |
125ns |
136ns |
Add 8×8 (flat unrolled) |
61ns |
185ns |
81ns |
86ns |
131ns |
144ns |
Add 8×8 (F64) |
490ns |
778ns |
621ns |
664ns |
778ns |
778ns |
Add 8×8 (F32) |
508ns |
822ns |
620ns |
673ns |
822ns |
822ns |
Add 8×8 (I32) |
455ns |
621ns |
539ns |
575ns |
616ns |
621ns |
Add 16×16 (Func) |
1798ns |
1942ns |
1836ns |
1843ns |
1942ns |
1942ns |
Add 16×16 (Loop) |
1179ns |
1246ns |
1208ns |
1217ns |
1246ns |
1246ns |
Add 16×16 (Loop Prealloc) |
710ns |
942ns |
762ns |
768ns |
942ns |
942ns |
Add 16×16 (unrolled) |
500ns |
ns |
734ns |
600ns |
3400ns |
10500ns |
Add 16×16 (unrolled dynamic) |
500ns |
ns |
799ns |
600ns |
6400ns |
10600ns |
Add 16×16 (flat) |
400ns |
548ns |
436ns |
447ns |
517ns |
548ns |
Add 16×16 (flat col major) |
423ns |
532ns |
453ns |
465ns |
513ns |
532ns |
Add 16×16 (flat simple) |
276ns |
405ns |
315ns |
335ns |
393ns |
405ns |
Add 16×16 (flat unrolled) |
300ns |
ns |
508ns |
400ns |
1000ns |
6100ns |
Add 16×16 (F64) |
1024ns |
5425ns |
1311ns |
1334ns |
5425ns |
5425ns |
Add 16×16 (F32) |
1148ns |
1784ns |
1345ns |
1422ns |
1784ns |
1784ns |
Add 16×16 (I32) |
784ns |
1202ns |
913ns |
966ns |
1202ns |
1202ns |
Add 32×32 (Func) |
5274ns |
6599ns |
5495ns |
5605ns |
6599ns |
6599ns |
Add 32×32 (Loop) |
5031ns |
5216ns |
5090ns |
5105ns |
5216ns |
5216ns |
Add 32×32 (Loop Prealloc) |
2648ns |
2769ns |
2700ns |
2716ns |
2769ns |
2769ns |
Add 32×32 (unrolled) |
73800ns |
ns |
83976ns |
85200ns |
ns |
ns |
Add 32×32 (unrolled dynamic) |
73000ns |
ns |
90772ns |
90900ns |
ns |
ns |
Add 32×32 (flat) |
1985ns |
2900ns |
2222ns |
2276ns |
2900ns |
2900ns |
Add 32×32 (flat col major) |
2047ns |
3228ns |
2199ns |
2258ns |
3228ns |
3228ns |
Add 32×32 (flat simple) |
1387ns |
1682ns |
1490ns |
1547ns |
1682ns |
1682ns |
Add 32×32 (flat unrolled) |
63600ns |
ns |
74574ns |
75200ns |
ns |
ns |
Add 32×32 (F64) |
3346ns |
4707ns |
3772ns |
4115ns |
4707ns |
4707ns |
Add 32×32 (F32) |
3258ns |
3840ns |
3344ns |
3337ns |
3840ns |
3840ns |
Add 32×32 (I32) |
2111ns |
2704ns |
2182ns |
2182ns |
2704ns |
2704ns |
Add 64×64 (Func) |
13000ns |
ns |
17451ns |
16300ns |
41900ns |
60700ns |
Add 64×64 (Loop) |
14300ns |
ns |
20651ns |
19200ns |
52900ns |
ns |
Add 64×64 (Loop Prealloc) |
9500ns |
ns |
10926ns |
10100ns |
25000ns |
35800ns |
Add 64×64 (unrolled) |
ns |
ns |
ns |
ns |
ns |
ns |
Add 64×64 (unrolled dynamic) |
ns |
ns |
ns |
ns |
ns |
ns |
Add 64×64 (flat) |
8512ns |
10514ns |
8775ns |
8715ns |
10514ns |
10514ns |
Add 64×64 (flat col major) |
7500ns |
ns |
10417ns |
10200ns |
26200ns |
37000ns |
Add 64×64 (flat simple) |
6381ns |
7219ns |
6602ns |
6675ns |
7219ns |
7219ns |
Add 64×64 (flat unrolled) |
ns |
ns |
ns |
ns |
ns |
ns |
Add 64×64 (F64) |
8000ns |
ns |
14203ns |
12700ns |
35300ns |
44800ns |
Add 64×64 (F32) |
10500ns |
ns |
18473ns |
21600ns |
66500ns |
ns |
Add 64×64 (I32) |
8742ns |
9569ns |
9138ns |
9305ns |
9569ns |
9569ns |
Add 100×100 (Func) |
30800ns |
ns |
40269ns |
38200ns |
ns |
ns |
Add 100×100 (Loop) |
38200ns |
ns |
54401ns |
54100ns |
ns |
ns |
Add 100×100 (Loop Prealloc) |
24500ns |
ns |
28392ns |
26300ns |
62100ns |
ns |
Add 100×100 (unrolled) |
ns |
ns |
ns |
ns |
ns |
ns |
Add 100×100 (unrolled dynamic) |
ns |
ns |
ns |
ns |
ns |
ns |
Add 100×100 (flat) |
15500ns |
ns |
23261ns |
21800ns |
54200ns |
ns |
Add 100×100 (flat col major) |
19800ns |
ns |
25090ns |
23500ns |
63000ns |
ns |
Add 100×100 (flat simple) |
9000ns |
ns |
17166ns |
15700ns |
49400ns |
ns |
Add 100×100 (flat unrolled) |
ns |
ns |
ns |
ns |
ns |
ns |
Add 100×100 (F64) |
23200ns |
ns |
35026ns |
33300ns |
82400ns |
ns |
Add 100×100 (F32) |
25800ns |
ns |
37062ns |
35800ns |
99800ns |
ns |
Add 100×100 (I32) |
12600ns |
ns |
22470ns |
21600ns |
50300ns |
72200ns |

原文链接:JS快速矩阵计算 – BimAnt
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/181750.html