介绍
perf是一个测试CPU运行时各项指标(比如cache-miss,band-width)的工具。perf从CPU的Performance Monitor Unit(PMU)计数器中读取这些信息,然后输出。
注意:由于各个厂家的生产CPU的标准不同,perf读取的性能种类也会有一定的差别。
矩阵乘法
在这里主要测试三种矩阵乘法的相关性能——传统乘法、分治算法、Strassen算法。程序都使用C语言写成。
矩阵乘法不是重点,我把代码放在最后了。
perf测试
三种矩阵乘法的程序使用gcc编译之后命名为p1、p2、p3,使用下面三条指令测试三个程序的相关性能:
$ sudo perf stat ./p1
$ sudo perf stat ./p2
$ sudo perf stat ./p3
以p1的结果为例,perf输出如下
Performance counter stats for './p1 -e cache-misses':
176.70 msec task-clock # 0.990 CPUs utilized
26 context-switches # 0.147 K/sec
1 cpu-migrations # 0.006 K/sec
396 page-faults # 0.002 M/sec
397,184,949 cycles # 2.248 GHz (81.90%)
566,738 stalled-cycles-frontend # 0.14% frontend cycles idle (83.25%)
269,961,726 stalled-cycles-backend # 67.97% backend cycles idle (84.20%)
1,103,594,689 instructions # 2.78 insn per cycle
# 0.24 stalled cycles per insn (84.17%)
27,099,584 branches # 153.369 M/sec (84.18%)
101,006 branch-misses # 0.37% of all branches (82.29%)
0.178496560 seconds time elapsed
0.177966000 seconds user
0.000000000 seconds sys
三个乘法算法的比较
我是AMD Ryzen 5 3500U的CPU。貌似它不支持cache-miss
最终三个矩阵乘法的算法的部分性能如下。传统算法是300 * 300的矩阵,剩下两种都是512 * 512的矩阵,因此可能在总体数据上不能和传统乘法作比较,但是在平均值上还是可以比较的。
测试指标 | 传统乘法 | 分治算法 | strassen算法 |
---|---|---|---|
task-clock | 176.70 msec | 1,718.96 msec | 12,237.24 msec |
context-switches | 0.147 K/sec | 0.172 K/sec | 0.144 K/sec |
cpu-migrations | 0.006 K/sec | 0.002 K/sec | 0.002 K/sec |
page-faults | 0.002 M/sec | 0.476 K/sec | 0.147 M/sec |
cycles | 2.248 GHz | 2.377 GHz | 2.377 GHz |
instructions | 2.78 insn per cycle | 3.27 insn per cycle | 2.78 insn per cycle |
branches | 153.369 M/sec | 514.335 M/sec | 1020.418 M/sec |
branch-misses | 0.37% of all branches | 0.01% of all branches | 0.15% of all branches |
time elapsed | 0.178496560 | 1.723157641 | 12.481839252 |
附录代码
传统矩阵乘法
#include <stdio.h>
#define _M 300
#define _N 300
int A[_M][_N];
int B[_M][_N];
int C[_M][_N];
int main()
{
// 开始做传统矩阵乘法
int i, j, k;
for(i = 0; i < _M; ++i)
for(j = 0; j < _N; ++j)
for(k = 0; k < _N; ++k)
C[i][j] += A[i][k] * B[k][j];
return 0;
}
分治算法
#include <stdio.h>
#define _N 511
#define SIZE (_N + 1)
int A[SIZE][SIZE];
int B[SIZE][SIZE];
int **C;
// 记录矩阵中一个元素的位置
typedef struct
{
int lin, col;
}posi;
void product_div_con(posi a_beg, posi a_end, posi b_beg, posi b_end);
int main()
{
posi a_beg, a_end, b_beg, b_end;
a_beg.lin = a_beg.col = b_beg.lin = b_beg.col = 0;
a_end.lin = a_end.col = b_end.lin = b_end.col = _N;
int i,j;
C = (int **)calloc(_N + 1, sizeof(int *));
for(i = 0; i < _N + 1; ++i)
C[i] = (int *)calloc(_N + 1, sizeof(int));
for (i = 0; i < _N + 1; ++i)
{
A[i][i] = 1;
for(j = 0; j < SIZE; ++j)
B[i][j] = 10;
}
product_div_con(a_beg, a_end, b_beg, b_end);
// print_matrix(C, _N + 1);
return 0;
}
void product_div_con(posi a_beg, posi a_end, posi b_beg, posi b_end)
{
// cout<<a_beg.lin<<'_'<<a_beg.col<<'_'<<a_end.lin<<'_'<<a_end.col<<'_'<<b_beg.lin<<'_'<<b_beg.col<<'_'<<b_end.lin<<'_'<<b_end.col<<'_'<<endl;
// 如果递归在最后只有一个元素,那么计算乘积,返回
if (a_beg.lin == a_end.lin && a_beg.col == a_end.col)
{
// cerr<<"ajsdif\n";
C[a_beg.lin][b_beg.col] += A[a_beg.lin][a_beg.col] * B[b_beg.lin][b_beg.col];
// cerr<<C[a_beg.lin][b_beg.col]<<endl;
return;
}
// 如果还能继续分下去
// 将矩阵分为4部分
posi a00_beg, a00_end, a01_beg, a01_end, a10_beg, a10_end, a11_beg, a11_end;
posi b00_beg, b00_end, b01_beg, b01_end, b10_beg, b10_end, b11_beg, b11_end;
// A矩阵的第一块
a00_beg.lin = a_beg.lin;
a00_beg.col = a_beg.col;
a00_end.lin = (a_beg.lin + a_end.lin) / 2;
a00_end.col = (a_beg.col + a_end.col) / 2;
// A矩阵的第二块
a01_beg.lin = a_beg.lin;
a01_beg.col = (a_beg.col + a_end.col) / 2 + 1;
a01_end.lin = (a_beg.lin + a_end.lin) / 2;
a01_end.col = a_end.col;
// A矩阵的左下块
a10_beg.lin = (a_beg.lin + a_end.lin) / 2 + 1;
a10_beg.col = a_beg.col;
a10_end.lin = a_end.lin;
a10_end.col = (a_beg.col + a_end.col) / 2;
// A矩阵的右下块
a11_beg.lin = (a_beg.lin + a_end.lin) / 2 + 1;
a11_beg.col = (a_beg.col + a_end.col) / 2 + 1;
a11_end.lin = a_end.lin;
a11_end.col = a_end.col;
// B矩阵的第一块
b00_beg.lin = b_beg.lin;
b00_beg.col = b_beg.col;
b00_end.lin = (b_beg.lin + b_end.col) / 2;
b00_end.col = (b_beg.col + b_end.col) / 2;
// B矩阵的右上块
b01_beg.lin = b_beg.lin;
b01_beg.col = (b_beg.col + b_end.col) / 2 + 1;
b01_end.lin = (b_beg.lin + b_end.lin) / 2;
b01_end.col = b_end.col;
// B矩阵的左下块
b10_beg.lin = (b_beg.lin + b_end.lin) / 2 + 1;
b10_beg.col = b_beg.col;
b10_end.lin = b_end.lin;
b10_end.col = (b_beg.col + b_end.col) / 2;
// B矩阵的右下块
b11_beg.lin = (b_beg.lin + b_end.lin) / 2 + 1;
b11_beg.col = (b_beg.col + b_end.col) / 2 + 1;
b11_end.lin = b_end.lin;
b11_end.col = b_end.col;
//下面开始八次递归调用
// A00 * B00
product_div_con(a00_beg, a00_end, b00_beg, b00_end);
// A01 * B10
product_div_con(a01_beg, a01_end, b10_beg, b10_end);
// A00 * B01
product_div_con(a00_beg, a00_end, b01_beg, b01_end);
// A01 * B111
product_div_con(a01_beg, a01_end, b11_beg, b11_end);
// A10 * B00
product_div_con(a10_beg, a10_end, b00_beg, b00_end);
// A11 * B10
product_div_con(a11_beg, a11_end, b10_beg, b10_end);
// A10 * B01
product_div_con(a10_beg, a10_end, b01_beg, b01_end);
// A11 * B11
product_div_con(a11_beg, a11_end, b11_beg, b11_end);
}
Strassen算法究极长,我放在网站上了