|
|
<1>内存大小失配(mismatches)
当不同的指令操作相同的数据时要避免内存大小失配
当一条指令读写了一个数据,随后另一条指令又读写
这个数据,则要保证数据是对齐的,而且保证两次都
以相同的大小读写
EX:
foo DQ ? ; 假设是8B对齐的
避免:
mov DWORD PTR foo, eax
mov DWORD PTR foo + 4, ebx
mov rcx, QWORD PTR foo
或
mov foo, eax
mov foo + 4, ebx
......
movq mm0, foo
建议:
mov foo, eax
mov foo + 4, ebx
......
movd mm0, foo
punpckldq mm0, foo + 4
<2>数据对象的自然对齐
读写对齐的数据至少在CPU的读写管线上耽搁一个时钟周期
数据对齐规则:
字(Word) 地址能被2整除
双字(Double Word) 地址能被4整除
四字(Quad Word) 地址能被8整除
十字节(Ten-byte) 地址能被8整除
双四字(Double Quad Word) 地址能被16整除
<3>适当的内存拷贝程序
<4>分支的密度
尽可能使分支对齐,不要使她们交叉与16B边界
Athlon64可以对一个16B(对齐的)代码窗中的三
条分支指令预取,所以尽可能使分支对齐,并且
在一个16B代码窗中不要放置超过三条分支指令
这些分支指令为:
jcc rel8
jcc rel32
jmp rel8
jmp rel32
jmp reg
jmp WORD PTR
jmp DWORD PTR
call rel16
call r/m16
call rel32
call r/m32
<5>预取指令
在读写大块连续内存时使用预取指令充分利用Athlon64的内存带宽
这些指令为:
读: PREFETCH / PREFETCHT0 / PREFETCHT1 / PREFETCHT2
写: PREFETCHW
无时间性: PREFETCHNTA
<6>双字节近返回指令
使用双字节近返回指令可以提高性能,因为CPU无法对单字节近返回指令分支预测
在使用单字节近返回指令时要避免以下两种情况:
(1) 所有的分支跳转指令以单字节近返回指令为目标;
(2) 在单字节指令前直接放置条件跳转指令
最简单的双字节近返回指令是在返回指令前加上重复前缀,宏定义如下:
REPRET TEXTEQU <0F3H, 0C3H>
<7>直接路径(DirectPath)指令
尽可能使用直接路径(DirectPath)指令,不要使用矢量路径(VectorPath)指令
直接路径(DirectPath)指令在Athlon64的流水线上有更好的执行性能
<8>读取--执行 整数指令
使用读取--执行整合的整数指令代替读取和执行分开的整数指令,除非你要
配对流水线,读取--执行整合的整数指令大多数为直接路径(DirectPath)指
令,比分离的指令有更好的执行性能
<9>读取--执行 浮点操作数的浮点指令
同上,这样的指令可以提高性能,原因如下:
(1) 可以提高指令Cache的命中率;
(2) 更少的macro-ops,提高并行执行的机率
EX:
避免:
movss xmm0, [float_var1]
movss xmm12, [float_var2]
mulss xmm0, xmm12
建议:
movss xmm0, [float_var1]
mulss xmm0, [float_var2]
<10>读取--执行 整数操作数的浮点指令
避免使用读取--执行整数操作数的X87指令,因为这样的指令为
矢量路径(VectorPath)指令,执行效率低
EX:
避免:
fld QWORD PTR [foo]
fimul DWORD PTR [bar]
fiadd DWORD PTR [baz]
建议:
fild DWORD PTR [bar]
fild DWORD PTR [baz]
fld QWORD PTR [foo]
fmulp st(2), st
faddp st(1), st
<11>写合并(Write-combining)
<12>依赖随机数据的分支
避免依赖随机数据的分支,以提高分支预测的正确机率用条
件移动(cmovcc)和条件设置(setbcc)指令代替分支指令
<13>SSE和SSE2指令与数据
用相应的SSE和SSE2指令操作相应的数据类型,否则可能会降低效率
<14>不把数据与程序放在同一个64B Cache线
避免把数据与程序放在同一个64B Cache线,尤其数据会被改变
避免代码自修改和将代码放在数据段里,这些会导致L1 Cache
性能下降
|
|