性能优化工具学习

TOOLS

valgrind & kcachegrind 分析


// 引入头文件
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <iostream>
using namespace std;

// 定义常量
#define MAXRANGE 1000

// 定义宏定义函数
#define min(A, B) ((A > B) ? B : A)

//************************************************
// 生成N个0到max-1之间的随机数,存储在store数组中
void numberGen(int N, int max, int* store) {
   int i;
   srand(time(0));
   for (i = 0; i < N; i++)
      store[i] = rand() % max;
}

//************************************************
// 比较函数,用于qsort函数排序
int comp(const void* a, const void* b) {
   int x = *((int*)a);
   int y = *((int*)b);
   return x - y;
}

//************************************************
// 桶排序函数
void BucketSort(int* data, int N, int numBuckets = 2) {
   // 分配numBuckets个桶,并记录每个桶内元素的数量
   int* bucket[numBuckets];
   int len[numBuckets];
   int bucketRange = MAXRANGE / numBuckets + 1;

   for (int i = 0; i < numBuckets; i++) {
      bucket[i] = new int[N];
      len[i] = 0;
   }

   // 将数据按照数量分入桶中
   for (int i = 0; i < N; i++) {
      int buckNum = data[i] / bucketRange;
      bucket[buckNum][len[buckNum]++] = data[i];
   }

   // 对每个桶内的元素进行快速排序
   for (int i = 0; i < numBuckets; i++)
      qsort(bucket[i], len[i], sizeof(int), comp);

   // 将已排序的元素放回到原始数据中
   int k = 0;
   for (int i = 0; i < numBuckets; i++)
      for (int j = 0; j < len[i]; j++)
         data[k++] = bucket[i][j];

   // 释放桶内存
   for (int i = 0; i < numBuckets; i++)
      delete[] bucket[i];
}

//--------------------------------------------------------
int main(int argc, char* argv[]) {
   // 命令行参数检查
   if (argc == 1) {
      fprintf(stderr, "%s N\n", argv[0]);
      exit(0);
   }

   // 将命令行参数读入N中,并分配空间生成N个随机数
   int N = atoi(argv[1]);
   int* data = (int*)malloc(N * sizeof(int));
   numberGen(N, 1000, data);

   // 使用桶排序方法对数据进行排序
   BucketSort(data, N);

   // 输出排序后的结果
   for (int i = 0; i < N; i++)
      cout << data[i] << " ";
   cout << endl;

   // 释放所分配的内存空间
   free(data);
   return 0;
}

上面是要通过valgrind 分析的代码

  1. 上面代码生成bucketsort 可执行文件
  2. valgrind --tool=callgrind ./bucketsort 1000000 调用命令生成分析的文件,这可以通过像kcachegrind这样的前端程序来可视化.
  3. kcachegrind callgrind.out.9876 可视化
    问题:(kcachegrind 在WSL2 中执行会出现Session bus not found)
    解决: 在执行kcachegrind之前执行export $(dbus-launch) 这个命令
    在这里插入图片描述

perftools

下面是随机创建生成L大小的一串字符串s,然后再创建N个指向S中的随机位置的指针,分别把他们保存到vs 中,然后对这个vs中字串进行排序。


// 子串排序
#include <algorithm>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <memory>
#include <random>
#include <vector>

using std::cout;
using std::endl;
using std::minstd_rand;
using std::unique_ptr;
using std::vector;
using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::chrono::system_clock;

// 字串排序的比较函数
bool compare(const char* s1, const char* s2, unsigned int l) {
   // 如果指向的是相同的位置,则认为它们相等,返回false
   if (s1 == s2)
      return false;
   for (unsigned int i1 = 0, i2 = 0; i1 < l; ++i1, ++i2) {
      // 比较两个字符串中从开头开始长度为l的子串
      // 如果两个字符不相等,则根据字符的ASCII码值决定它们的大小
      if (s1[i1] != s2[i2])
         return s1[i1] > s2[i2];
   }
   // 如果两个字符串的前l个字符都相同,则认为它们相等,返回false
   return false;
}

