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 分析的代码
- 上面代码生成bucketsort 可执行文件
valgrind --tool=callgrind ./bucketsort 1000000
调用命令生成分析的文件,这可以通过像kcachegrind这样的前端程序来可视化.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;
}
- 工具安装
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
- google-pprof 读取保存的prof.data
google-pprof --text ./test prof.data
分析器显示,大多时间都花在比较函数compare()上,而排序几乎不花时间
-
google-pprof交互式
text text --lines
-
生成调用图
google-pprof --pdf ./test prof.data > prof.pdf
perf分析器
- 安装 我的是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
- 编译程序并执行
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
- 安装工具
sudo apt-fast install libbenchmark-dev
- 代码
#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();
- 编译
clang++-11 -g -O0 -mavx2 -Wall -pedantic -o test day1.cpp -lbenchmark
sudo perf record -g -a ./test
- 结果
Benchmark列:列出了我们所测量的基准的名称,如“BM_Sort”。
Time列:列出了每次执行基准所花费的时间(平均时间)。它以秒为单位呈现,这里使用了微秒(us)的分辨率,即以百万分之一秒为单位。
CPU列:列出了经过的 CPU 时间。与时间列不同,CPU 时间是在 CPU 上运行的时间,因此它更为准确。
Iterations列:列出了benchmark的运行次数。benchmark会根据不同的参数重复执行benchmark,以减小测量误差。每次benchmark运行所花费的时间是Time和CPU除以迭代次数。
- 分析代码
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必
须等待乘法的输入准备就绪。
- 验证
现在修改之前的代码
#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