有很多用来表示JS中矩阵数学的方法。有些是可读的,有些是快速的。我想稍微探索差异。某些技术实际上救了我多少钱?
为此,我将要查看一个操作:元素添加以减少总案例,但是差异操作可以稍微改变整体值,尤其是诸如矩阵乘法之类的值,矩阵乘法需要更复杂的应用程序规则。各州也在我的计算机上,使用deNo(v8)较大的i7 8700k。如果有不同的优化,则不同的运行时间可能会有所不同。
简单的功能方式
我以为我会从这里开始,因为这就是我可以为初稿编写它的方式。这是非常优化的代码,但我怀疑性能实际上很差。不变性非常适合避免虫子,但对于perf来说很可怕,尤其是当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输出中,每个测试略有不同。
这是我们的工作方式:
名称 | 最低 | max | avg | p75 | p99 | p995 |
---|---|---|---|---|---|---|
添加1x1(func) | 63ns | 180ns | 70ns | 74NS | 113ns | 124ns |
添加2x2(func) | 144ns | 208NS | 152ns | 158ns | 184ns | 196NS |
添加4x4(func) | 312NS | 373NS | 329NS | 335NS | 370NS | 373NS |
添加8x8(func) | 694ns | 930ns | 724NS | 731NS | 930ns | 930ns |
添加16x16(func) | 1798ns | 1942ns | 1836ns | 1843ns | 1942ns | 1942ns |
添加32x32(func) | 5274NS | 6599ns | 5495NS | 5605NS | 6599ns | 6599ns |
添加64x64(func) | 13000ns | 2331200NS | 17451NS | 16300NS | 41900NS | 60700ns |
添加100x100(func) | 30800NS | 512800NS | 40269ns | 38200NS | 105700NS | 218300NS |
循环
所以我认为我们可以改进的第一种方法是循环。功能有开销
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
的大小与边界检查相同。
名称 | 最低 | max | avg | p75 | p99 | p995 |
---|---|---|---|---|---|---|
添加1x1(loop) | 28ns | 210NS | 46ns | 47ns | 142ns | 168ns |
添加2x2(loop) | 55ns | 163ns | 71NS | 76ns | 125ns | 143ns |
添加4x4(loop) | 122ns | 227NS | 143ns | 151ns | 195ns | 225ns |
添加8x8(loop) | 360NS | 807NS | 411NS | 422NS | 744NS | 807NS |
添加16x16(loop) | 1179ns | 1246ns | 1208NS | 1217ns | 1246ns | 1246ns |
添加32x32(loop) | 5031NS | 5216NS | 5090NS | 5105NS | 5216NS | 5216NS |
添加64x64(loop) | 14300ns | 362400NS | 20651NS | 19200ns | 52900NS | 110500NS |
添加100x100(循环) | 38200NS | 425400NS | 54401NS | 54100NS | 227700NS | 256300NS |
循环启动更快,但是一旦我们击中32x32左右,它们就等于.map
,并且比.map
更大。非常令人惊讶!
预先分配阵列
我的下一个想法是预先分配数组,因为将数组推入数组可能会导致重新尺寸,也许这就是为什么它较慢。
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;
}
名称 | 最低 | max | avg | p75 | p99 | p995 |
---|---|---|---|---|---|---|
添加1x1(loop prealloc) | 13ns | 137ns | 18NS | 20NS | 56ns | 73ns |
添加2x2(loop prealloc) | 25ns | 65ns | 28ns | 27ns | 45ns | 53ns |
添加4x4(loop prealloc) | 61NS | 152ns | 73ns | 78ns | 124ns | 129ns |
添加8x8(loop prealloc) | 203ns | 444ns | 228NS | 232ns | 348NS | 434ns |
添加16x16(loop prealloc) | 710ns | 942NS | 762NS | 768ns | 942NS | 942NS |
添加32x32(循环prealloc) | 2648NS | 2769ns | 2700NS | 2716ns | 2769ns | 2769ns |
添加64x64(循环prealloc) | 9500NS | 372100NS | 10926ns | 10100NS | 25000NS | 35800NS |
添加100x100(循环prealloc) | 24500NS | 515800NS | 28392NS | 26300NS | 62100NS | 204400NS |
那做了这个技巧!我们比开始的地方快1.5倍!
展开循环
如果我们只是删除了所有循环并长时间写了它?
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);
}
由于我们正在使用评估,我们应该确保对输入进行消毒。
const addMatrix1x1Dyn = genMatAddFunc(1,1);
const addMatrix2x2Dyn = genMatAddFunc(2,2);
const addMatrix4x4Dyn = genMatAddFunc(4,4);
// etc.
const addMatrix100x100Dyn = genMatAddFunc(100,100);
名称 | 最低 | max | avg | p75 | p99 | p995 |
---|---|---|---|---|---|---|
添加1x1(展开) | 7ns | 34NS | 8NS | 8NS | 19NS | 20NS |
添加1x1(展开的动态) | 7ns | 40ns | 8NS | 7ns | 19NS | 20NS |
添加2x2(展开) | 11ns | 46ns | 13ns | 12ns | 26ns | 29ns |
添加2x2(展开的动态) | 11ns | 39ns | 12ns | 12ns | 27ns | 29ns |
添加4x4(展开) | 36ns | 159ns | 59ns | 72NS | 124ns | 130ns |
添加4x4(展开的动态) | 36ns | 236ns | 67ns | 84NS | 156ns | 181ns |
添加8x8(展开) | 92NS | 243ns | 130ns | 142ns | 235ns | 242ns |
添加8x8(展开的动态) | 89ns | 262NS | 113ns | 119ns | 186ns | 209ns |
添加16x16(展开) | 500NS | 672800NS | 734ns | 600NS | 3400NS | 10500NS |
添加16x16(展开的动态) | 500NS | 2052000NS | 799ns | 600NS | 6400NS | 10600NS |
添加32x32(展开) | 73800NS | 562500NS | 83976ns | 85200NS | 136400NS | 160600NS |
添加32x32(展开的动态) | 73000ns | 908200NS | 90772NS | 90900NS | 137900NS | 162600NS |
添加64x64(展开) | 328700NS | 737300NS | 350104NS | 343900NS | 574500NS | 587000NS |
添加64x64(展开的动态) | 327600NS | 698800NS | 349201NS | 345400NS | 573900NS | 592400NS |
添加100x100(展开) | 829600NS | 1250900NS | 876580NS | 873700NS | 1143900NS | 1157500NS |
添加100x100(展开的动态) | 816900NS | 1416300NS | 891844NS | 894500NS | 1227700NS | 1288200NS |
对于小价值观,击败预先分配的环路约1.5至2倍,这是一个很大的进步,但对于大型循环的速度较慢。我不确定为什么是这样,也许必须适应功能本身的大小?生成的代码很大。同样,动态的生成基本上与写出它们相同。因此,如果您想节省有效载荷(并且不受CSP的限制),则可以在无惩罚的情况下动态创建这些。
使阵列变平
我认为我们可以保存的另一件事是数组。从技术上讲,我们不需要有很多嵌套阵列,它们会添加一些开销来创建。所以现在有一个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;
}
名称 | 最低 | max | avg | p75 | p99 | p995 |
---|---|---|---|---|---|---|
添加1x1(flat) | 9NS | 53ns | 10ns | 10ns | 24ns | 29ns |
添加2x2(flat) | 14NS | 49ns | 15ns | 15ns | 29ns | 30ns |
添加4x4(flat) | 32ns | 107ns | 40ns | 46ns | 86ns | 94NS |
添加8x8(flat) | 97ns | 167ns | 110NS | 113ns | 143ns | 157ns |
添加16x16(flat) | 400NS | 548NS | 436ns | 447ns | 517NS | 548NS |
添加32x32(flat) | 1985ns | 2900NS | 2222NS | 2276ns | 2900NS | 2900NS |
添加64x64(flat) | 8512NS | 10514NS | 8775NS | 8715NS | 10514NS | 10514NS |
添加100x100(flat) | 15500NS | 701100NS | 23261NS | 21800NS | 54200NS | 194800ns |
在更大的矩阵上比以前的最佳矩阵快20%,但在1x1和2x2上的矩阵速度比独立的矩阵慢20%。由于这些并不重要,我会说这是一个巨大的胜利。
行与专栏专业
我们是否越过行与列相对于列,是否有关系?有人可能会怀疑当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;
}
名称 | 最低 | max | avg | p75 | p99 | p995 |
---|---|---|---|---|---|---|
添加1x1(平面Col Major) | 9NS | 41NS | 10ns | 9NS | 21ns | 22ns |
添加2x2(平面Col Major) | 14NS | 41NS | 15ns | 14NS | 29ns | 32ns |
添加4x4(平面Col Major) | 32ns | 79ns | 37ns | 37ns | 61NS | 67ns |
添加8x8(平面Col Major) | 101NS | 156ns | 114ns | 116ns | 147ns | 153ns |
添加16x16(平面Col Major) | 423ns | 532NS | 453ns | 465ns | 513ns | 532NS |
添加32x32(平面Col Major) | 2047ns | 3228NS | 2199ns | 2258NS | 3228NS | 3228NS |
添加64x64(平面Col Major) | 7500NS | 413800NS | 10417NS | 10200NS | 26200NS | 37000NS |
添加100x100(平面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;
}
名称 | 最低 | max | avg | p75 | p99 | p995 |
---|---|---|---|---|---|---|
添加1x1(平面简单) | 7ns | 46ns | 8NS | 8NS | 18NS | 20NS |
添加2x2(平面简单) | 9NS | 54NS | 10ns | 10ns | 23ns | 26ns |
添加4x4(平面简单) | 18NS | 77ns | 24ns | 28ns | 51NS | 56ns |
添加8x8(平面简单) | 55ns | 159ns | 73ns | 78ns | 125ns | 136ns |
添加16x16(平面简单) | 276ns | 405NS | 315ns | 335NS | 393NS | 405NS |
添加32x32(平面简单) | 1387ns | 1682ns | 1490NS | 1547ns | 1682ns | 1682ns |
添加64x64(平面简单) | 6381NS | 7219NS | 6602NS | 6675NS | 7219NS | 7219NS |
添加100x100(平面简单) | 9000NS | 598000NS | 17166ns | 15700NS | 49400NS | 178400NS |
这就像20%+更快。
展开
我们也可以展开这些内容,看看会发生什么,也许更简单的结构会有所帮助?使用此代码:
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]];
}
我们可以通过这样的评估动态创建它们:
export function genMatAddFlatFunc(rows, cols) {
rows = Number(rows);
cols = Number(cols);
const body = genMatAddFlatBody(rows, cols);
return new Function("a", "b", body);
}
名称 | 最低 | max | avg | p75 | p99 | p995 |
---|---|---|---|---|---|---|
添加1x1(平面展开) | 6ns | 53ns | 7ns | 7ns | 19NS | 22ns |
添加2x2(平面外) | 7ns | 62ns | 8NS | 8NS | 21ns | 23ns |
添加4x4(平面展开) | 24ns | 136ns | 37ns | 41NS | 84NS | 93NS |
添加8x8(平面展开) | 61NS | 185ns | 81NS | 86ns | 131ns | 144ns |
添加16x16(平面展开) | 300NS | 564700NS | 508NS | 400NS | 1000NS | 6100NS |
添加32x32(平面展开) | 63600NS | 826700NS | 74574NS | 75200NS | 133000ns | 162600NS |
添加64x64(平面展开) | 263500NS | 788800NS | 286503NS | 280600NS | 502900NS | 528900NS |
添加100x100(平面展开) | 706400NS | 1760300NS | 764369ns | 758900NS | 1102800NS | 1118900NS |
它只是在1x1和2x2上越过简单的循环,并且在大尺寸的情况下会失去并变得更糟。
打字阵列
因此,我可以看到的下一个可能的优化领域是实际使用类型。我们可以在JavaScript中使用类型阵列进行此操作。这将使我们能够分配一块内存并减少任何数组结构的开销。不过,这实际上更重要。通过使用类型阵列,我们实际上可以减少转换。像WASM,WebGL和WebGPU这样的API处理内存块,而我们要更快地转换的速度就越少。因此,我认为即使事实证明这要慢一点,仍然有充分的理由追求它。尽管我们最终走了不同的路径,但一个用于浮子,一个用于整数,即使我们选择不同的位宽度也可能很重要。另外,由于我们已经证明平坦结构的整体表现更好,因此我们不需要考虑嵌套阵列。简而言之
浮点64
名称 | 最低 | max | avg | p75 | p99 | p995 |
---|---|---|---|---|---|---|
添加1x1(f64) | 330ns | 1600NS | 400NS | 397NS | 663ns | 1600NS |
添加2x2(f64) | 329NS | 598NS | 393NS | 409NS | 493NS | 598NS |
添加4x4(f64) | 393NS | 1786ns | 490NS | 503NS | 662NS | 1786ns |
添加8x8(f64) | 490NS | 778ns | 621NS | 664ns | 778ns | 778ns |
添加16x16(f64) | 1024ns | 5425NS | 1311ns | 1334ns | 5425NS | 5425NS |
添加32x32(f64) | 3346ns | 4707NS | 3772NS | 4115NS | 4707NS | 4707NS |
添加64x64(f64) | 8000NS | 2309700NS | 14203NS | 12700NS | 35300NS | 44800NS |
添加100x100(F64) | 23200NS | 3328400NS | 35026NS | 33300NS | 82400NS | 231000NS |
JavaScript号码是Float 64。因此,这些行为比普通的JavaScript阵列慢,这确实令人惊讶。进行小阵列实际上比array.map
慢。我猜这与引擎如何对待它们有关。随着矩阵变得更大,这些速度变得更快,但是即使在100x100项目中,它仍然比普通的平面阵列要慢。
浮点32
名称 | 最低 | max | avg | p75 | p99 | p995 |
---|---|---|---|---|---|---|
添加1x1(f32) | 324ns | 554NS | 380NS | 391NS | 506ns | 554NS |
添加2x2(f32) | 324ns | 594NS | 391NS | 408NS | 520NS | 594NS |
添加4x4(f32) | 396ns | 658NS | 463ns | 489ns | 569ns | 658NS |
添加8x8(f32) | 508NS | 822NS | 620NS | 673NS | 822NS | 822NS |
添加16x16(f32) | 1148NS | 1784ns | 1345ns | 1422ns | 1784ns | 1784ns |
添加32x32(F32) | 3258NS | 3840NS | 3344ns | 3337NS | 3840NS | 3840NS |
添加64x64(F32) | 10500NS | 1101800NS | 18473ns | 21600NS | 66500NS | 101200NS |
添加100x100(F32) | 25800NS | 1797500NS | 37062NS | 35800NS | 99800NS | 245400NS |
f32阵列与float64s具有相同的问题。尽管较小,但性能几乎是相同的,因此对于纯速度来说,选择它们是没有意义的。实际上,在100x100时,F64阵列的速度更快。我们获得的唯一好处是一半的记忆,这可能是选择这些的原因。
INT 32
名称 | 最低 | max | avg | p75 | p99 | p995 |
---|---|---|---|---|---|---|
添加1x1(i32) | 321NS | 1015ns | 390NS | 398NS | 704NS | 1015ns |
添加2x2(i32) | 324ns | 570NS | 390NS | 403ns | 501NS | 570NS |
添加4x4(i32) | 372NS | 530ns | 426ns | 443ns | 488ns | 530ns |
添加8x8(i32) | 455ns | 621NS | 539ns | 575NS | 616ns | 621NS |
添加16x16(i32) | 784ns | 1202NS | 913NS | 966ns | 1202NS | 1202NS |
添加32x32(i32) | 2111ns | 2704NS | 2182NS | 2182NS | 2704NS | 2704NS |
添加64x64(i32) | 8742NS | 9569ns | 9138NS | 9305NS | 9569ns | 9569ns |
添加100x100(i32) | 12600NS | 2578300NS | 22470NS | 21600NS | 50300NS | 72200NS |
i32的行为再次类似,但开始在较大的矩阵下看到更大的收益。实际上,在100x100时,i32矩阵大约等于平坦基质。并不令人惊讶,但是如果您要处理大整数矩阵,这可能是您的最佳选择。
结论
对于简单的单线读JavaScript,我们已经观察到了一些内容(在Deno/V8 @ 2023-03-31中):
- 循环的性能大多要比
.map
更好,但只有很大的值,并且只有嵌套数组(我尝试了一个平坦的数组,而且还不足以复制数据)。 - 定制的展开功能在非常小的4x4或更小的尺寸上很好地工作,但不会击败一个简单的循环,并且非常非常快速地掉落。
- 减少结构有很大的不同。
- 预分配阵列会使巨大不同,如果可以的话,请始终执行此操作。
- 打字阵列没有任何速度优势(但是我们可能会更少的转换开销和节省空间)。
我们有更多的方法可以处理矩阵,我想看看wasm和webgpu的样子,而高空头顶,但由于并行性而导致的实际计算速度可能会增加。网络工人也是如此。另外,不同的操作可能会差异很大。矩阵乘法对左手和右手结构的使用方式不同,可能需要一些不同的策略。但是我认为最大的外卖:
对广义元素矩阵OP的最佳选择是在正常JS数组上的单个平坦环,因为它快速缩放
数据
名称 | 最低 | max | avg | p75 | p99 | p995 |
---|---|---|---|---|---|---|
添加1x1(func) | 63ns | 180ns | 70ns | 74NS | 113ns | 124ns |
添加1x1(loop) | 28ns | 210NS | 46ns | 47ns | 142ns | 168ns |
添加1x1(loop prealloc) | 13ns | 137ns | 18NS | 20NS | 56ns | 73ns |
添加1x1(展开) | 7ns | 34NS | 8NS | 8NS | 19NS | 20NS |
添加1x1(展开的动态) | 7ns | 40ns | 8NS | 7ns | 19NS | 20NS |
添加1x1(flat) | 9NS | 53ns | 10ns | 10ns | 24ns | 29ns |
添加1x1(平面Col Major) | 9NS | 41NS | 10ns | 9NS | 21ns | 22ns |
添加1x1(平面简单) | 7ns | 46ns | 8NS | 8NS | 18NS | 20NS |
添加1x1(平面展开) | 6ns | 53ns | 7ns | 7ns | 19NS | 22ns |
添加1x1(f64) | 330ns | 1600NS | 400NS | 397NS | 663ns | 1600NS |
添加1x1(f32) | 324ns | 554NS | 380NS | 391NS | 506ns | 554NS |
添加1x1(i32) | 321NS | 1015ns | 390NS | 398NS | 704NS | 1015ns |
添加2x2(func) | 144ns | 208NS | 152ns | 158ns | 184ns | 196NS |
添加2x2(loop) | 55ns | 163ns | 71NS | 76ns | 125ns | 143ns |
添加2x2(loop prealloc) | 25ns | 65ns | 28ns | 27ns | 45ns | 53ns |
添加2x2(展开) | 11ns | 46ns | 13ns | 12ns | 26ns | 29ns |
添加2x2(展开的动态) | 11ns | 39ns | 12ns | 12ns | 27ns | 29ns |
添加2x2(flat) | 14NS | 49ns | 15ns | 15ns | 29ns | 30ns |
添加2x2(平面Col Major) | 14NS | 41NS | 15ns | 14NS | 29ns | 32ns |
添加2x2(平面简单) | 9NS | 54NS | 10ns | 10ns | 23ns | 26ns |
添加2x2(平面外) | 7ns | 62ns | 8NS | 8NS | 21ns | 23ns |
添加2x2(f64) | 329NS | 598NS | 393NS | 409NS | 493NS | 598NS |
添加2x2(f32) | 324ns | 594NS | 391NS | 408NS | 520NS | 594NS |
添加2x2(i32) | 324ns | 570NS | 390NS | 403ns | 501NS | 570NS |
添加4x4(func) | 312NS | 373NS | 329NS | 335NS | 370NS | 373NS |
添加4x4(loop) | 122ns | 227NS | 143ns | 151ns | 195ns | 225ns |
添加4x4(loop prealloc) | 61NS | 152ns | 73ns | 78ns | 124ns | 129ns |
添加4x4(展开) | 36ns | 159ns | 59ns | 72NS | 124ns | 130ns |
添加4x4(展开的动态) | 36ns | 236ns | 67ns | 84NS | 156ns | 181ns |
添加4x4(flat) | 32ns | 107ns | 40ns | 46ns | 86ns | 94NS |
添加4x4(平面Col Major) | 32ns | 79ns | 37ns | 37ns | 61NS | 67ns |
添加4x4(平面简单) | 18NS | 77ns | 24ns | 28ns | 51NS | 56ns |
添加4x4(平面展开) | 24ns | 136ns | 37ns | 41NS | 84NS | 93NS |
添加4x4(f64) | 393NS | 1786ns | 490NS | 503NS | 662NS | 1786ns |
添加4x4(f32) | 396ns | 658NS | 463ns | 489ns | 569ns | 658NS |
添加4x4(i32) | 372NS | 530ns | 426ns | 443ns | 488ns | 530ns |
添加8x8(func) | 694ns | 930ns | 724NS | 731NS | 930ns | 930ns |
添加8x8(loop) | 360NS | 807NS | 411NS | 422NS | 744NS | 807NS |
添加8x8(loop prealloc) | 203ns | 444ns | 228NS | 232ns | 348NS | 434ns |
添加8x8(展开) | 92NS | 243ns | 130ns | 142ns | 235ns | 242ns |
添加8x8(展开的动态) | 89ns | 262NS | 113ns | 119ns | 186ns | 209ns |
添加8x8(flat) | 97ns | 167ns | 110NS | 113ns | 143ns | 157ns |
添加8x8(平面Col Major) | 101NS | 156ns | 114ns | 116ns | 147ns | 153ns |
添加8x8(平面简单) | 55ns | 159ns | 73ns | 78ns | 125ns | 136ns |
添加8x8(平面展开) | 61NS | 185ns | 81NS | 86ns | 131ns | 144ns |
添加8x8(f64) | 490NS | 778ns | 621NS | 664ns | 778ns | 778ns |
添加8x8(f32) | 508NS | 822NS | 620NS | 673NS | 822NS | 822NS |
添加8x8(i32) | 455ns | 621NS | 539ns | 575NS | 616ns | 621NS |
添加16x16(func) | 1798ns | 1942ns | 1836ns | 1843ns | 1942ns | 1942ns |
添加16x16(loop) | 1179ns | 1246ns | 1208NS | 1217ns | 1246ns | 1246ns |
添加16x16(loop prealloc) | 710ns | 942NS | 762NS | 768ns | 942NS | 942NS |
添加16x16(展开) | 500NS | 672800NS | 734ns | 600NS | 3400NS | 10500NS |
添加16x16(展开的动态) | 500NS | 2052000NS | 799ns | 600NS | 6400NS | 10600NS |
添加16x16(flat) | 400NS | 548NS | 436ns | 447ns | 517NS | 548NS |
添加16x16(平面Col Major) | 423ns | 532NS | 453ns | 465ns | 513ns | 532NS |
添加16x16(平面简单) | 276ns | 405NS | 315ns | 335NS | 393NS | 405NS |
添加16x16(平面展开) | 300NS | 564700NS | 508NS | 400NS | 1000NS | 6100NS |
添加16x16(f64) | 1024ns | 5425NS | 1311ns | 1334ns | 5425NS | 5425NS |
添加16x16(f32) | 1148NS | 1784ns | 1345ns | 1422ns | 1784ns | 1784ns |
添加16x16(i32) | 784ns | 1202NS | 913NS | 966ns | 1202NS | 1202NS |
添加32x32(func) | 5274NS | 6599ns | 5495NS | 5605NS | 6599ns | 6599ns |
添加32x32(loop) | 5031NS | 5216NS | 5090NS | 5105NS | 5216NS | 5216NS |
添加32x32(循环prealloc) | 2648NS | 2769ns | 2700NS | 2716ns | 2769ns | 2769ns |
添加32x32(展开) | 73800NS | 562500NS | 83976ns | 85200NS | 136400NS | 160600NS |
添加32x32(展开的动态) | 73000ns | 908200NS | 90772NS | 90900NS | 137900NS | 162600NS |
添加32x32(flat) | 1985ns | 2900NS | 2222NS | 2276ns | 2900NS | 2900NS |
添加32x32(平面Col Major) | 2047ns | 3228NS | 2199ns | 2258NS | 3228NS | 3228NS |
添加32x32(平面简单) | 1387ns | 1682ns | 1490NS | 1547ns | 1682ns | 1682ns |
添加32x32(平面展开) | 63600NS | 826700NS | 74574NS | 75200NS | 133000ns | 162600NS |
添加32x32(f64) | 3346ns | 4707NS | 3772NS | 4115NS | 4707NS | 4707NS |
添加32x32(F32) | 3258NS | 3840NS | 3344ns | 3337NS | 3840NS | 3840NS |
添加32x32(i32) | 2111ns | 2704NS | 2182NS | 2182NS | 2704NS | 2704NS |
添加64x64(func) | 13000ns | 2331200NS | 17451NS | 16300NS | 41900NS | 60700ns |
添加64x64(loop) | 14300ns | 362400NS | 20651NS | 19200ns | 52900NS | 110500NS |
添加64x64(循环prealloc) | 9500NS | 372100NS | 10926ns | 10100NS | 25000NS | 35800NS |
添加64x64(展开) | 328700NS | 737300NS | 350104NS | 343900NS | 574500NS | 587000NS |
添加64x64(展开的动态) | 327600NS | 698800NS | 349201NS | 345400NS | 573900NS | 592400NS |
添加64x64(flat) | 8512NS | 10514NS | 8775NS | 8715NS | 10514NS | 10514NS |
添加64x64(平面Col Major) | 7500NS | 413800NS | 10417NS | 10200NS | 26200NS | 37000NS |
添加64x64(平面简单) | 6381NS | 7219NS | 6602NS | 6675NS | 7219NS | 7219NS |
添加64x64(平面展开) | 263500NS | 788800NS | 286503NS | 280600NS | 502900NS | 528900NS |
添加64x64(f64) | 8000NS | 2309700NS | 14203NS | 12700NS | 35300NS | 44800NS |
添加64x64(F32) | 10500NS | 1101800NS | 18473ns | 21600NS | 66500NS | 101200NS |
添加64x64(i32) | 8742NS | 9569ns | 9138NS | 9305NS | 9569ns | 9569ns |
添加100x100(func) | 30800NS | 512800NS | 40269ns | 38200NS | 105700NS | 218300NS |
添加100x100(循环) | 38200NS | 425400NS | 54401NS | 54100NS | 227700NS | 256300NS |
添加100x100(循环prealloc) | 24500NS | 515800NS | 28392NS | 26300NS | 62100NS | 204400NS |
添加100x100(展开) | 829600NS | 1250900NS | 876580NS | 873700NS | 1143900NS | 1157500NS |
添加100x100(展开的动态) | 816900NS | 1416300NS | 891844NS | 894500NS | 1227700NS | 1288200NS |
添加100x100(flat) | 15500NS | 701100NS | 23261NS | 21800NS | 54200NS | 194800ns |
添加100x100(平面Col Major) | 19800ns | 575300NS | 25090NS | 23500NS | 63000NS | 198500NS |
添加100x100(平面简单) | 9000NS | 598000NS | 17166ns | 15700NS | 49400NS | 178400NS |
添加100x100(平面展开) | 706400NS | 1760300NS | 764369ns | 758900NS | 1102800NS | 1118900NS |
添加100x100(F64) | 23200NS | 3328400NS | 35026NS | 33300NS | 82400NS | 231000NS |
添加100x100(F32) | 25800NS | 1797500NS | 37062NS | 35800NS | 99800NS | 245400NS |
添加100x100(i32) | 12600NS | 2578300NS | 22470NS | 21600NS | 50300NS | 72200NS |