int main() {
   // constexpr unsigned int L = 1 << 24, N = 1 << 20;  // Use with options A, B
   constexpr unsigned int L = 1 << 18, N = 1 << 14;  // Use with option C
   // constexpr unsigned int L = 1 << 22, N = 1 << 18;  // Use with option C, takes longer
   // constexpr unsigned int L = 1 << 8, N = 1 << 3;

   // system_clock::time_point t0 = system_clock::now();
   //  动态分配L个字符的内存
   unique_ptr<char[]> s(new char[L]);
   // 创建长度为N的指向const char的指针向量
   vector<const char*> vs(N);
   {
      // 创建一个随机数生成器
      minstd_rand rgen;
      using rand_t = minstd_rand::result_type;
      // Option A:将随机数填充到s中
      if (0) {
         for (char *p = s.get(), *end = p + L; p != end; p += sizeof(rand_t)) {
            const rand_t x = rgen();
            ::memcpy(p, &x, sizeof(x));
         }
      }
      // Option B:用随机小写字母填充s
      else if (0) {
         for (unsigned int i = 0; i < L; ++i) {
            // 用随机数生成在a到z之间的ASCII码值,并将其转换为小写字母
            s[i] = 'a' + (rgen() % ('z' - 'a' + 1));
         }
      }
      // Option C:用大量小写字母填充s,并在其中随机替换一些字符为其他小写字母
      else {
         // 将s的所有字符都设置为'a'
         ::memset(s.get(), 'a', L * sizeof(char));
         // 在s中随机替换L/1024个字符为随机小写字母
         for (unsigned int i = 0; i < L / 1024; ++i) {
            s[rgen() % (L - 1)] = 'a' + (rgen() % ('z' - 'a' + 1));
         }
      }
      // 将s的最后一个字符设置为0
      s[L - 1] = 0;
      // 将vs中的每个元素都指向s中的随机位置
      for (unsigned int i = 0; i < N; ++i) {
         vs[i] = &s[rgen() % (L - 1)];
      }
      // cout << "s=" << s.get() << endl;
      // for (unsigned int i = 0; i < N; ++i)
      //    cout << "vs[" << i << "]=" << vs[i] << endl;
   }
   // 记录开始排序的时间点
   system_clock::time_point t1 = system_clock::now();
   // cout << "Prep time(L=" << L << ", N=" << N << "): "
   //      << duration_cast<milliseconds>(t1 - t0).count()
   //      << "ms" << endl;

   size_t count = 0;
   // 对vs中的指针按字符串的大小排序
   std::sort(vs.begin(), vs.end(), [&](const char* a, const char* b) {
       ++count; return compare(a, b, L); });
   // for (unsigned int i = 0; i < N; ++i)
   //    cout << "vs[" << i << "]=" << vs[i] << endl;
   // 记录排序结束的时间点
   system_clock::time_point t2 = system_clock::now();
   // 输出排序所用时间以及总共进行了多少次比较
   cout << "Sort time: "
        << duration_cast<milliseconds>(t2 - t1).count()
        << "ms (" << count << " comparisons)"
        << endl;
}

  1. 工具安装

sudo apt-fast install clang-11
sudo apt-fast install google-perftools
sudo apt-fast install libgoogle-perftools-dev

执行下面得到命令运行代码


clang++-11 -g -O3 -mavx2 -Wall -pedantic day1.cpp -lprofiler -o test

在这里插入图片描述

花费的时间36ms

现在用GperfTools对代码进行分析。
2. 生成prof.data 执行下面命令


clang++-11 -g -O3 -mavx2 -Wall -pedantic day1.cpp -lprofiler -o test
CPUPROFILE=prof.data ./test

在这里插入图片描述

  1. google-pprof 读取保存的prof.data

google-pprof --text ./test prof.data

在这里插入图片描述

分析器显示,大多时间都花在比较函数compare()上,而排序几乎不花时间

  1. google-pprof交互式
    text text --lines
    在这里插入图片描述

  2. 生成调用图


google-pprof --pdf ./test prof.data > prof.pdf

perf分析器

  1. 安装 我的是WSL2 需要手动编译
    手动编译perf

sudo apt install build-essential flex bison dwarves libssl-dev libelf-dev python3-dev bc 
sudo apt install linux-source

cd /usr/src/
cd linux-source-5.15.0
sudo tar -xvf linux-source-5.15.0.tar.bz2
cd linux-source-5.15.0
cd tools/perf

sudo make -j8
sudo cp perf /usr/local/bin

在这里插入图片描述

打开 .bashrc 在末尾添加后source .bashrc


export=$PATH:/usr/local/bin

  1. 编译程序并执行

clang++-11 -O3 -mavx2 -Wall -pedantic day1.cpp -o test
perf stat ./test

在这里插入图片描述

perf list

进入root 权限会看到很多event
列出perf event


root@xz:/home/xiaqiu/study/cpp/test# perf list

