用x64汇编编写国密SM3散列算法遇到了一个小问题:
假定32位数组W[68]的前7个数据被导入xmm0:1寄存器:
|-------------------|-------------------|
| xmm0 | xmm1 |
|----|----|----|----|----|----|----|----|
|W[0]|W[1]|W[2]|W[3]|W[4]|W[5]|W[6]|W[7]|
|----|----|----|----|----|----|----|----|
现要将W[3:6]导入xmm2寄存器:
|-------------------|
| xmm2 |
|----|----|----|----|
|W[3]|W[4]|W[5]|W[6]|
|----|----|----|----|
不许改变xmm0:1的内容,可以使用其它xmm寄存器,CPUID限定06_5EH。
能够产生正确结果的代码并非唯一:
代码一:
pextrd $3, %xmm0, %eax #eax = W[3]
pshufd $0x93, %xmm1, %xmm2 #xmm2 = |W[7]|W[4]|W[5]|W[6]|
pinsrd $0, %eax, %xmm2 #xmm2 = |W[3]|W[4]|W[5]|W[6]|
代码二:
pextrd $3, %xmm0, %eax #eax = W[3]
movdqa %xmm1, %xmm2 #xmm2 = |W[4]|W[5]|W[6]|W[7]|
psrldq $0x8, %xmm2 #xmm2 = |0000|W[4]|W[5]|W[6]|
pinsrd $0, %eax, %xmm2 #xmm2 = |W[3]|W[4]|W[5]|W[6]|
代码三:
movdqa %xmm1, %xmm2 #xmm2 = |W[4]|W[5]|W[6]|W[7]|
pslldq $0x4, %xmm2 #xmm2 = |0000|W[4]|W[5]|W[6]|
movdqa %xmm0, %xmm3 #xmm3 = |W[0]|W[1]|W[2]|W[3]|
psrldq $0x12, %xmm3 #xmm3 = |w[3]|0000|0000|0000|
por %xmm3, %xmm2 #xmm2 = |W[3]|W[4]|W[5]|W[6]|
分别将上述代码插入循环测试程序中,得到的结果是:代码一和代码二速度一样,
代码三比前者速度快一倍!查阅Intel编程手册才发现这里面有个大坑:pextrd
指令的耗时高达3,pinsrd指令耗时为2,两者的冷却都是1,而其它的指令耗时只有1,
冷却最短的movdqa只有0.25,字节位移指令pslldq和psrldq的冷却也只有0.33,
导致代码三性能大幅度胜出。pextrd比pinsrd还慢尤其毁三观,以内存为例,写入
速度比读取速度慢N倍都是很正常的,然而xmm寄存器和通用寄存器的交换过程中,
导出(类同读取)竟然比导入(类同写入)还慢50%,这个耗时已经接近64位乘法(耗时4)
和L1缓存读取64位数据(耗时4),即便与64位写入L1缓存(耗时5)相比,pextrd的耗时
3也是惊人的。