perf测试矩阵乘法

介绍

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
Screenshot from 2020-04-03 22-21-26.png

最终三个矩阵乘法的算法的部分性能如下。传统算法是300 * 300的矩阵,剩下两种都是512 * 512的矩阵,因此可能在总体数据上不能和传统乘法作比较,但是在平均值上还是可以比较的。

测试指标传统乘法分治算法strassen算法
task-clock176.70 msec1,718.96 msec12,237.24 msec
context-switches0.147 K/sec0.172 K/sec0.144 K/sec
cpu-migrations0.006 K/sec0.002 K/sec0.002 K/sec
page-faults0.002 M/sec0.476 K/sec0.147 M/sec
cycles2.248 GHz2.377 GHz2.377 GHz
instructions2.78 insn per cycle3.27 insn per cycle2.78 insn per cycle
branches153.369 M/sec514.335 M/sec1020.418 M/sec
branch-misses0.37% of all branches0.01% of all branches0.15% of all branches
time elapsed0.1784965601.72315764112.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算法究极长,我放在网站上了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值