List of pre-defined events (to be used in -e):

  alignment-faults                                   [Software event]
  bpf-output                                         [Software event]
  cgroup-switches                                    [Software event]
  context-switches OR cs                             [Software event]
  cpu-clock                                          [Software event]
  cpu-migrations OR migrations                       [Software event]
  dummy                                              [Software event]
  emulation-faults                                   [Software event]
  major-faults                                       [Software event]
  minor-faults                                       [Software event]
  page-faults OR faults                              [Software event]
  task-clock                                         [Software event]

  duration_time                                      [Tool event]

  msr/tsc/                                           [Kernel PMU event]

  rNNN                                               [Raw hardware event descriptor]
  cpu/t1=v1[,t2=v2,t3 ...]/modifier                  [Raw hardware event descriptor]
   (see 'man perf-list' on how to encode it)

  mem:<addr>[/len][:access]                          [Hardware breakpoint]

  9p:9p_client_req                                   [Tracepoint event]
  9p:9p_client_res                                   [Tracepoint event]
  9p:9p_protocol_dump                                [Tracepoint event]
  alarmtimer:alarmtimer_cancel                       [Tracepoint event]
  alarmtimer:alarmtimer_fired                        [Tracepoint event]
  alarmtimer:alarmtimer_start                        [Tracepoint event]
  alarmtimer:alarmtimer_suspend                      [Tracepoint event]
:
...

perf record -a -g

收集数据


xiaqiu@xz:~/study/cpp/test$ clang++-11 -g -O3 -mavx2 -Wall -pedantic day1.cpp -o test
root@xz:/home/xiaqiu/study/cpp/test$ sudo perf record -a -g ./test
Sort time: 155ms (292894 comparisons)
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 1.433 MB perf.data (12448 samples) ]
root@xz:/home/xiaqiu/study/cpp/test#
xiaqiu@xz:~/study/cpp/test$ ls
day1.cpp  perf.data  test
xiaqiu@xz:~/study/cpp/test$

perf report


xiaqiu@xz:~/perf$ sudo perf report

屏幕输出包括常规输出和分析器的信息
在这里插入图片描述

perf annotate


xiaqiu@xz:~/perf$ sudo perf annotate

显示的内容如下
在这里插入图片描述

代码分析

输入perf report命令后出现下面的界面
在这里插入图片描述

按a键进入到compare内部
在这里插入图片描述

可以按/进行搜索
在这里插入图片描述

在这里插入图片描述

benchmark

  1. 安装工具

sudo apt-fast install libbenchmark-dev

  1. 代码

#include <stdlib.h>
#include <string.h>
#include <benchmark/benchmark.h>

// 定义benchmark测试函数BM_add,参数为benchmark::State类型的state
void BM_add(benchmark::State& state) {
    // 使用rand()函数生成两个大小为N的随机数组v1和v2
    srand(1);
    const unsigned int N = state.range(0);
    std::vector<unsigned long> v1(N), v2(N);
    for (size_t i = 0; i < N; ++i) {
        v1[i] = rand();
        v2[i] = rand();
    }
    // 分别定义指针p1和p2指向数组v1和v2的首元素
    unsigned long* p1 = v1.data();
    unsigned long* p2 = v2.data();
    // benchmark循环执行的代码,使用DoNotOptimize和ClobberMemory宏
    for (auto _ : state) {
        unsigned long a1 = 0, a2 = 0;
        for (size_t i = 0; i < N; ++i) {
            // 将数组v1和v2的对应元素相加,存入a1
            a1 += p1[i] + p2[i];
        }
        // 使用DoNotOptimize宏防止编译器优化掉benchmark测试
        benchmark::DoNotOptimize(a1);
        benchmark::DoNotOptimize(a2);
        // 使用ClobberMemory宏强制刷新寄存器
        benchmark::ClobberMemory();
    }
    // 设置benchmark测试的参数
    state.SetItemsProcessed(N*state.iterations());
    //state.SetBytesProcessed(N*sizeof(unsigned long)*state.iterations());
}

// 定义一个宏ARGS,设置benchmark测试的参数
#define ARGS ->Arg(1<<22)

// 注册BM_add函数为benchmark测试函数,并使用ARGS宏设置测试参数
BENCHMARK(BM_add) ARGS;
// 使用BENCHMARK_MAIN()宏定义main函数
BENCHMARK_MAIN();

  1. 编译

clang++-11 -g -O0  -mavx2 -Wall -pedantic -o test day1.cpp -lbenchmark
sudo perf record -g -a ./test

在这里插入图片描述

  1. 结果
    在这里插入图片描述

Benchmark列:列出了我们所测量的基准的名称,如“BM_Sort”。
Time列:列出了每次执行基准所花费的时间(平均时间)。它以秒为单位呈现,这里使用了微秒(us)的分辨率,即以百万分之一秒为单位。
CPU列:列出了经过的 CPU 时间。与时间列不同,CPU 时间是在 CPU 上运行的时间,因此它更为准确。
Iterations列:列出了benchmark的运行次数。benchmark会根据不同的参数重复执行benchmark,以减小测量误差。每次benchmark运行所花费的时间是Time和CPU除以迭代次数。

  1. 分析代码

