国科大-高性能计算编程-作业一
作业题目
提交内容一:平台信息
- 本次作业在Windows平台及MacOs平台上均测试了homework1.cpp的运行,具体平台信息如下
Windows平台
系统:Win11-23H2
CPU:i9-13900HX(x86)
- 内存:64G
- IDE:CLion
- 编译器:MinGW
MacOs平台
- 系统:MacOs-14.3
- CPU:M2(arm)
- 内存:16G
- IDE:CLion
- 编译器:Clang
提交内容二:代码运行时间
- 本次作业分别在两个平台上使用-O0 -O1 -O2 -O3编译homework1.cpp,并记录四种优化条件下代码的运行时间,具体结果如下
Windows平台
-O0编译
-O1编译
-O2编译
-O3编译
数据总览
Win-MinGW | Vecadd Timing/us | Matrix Multiply Timing/us | Pi Timing/us |
---|---|---|---|
-O0 | 3224 | 2557602 | 4074 |
-O1 | 2669 | 2310377 | 1001 |
-O2 | 2009 | 2344274 | 1017 |
-O3 | 2000 | 2358837 | 1651 |
MacOs平台
-O0编译
-O1编译
-O2编译
-O3编译
数据总览
Mac-Clang | Vecadd Timing/us | Matrix Multiply Timing/us | Pi Timing/us |
---|---|---|---|
-O0 | 8858 | 6661187 | 6147 |
-O1 | 0 | 0 | 2358 |
-O2 | 0 | 1 | 2390 |
-O3 | 1 | 0 | 2592 |
- 可以发现在Mac端,使用-O1及以上优化级别进行编译时,Vecadd Timing及Matrix Multiply Timing均为0us或1us,因此可能是这两个函数根本没被运行。查看代码发现,原实验代码中Vecadd和Matrix Multiply函数计算完c[1024*1024]及cc[1024*1024]后,这两个数组都没有再被使用过,因此可能是Clang编译器识别到这一情况后直接将这两个函数视为无用代码在编译时进行了删除。因此修改源代码,在计算完c[1024*1024]及cc[1024*1024]后分别输出一下c[0],让编译器在编译时不删除这两个函数的内容。以下是修改源代码后再次测试的结果
MacOs平台修改源码后再次测试
-O1编译
-O2编译
-O3编译
数据总览
Mac-Clang | Vecadd Timing/us | Matrix Multiply Timing/us | Pi Timing/us |
---|---|---|---|
-O0 | 8858 | 6661187 | 6147 |
-O1 | 6532 | 2105799 | 1745 |
-O2 | 5817 | 2105989 | 1564 |
-O3 | 2585 | 2104009 | 1601 |
提交内容三:优化方法分析
- 以Windows平台,Vecadd函数为例进行分析
-O0
- 不进行任何优化
-O1
- 在 -O0中,vecadd 函数的循环是这样的:
.L7:
movq -8(%rbp), %rax
cmpq 40(%rbp), %rax
jnb .L6
movq -8(%rbp), %rax
leaq 0(,%rax,8), %rdx
movq 16(%rbp), %rax
addq %rdx, %rax
movsd (%rax), %xmm1
movq -8(%rbp), %rax
leaq 0(,%rax,8), %rdx
movq 24(%rbp), %rax
addq %rdx, %rax
movsd (%rax), %xmm0
movq -8(%rbp), %rax
leaq 0(,%rax,8), %rdx
movq 32(%rbp), %rax
addq %rdx, %rax
addsd %xmm1, %xmm0
movsd %xmm0, (%rax)
addq $1, -8(%rbp)
jmp .L7
.L6:
movl $0, %eax
addq $16, %rsp
popq %rbp
ret
- 在 -O1中,vecadd 函数的循环是这样的:
.L5:
movsd (%rcx,%rax,8), %xmm0
addsd (%rdx,%rax,8), %xmm0
movsd %xmm0, (%r8,%rax,8)
addq $1, %rax
cmpq %rax, %r9
jne .L5
.L4:
movl $0, %eax
ret
- 循环展开:在 -O1 中,循环体被简化了。在 -O0 中,我们看到了对索引的计算(
leaq 0(,%rax,8), %rdx
),然后使用这个索引来访问数组。在 -O1 中,这个计算被直接内联到循环体中,减少了对寄存器的依赖和循环控制的开销。 - 寄存器使用:在 -O1 中,编译器对寄存器的使用进行了优化,相比于-O0更加高效
- 循环简化:在 -O1 中,循环控制(
addq $1, %rax
,cmpq %rax, %r9
)和条件跳转(jne .L5
)相较于-O0中的代码而言更加简化,这有助于提高循环的执行效率。
-O2
- -O2的vecadd函数基本与-O1中一样,证明-O1已经对该函数进行了足够的简化,或该函数较为简单,无须再次优化
-O3
在 -O2中,vecadd 函数的汇编代码是这样的:
.L6: movsd (%rcx,%rax,8), %xmm0 addsd (%rdx,%rax,8), %xmm0 movsd %xmm0, (%r8,%rax,8) addq $1, %rax cmpq %rax, %r9 jne .L6 .L5: xorl %eax, %eax ret
在 -O3中,vecadd 函数的汇编代码是这样的:
.L7: movq (%rcx,%rax), %xmm0 movq (%rdx,%rax), %xmm1 movhpd 8(%rcx,%rax), %xmm0 movhpd 8(%rdx,%rax), %xmm1 addpd %xmm1, %xmm0 movlpd %xmm0, (%r8,%rax) movhpd %xmm0, 8(%r8,%rax) addq $16, %rax cmpq %rax, %r10 jne .L7 movq %r9, %rax andq $-2, %rax cmpq %rax, %r9 je .L5 movsd (%rcx,%rax,8), %xmm0 addsd (%rdx,%rax,8), %xmm0 movsd %xmm0, (%r8,%rax,8) .L5: xorl %eax, %eax ret
向量化操作:在 -O3 中,编译器添加了 movhpd 指令的使用,这表明编译器可能在尝试利用 SSE2 指令集进行向量化操作。这可以显著提高数据处理的效率,因为它允许同时处理多个数据。
- 循环展开:在 -O3 中,循环体被进一步展开,以减少循环控制的开销。例如,movhpd 指令用于同时加载和存储两个双字(64位)浮点数,这减少了内存访问次数。
- 条件分支优化:在 -O3 中,编译器对条件分支进行了优化,例如通过使用 setnb 和 seta 指令来优化条件跳转。这有助于提高分支预测的准确性。
- 循环控制逻辑:在 -O3 中,循环控制逻辑更加复杂,这表明编译器可能在尝试更精细地控制循环的执行,以减少不必要的计算和内存访问。
总结
- -O0:不进行优化
- -O1:主要对代码的分支、表达式、循环体等进行优化
- -O2:尝试更多的寄存器级和汇编指令级的优化,增大调试难度
- -O3:在O2的基础上对循环体等进行更多精细、激进的优化,可能会使汇编代码的逻辑更加复杂,加大精度的同时可能会增大运行时间及调试难度
- Clang编译器的优化更为激进,会删除不影响程序运行的代码段(例如定义后未使用的变量等),因此在本次作业中需要修改源代码才能进行运行时间的测量,否则编译器会直接删除main函数中Vecadd以及Matrix Multiply函数的调用
自评分及理由
自评分
- 10分
评分理由
- 在Windows及MacOs双平台完成作业
- 收集双平台数据并绘制总览图表
- 根据汇编代码进行不同优化级别的优化方法分析
- 分析Clang(Mac)进行-O1以上级别的编译时代码运行时间为0的原因
- 对比分析MinGW(Win)及Clang(Mac)的优化策略
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yuichi's Blog!