sudo perf report

按a键进入到BM_add
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

现在开启优化看看
在这里插入图片描述

在这里插入图片描述

机器码分析器(MCA)

在之前代码添加宏


#define MCA_START __asm volatile("# LLVM-MCA-BEGIN");
#define MCA_END __asm volatile("# LLVM-MCA-END");

在加法的前后添加上面的宏地方


for (size_t i = 0; i < N; ++i) {
   // 将数组v1和v2的对应元素相加,存入a1
   MCA_START
   a1 += p1[i] + p2[i];
   MCA_END
}

而是用Intel语法生成汇编输出(-S),输出到分析器。


 clang++-11 day2.cpp -g --std=c++17 -mllvm -x86-asm-syntax=intel -S -o - | llvm-mca-11 -mcpu=btver2 -timeline

下面输出的结果


xiaqiu@xz:~/perf$ clang++-11 day2.cpp -g --std=c++17 -mllvm -x86-asm-syntax=intel -S -o - | llvm-mca-11 -mcpu=btver2 -timeline

[0] Code Region

Iterations:        100
Instructions:      800
Total Cycles:      708
Total uOps:        800

Dispatch Width:    2
uOps Per Cycle:    1.13
IPC:               1.13
Block RThroughput: 7.0


Instruction Info:
[1]    [2]    [3]    [4]    [5]    [6]    Instructions:
 1      3     1.00    *                   mov   rax, qword ptr [rbp - 392]
 1      3     1.00    *                   mov   rcx, qword ptr [rbp - 480]
 1      3     1.00    *                   mov   rax, qword ptr [rax + 8*rcx]
 1      3     1.00    *                   mov   rcx, qword ptr [rbp - 400]
 1      3     1.00    *                   mov   rdx, qword ptr [rbp - 480]
 1      4     1.00    *                   add   rax, qword ptr [rcx + 8*rdx]
 1      4     1.00    *                   add   rax, qword ptr [rbp - 464]
 1      1     1.00           *            mov   qword ptr [rbp - 464], rax


Resources:
[0]   - JALU0
[1]   - JALU1
[2]   - JDiv
[3]   - JFPA
[4]   - JFPM
[5]   - JFPU0
[6]   - JFPU1
[7]   - JLAGU
[8]   - JMul
[9]   - JSAGU
[10]  - JSTC
[11]  - JVALU0
[12]  - JVALU1
[13]  - JVIMUL


Resource pressure per iteration:
[0]    [1]    [2]    [3]    [4]    [5]    [6]    [7]    [8]    [9]    [10]   [11]   [12]   [13]
1.00   1.00    -      -      -      -      -     7.00    -     1.00    -      -      -      -

Resource pressure by instruction:
[0]    [1]    [2]    [3]    [4]    [5]    [6]    [7]    [8]    [9]    [10]   [11]   [12]   [13]   Instructions:
 -      -      -      -      -      -      -     1.00    -      -      -      -      -      -     mov   rax, qword ptr [rbp - 392]
 -      -      -      -      -      -      -     1.00    -      -      -      -      -      -     mov   rcx, qword ptr [rbp - 480]
 -      -      -      -      -      -      -     1.00    -      -      -      -      -      -     mov   rax, qword ptr [rax + 8*rcx]
 -      -      -      -      -      -      -     1.00    -      -      -      -      -      -     mov   rcx, qword ptr [rbp - 400]
 -      -      -      -      -      -      -     1.00    -      -      -      -      -      -     mov   rdx, qword ptr [rbp - 480]
 -     1.00    -      -      -      -      -     1.00    -      -      -      -      -      -     add   rax, qword ptr [rcx + 8*rdx]
1.00    -      -      -      -      -      -     1.00    -      -      -      -      -      -     add   rax, qword ptr [rbp - 464]
 -      -      -      -      -      -      -      -      -     1.00    -      -      -      -     mov   qword ptr [rbp - 464], rax


Timeline view:
                    0123456789          0123456789          0123456789          01234567
Index     0123456789          0123456789          0123456789          0123456789

[0,0]     DeeeER    .    .    .    .    .    .    .    .    .    .    .    .    .    . .   mov  rax, qword ptr [rbp - 392]
[0,1]     D=eeeER   .    .    .    .    .    .    .    .    .    .    .    .    .    . .   mov  rcx, qword ptr [rbp - 480]
[0,2]     .D===eeeER.    .    .    .    .    .    .    .    .    .    .    .    .    . .   mov  rax, qword ptr [rax + 8*rcx]
[0,3]     .D=eeeE--R.    .    .    .    .    .    .    .    .    .    .    .    .    . .   mov  rcx, qword ptr [rbp - 400]
[0,4]     . D=eeeE--R    .    .    .    .    .    .    .    .    .    .    .    .    . .   mov  rdx, qword ptr [rbp - 480]
[0,5]     . D====eeeeER  .    .    .    .    .    .    .    .    .    .    .    .    . .   add  rax, qword ptr [rcx + 8*rdx]
[0,6]     .  D====eeeeER .    .    .    .    .    .    .    .    .    .    .    .    . .   add  rax, qword ptr [rbp - 464]
[0,7]     .  D========eER.    .    .    .    .    .    .    .    .    .    .    .    . .   mov  qword ptr [rbp - 464], rax
[1,0]     .   D=eeeE----R.    .    .    .    .    .    .    .    .    .    .    .    . .   mov  rax, qword ptr [rbp - 392]
[1,1]     .   D====eeeE--R    .    .    .    .    .    .    .    .    .    .    .    . .   mov  rcx, qword ptr [rbp - 480]
[1,2]     .    D======eeeER   .    .    .    .    .    .    .    .    .    .    .    . .   mov  rax, qword ptr [rax + 8*rcx]
[1,3]     .    D====eeeE--R   .    .    .    .    .    .    .    .    .    .    .    . .   mov  rcx, qword ptr [rbp - 400]
[1,4]     .    .D====eeeE--R  .    .    .    .    .    .    .    .    .    .    .    . .   mov  rdx, qword ptr [rbp - 480]
[1,5]     .    .D=======eeeeER.    .    .    .    .    .    .    .    .    .    .    . .   add  rax, qword ptr [rcx + 8*rdx]
[1,6]     .    . D=======eeeeER    .    .    .    .    .    .    .    .    .    .    . .   add  rax, qword ptr [rbp - 464]
[1,7]     .    . D===========eER   .    .    .    .    .    .    .    .    .    .    . .   mov  qword ptr [rbp - 464], rax
[2,0]     .    .  D====eeeE----R   .    .    .    .    .    .    .    .    .    .    . .   mov  rax, qword ptr [rbp - 392]
[2,1]     .    .  D=======eeeE--R  .    .    .    .    .    .    .    .    .    .    . .   mov  rcx, qword ptr [rbp - 480]
[2,2]     .    .   D=========eeeER .    .    .    .    .    .    .    .    .    .    . .   mov  rax, qword ptr [rax + 8*rcx]
[2,3]     .    .   D=======eeeE--R .    .    .    .    .    .    .    .    .    .    . .   mov  rcx, qword ptr [rbp - 400]
[2,4]     .    .    D=======eeeE--R.    .    .    .    .    .    .    .    .    .    . .   mov  rdx, qword ptr [rbp - 480]
[2,5]     .    .    D==========eeeeER   .    .    .    .    .    .    .    .    .    . .   add  rax, qword ptr [rcx + 8*rdx]
[2,6]     .    .    .D==========eeeeER  .    .    .    .    .    .    .    .    .    . .   add  rax, qword ptr [rbp - 464]
[2,7]     .    .    . D=============eER .    .    .    .    .    .    .    .    .    . .   mov  qword ptr [rbp - 464], rax
[3,0]     .    .    . D=======eeeE----R .    .    .    .    .    .    .    .    .    . .   mov  rax, qword ptr [rbp - 392]
[3,1]     .    .    .  D=========eeeE--R.    .    .    .    .    .    .    .    .    . .   mov  rcx, qword ptr [rbp - 480]
[3,2]     .    .    .   D===========eeeER    .    .    .    .    .    .    .    .    . .   mov  rax, qword ptr [rax + 8*rcx]
[3,3]     .    .    .    D========eeeE--R    .    .    .    .    .    .    .    .    . .   mov  rcx, qword ptr [rbp - 400]
[3,4]     .    .    .    .D========eeeE--R   .    .    .    .    .    .    .    .    . .   mov  rdx, qword ptr [rbp - 480]
[3,5]     .    .    .    . D==========eeeeER .    .    .    .    .    .    .    .    . .   add  rax, qword ptr [rcx + 8*rdx]
[3,6]     .    .    .    .  D==========eeeeER.    .    .    .    .    .    .    .    . .   add  rax, qword ptr [rbp - 464]
[3,7]     .    .    .    .   D=============eER    .    .    .    .    .    .    .    . .   mov  qword ptr [rbp - 464], rax
[4,0]     .    .    .    .   D=======eeeE----R    .    .    .    .    .    .    .    . .   mov  rax, qword ptr [rbp - 392]
[4,1]     .    .    .    .    D=========eeeE--R   .    .    .    .    .    .    .    . .   mov  rcx, qword ptr [rbp - 480]
[4,2]     .    .    .    .    .D===========eeeER  .    .    .    .    .    .    .    . .   mov  rax, qword ptr [rax + 8*rcx]
[4,3]     .    .    .    .    . D========eeeE--R  .    .    .    .    .    .    .    . .   mov  rcx, qword ptr [rbp - 400]
[4,4]     .    .    .    .    .  D========eeeE--R .    .    .    .    .    .    .    . .   mov  rdx, qword ptr [rbp - 480]
[4,5]     .    .    .    .    .   D==========eeeeER    .    .    .    .    .    .    . .   add  rax, qword ptr [rcx + 8*rdx]
[4,6]     .    .    .    .    .    D==========eeeeER   .    .    .    .    .    .    . .   add  rax, qword ptr [rbp - 464]
[4,7]     .    .    .    .    .    .D=============eER  .    .    .    .    .    .    . .   mov  qword ptr [rbp - 464], rax
[5,0]     .    .    .    .    .    .D=======eeeE----R  .    .    .    .    .    .    . .   mov  rax, qword ptr [rbp - 392]
[5,1]     .    .    .    .    .    . D=========eeeE--R .    .    .    .    .    .    . .   mov  rcx, qword ptr [rbp - 480]
[5,2]     .    .    .    .    .    .  D===========eeeER.    .    .    .    .    .    . .   mov  rax, qword ptr [rax + 8*rcx]
[5,3]     .    .    .    .    .    .   D========eeeE--R.    .    .    .    .    .    . .   mov  rcx, qword ptr [rbp - 400]
[5,4]     .    .    .    .    .    .    D========eeeE--R    .    .    .    .    .    . .   mov  rdx, qword ptr [rbp - 480]
[5,5]     .    .    .    .    .    .    .D==========eeeeER  .    .    .    .    .    . .   add  rax, qword ptr [rcx + 8*rdx]
[5,6]     .    .    .    .    .    .    . D==========eeeeER .    .    .    .    .    . .   add  rax, qword ptr [rbp - 464]
[5,7]     .    .    .    .    .    .    .  D=============eER.    .    .    .    .    . .   mov  qword ptr [rbp - 464], rax
[6,0]     .    .    .    .    .    .    .  D=======eeeE----R.    .    .    .    .    . .   mov  rax, qword ptr [rbp - 392]
[6,1]     .    .    .    .    .    .    .   D=========eeeE--R    .    .    .    .    . .   mov  rcx, qword ptr [rbp - 480]
[6,2]     .    .    .    .    .    .    .    D===========eeeER   .    .    .    .    . .   mov  rax, qword ptr [rax + 8*rcx]
[6,3]     .    .    .    .    .    .    .    .D========eeeE--R   .    .    .    .    . .   mov  rcx, qword ptr [rbp - 400]
[6,4]     .    .    .    .    .    .    .    . D========eeeE--R  .    .    .    .    . .   mov  rdx, qword ptr [rbp - 480]
[6,5]     .    .    .    .    .    .    .    .  D==========eeeeER.    .    .    .    . .   add  rax, qword ptr [rcx + 8*rdx]
[6,6]     .    .    .    .    .    .    .    .   D==========eeeeER    .    .    .    . .   add  rax, qword ptr [rbp - 464]
[6,7]     .    .    .    .    .    .    .    .    D=============eER   .    .    .    . .   mov  qword ptr [rbp - 464], rax
[7,0]     .    .    .    .    .    .    .    .    D=======eeeE----R   .    .    .    . .   mov  rax, qword ptr [rbp - 392]
[7,1]     .    .    .    .    .    .    .    .    .D=========eeeE--R  .    .    .    . .   mov  rcx, qword ptr [rbp - 480]
[7,2]     .    .    .    .    .    .    .    .    . D===========eeeER .    .    .    . .   mov  rax, qword ptr [rax + 8*rcx]
[7,3]     .    .    .    .    .    .    .    .    .  D========eeeE--R .    .    .    . .   mov  rcx, qword ptr [rbp - 400]
[7,4]     .    .    .    .    .    .    .    .    .   D========eeeE--R.    .    .    . .   mov  rdx, qword ptr [rbp - 480]
[7,5]     .    .    .    .    .    .    .    .    .    D==========eeeeER   .    .    . .   add  rax, qword ptr [rcx + 8*rdx]
[7,6]     .    .    .    .    .    .    .    .    .    .D==========eeeeER  .    .    . .   add  rax, qword ptr [rbp - 464]
[7,7]     .    .    .    .    .    .    .    .    .    . D=============eER .    .    . .   mov  qword ptr [rbp - 464], rax
[8,0]     .    .    .    .    .    .    .    .    .    . D=======eeeE----R .    .    . .   mov  rax, qword ptr [rbp - 392]
[8,1]     .    .    .    .    .    .    .    .    .    .  D=========eeeE--R.    .    . .   mov  rcx, qword ptr [rbp - 480]
[8,2]     .    .    .    .    .    .    .    .    .    .   D===========eeeER    .    . .   mov  rax, qword ptr [rax + 8*rcx]
[8,3]     .    .    .    .    .    .    .    .    .    .    D========eeeE--R    .    . .   mov  rcx, qword ptr [rbp - 400]
[8,4]     .    .    .    .    .    .    .    .    .    .    .D========eeeE--R   .    . .   mov  rdx, qword ptr [rbp - 480]
[8,5]     .    .    .    .    .    .    .    .    .    .    . D==========eeeeER .    . .   add  rax, qword ptr [rcx + 8*rdx]
[8,6]     .    .    .    .    .    .    .    .    .    .    .  D==========eeeeER.    . .   add  rax, qword ptr [rbp - 464]
[8,7]     .    .    .    .    .    .    .    .    .    .    .   D=============eER    . .   mov  qword ptr [rbp - 464], rax
[9,0]     .    .    .    .    .    .    .    .    .    .    .   D=======eeeE----R    . .   mov  rax, qword ptr [rbp - 392]
[9,1]     .    .    .    .    .    .    .    .    .    .    .    D=========eeeE--R   . .   mov  rcx, qword ptr [rbp - 480]
[9,2]     .    .    .    .    .    .    .    .    .    .    .    .D===========eeeER  . .   mov  rax, qword ptr [rax + 8*rcx]
[9,3]     .    .    .    .    .    .    .    .    .    .    .    . D========eeeE--R  . .   mov  rcx, qword ptr [rbp - 400]
[9,4]     .    .    .    .    .    .    .    .    .    .    .    .  D========eeeE--R . .   mov  rdx, qword ptr [rbp - 480]
[9,5]     .    .    .    .    .    .    .    .    .    .    .    .   D==========eeeeER .   add  rax, qword ptr [rcx + 8*rdx]
[9,6]     .    .    .    .    .    .    .    .    .    .    .    .    D==========eeeeER.   add  rax, qword ptr [rbp - 464]
[9,7]     .    .    .    .    .    .    .    .    .    .    .    .    .D=============eER   mov  qword ptr [rbp - 464], rax


Average Wait times (based on the timeline view):
[1]: Average time spent waiting in a scheduler's queue
[2]: Average time spent waiting in a scheduler's queue while ready
[3]: Average time elapsed from WB until retire stage

      [0]    [1]    [2]    [3]
0.     10    6.4    6.4    3.6       mov        rax, qword ptr [rbp - 392]
1.     10    8.5    8.5    1.8       mov        rcx, qword ptr [rbp - 480]
2.     10    10.5   0.0    0.0       mov        rax, qword ptr [rax + 8*rcx]
3.     10    7.8    7.8    2.0       mov        rcx, qword ptr [rbp - 400]
4.     10    7.8    7.8    2.0       mov        rdx, qword ptr [rbp - 480]
5.     10    10.1   0.0    0.0       add        rax, qword ptr [rcx + 8*rdx]
6.     10    10.1   0.0    0.0       add        rax, qword ptr [rbp - 464]
7.     10    13.3   0.0    0.0       mov        qword ptr [rbp - 464], rax
       10    9.3    3.8    1.2       <total>
xiaqiu@xz:~/perf$

Timeline view::该部分显示时间线的整体视图,每行表示一个 CPU 周期。每个周期显示该周期内处理器执行的指令,以及该指令所占用的处理器端口、延迟和执行结果等信息。
Iterations::该部分显示执行机器码的迭代次数,以及每次迭代的统计信息,例如各种指令的执行次数、处理器的繁忙时间等等。
Resource pressure::该部分显示处理器资源的使用情况,例如各个端口的繁忙时间、指令的等待时间等等

在这里插入图片描述

现在修改代码添加一条乘法的代码。


```cpp

for (size_t i = 0; i < N; ++i) {
   // 将数组v1和v2的对应元素相加,存入a1
   MCA_START
   a1 += p1[i] + p2[i];
   a2 += p1[i] * p2[i];
   MCA_END
}

在这里插入图片描述

看看O3优化后的效果

只要操作数在寄存器中,处理器就可以同时执行多个操作。

数据依赖和流水线


for (size_t i = 0; i < N; ++i) {
   a1 += (p1[i] + p2[i])*(p1[i] - p2[i]);
}

引入了两个临时变量


for (size_t i = 0; i < N; ++i) {
  s[i] = (p1[i] + p2[i]);
  d[i] = (p1[i] - p2[i]);
  a1[i] += s[i]*d[i];
}

加减的结果s[i]和d[i]可以同时计算,最后一行不能执行,除非已知s[i]和d[i]的值。无论CPU一次可以做多少加法和乘法,但无法计算输入未知的操作的结果。而这里,CPU必
须等待乘法的输入准备就绪。
在这里插入图片描述

在这里插入图片描述

  1. 验证
    现在修改之前的代码

#include <stdlib.h>
#include <string.h>
#include <benchmark/benchmark.h>
#define MCA_START __asm volatile("# LLVM-MCA-BEGIN");
#define MCA_END __asm volatile("# LLVM-MCA-END");

// 定义benchmark测试函数BM_add,参数为benchmark::State类型的state
void BM_add(benchmark::State& state) {
    // 使用rand()函数生成两个大小为N的随机数组v1和v2
    srand(1);
    const unsigned int N = state.range(0);
    std::vector<unsigned long> v1(N), v2(N);
    for (size_t i = 0; i < N; ++i) {
        v1[i] = rand();
        v2[i] = rand();
    }
    // 分别定义指针p1和p2指向数组v1和v2的首元素
    unsigned long* p1 = v1.data();
    unsigned long* p2 = v2.data();
    // benchmark循环执行的代码,使用DoNotOptimize和ClobberMemory宏
    for (auto _ : state) {
        unsigned long a1 = 0, a2 = 0;
        for (size_t i = 0; i < N; ++i) {
            MCA_START
            a1 += p1[i] * p2[i];
            MCA_END
        }
        // 使用DoNotOptimize宏防止编译器优化掉benchmark测试
        benchmark::DoNotOptimize(a1);
        benchmark::DoNotOptimize(a2);
        // 使用ClobberMemory宏强制刷新寄存器
        benchmark::ClobberMemory();
    }
    // 设置benchmark测试的参数
    state.SetItemsProcessed(N*state.iterations());
    //state.SetBytesProcessed(N*sizeof(unsigned long)*state.iterations());
}

// 定义benchmark测试函数BM_add_multiply_dep,参数为benchmark::State类型的state
void BM_add_multiply_dep(benchmark::State& state) {
    // 使用rand()函数生成两个大小为N的随机数组v1和v2
    srand(1);
    const unsigned int N = state.range(0);
    std::vector<unsigned long> v1(N), v2(N);
    for (size_t i = 0; i < N; ++i) {
        v1[i] = rand();
        v2[i] = rand();
    }
    // 分别定义指针p1和p2指向数组v1和v2的首元素
    unsigned long* p1 = v1.data();
    unsigned long* p2 = v2.data();
    // benchmark循环执行的代码,使用DoNotOptimize和ClobberMemory宏
    for (auto _ : state) {
        unsigned long a1 = 0, a2 = 0;
        for (size_t i = 0; i < N; ++i) {
            MCA_START
            a1 += (p1[i] + p2[i]) * (p1[i] - p2[i]);
            MCA_END
        }
        // 使用DoNotOptimize宏防止编译器优化掉benchmark测试
        benchmark::DoNotOptimize(a1);
        benchmark::DoNotOptimize(a2);
        // 使用ClobberMemory宏强制刷新寄存器
        benchmark::ClobberMemory();
    }
    // 设置benchmark测试的参数
    state.SetItemsProcessed(N*state.iterations());
    //state.SetBytesProcessed(N*sizeof(unsigned long)*state.iterations());
}


// 定义一个宏ARGS,设置benchmark测试的参数
#define ARGS ->Arg(1<<22)

// 注册BM_add函数为benchmark测试函数,并使用ARGS宏设置测试参数
BENCHMARK(BM_multiply) ARGS;
BENCHMARK(BM_add_multiply_dep) ARGS;
// 使用BENCHMARK_MAIN()宏定义main函数
BENCHMARK_MAIN();

a1 += p1[i] * p2[i];a1 += (p1[i] + p2[i]) * (p1[i] - p2[i]); 测试看看效果


clang++-11 -g -O3 -mavx2 -Wall -pedantic -o test day4.cpp -lbenchmark -lprofiler
sudo perf record -g -a ./test

在这里插入图片描述

clang+±11 day4.cpp -g -O3 --std=c++17 -mllvm -x86-asm-syntax=intel -S -o - | llvm-mca-11 -mcpu=btver2 -timeline
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值