SHA512编程实现与优化之一:maj函数

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

SHA512算法中的maj函数可以C函数简单方便地实现:

#include 

int64_t maj(int64_t a, int64_t b, int64_t c)
{
	return((a & b) ^ (a & c) ^ (b & c));
}

从字面上看,逻辑乘"&"有3次,异或"^"有2次,事实真的如此吗?
将此函数编译成汇编语言得到如下结果:(注释是我后加上的)


	.file	"maj.c"
	.text
	.p2align 4,,15
.globl maj
	.type	maj, @function
maj:
.LFB0:
	.cfi_startproc
#	rdi = a
#	rsi = b
#	rdx = c
	movq	%rsi, %rax	#rax = b
	xorq	%rdi, %rax	#rax = (rdi ^ rax) = (a ^ b)
	andq	%rsi, %rdi	#rdi = (rsi & rdi) = (b & a)
	andq	%rdx, %rax	#rax = (rdx & rax) = (c & (a ^ b)) = ((c & a) ^ (c & b))
	xorq	%rdi, %rax	#rax = (rdi ^ rax) = ((b & a) ^ (c & a) ^ (c & b))
	ret			#return rax as result
	.cfi_endproc
.LFE0:
	.size	maj, .-maj
	.ident	"GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-16)"
	.section	.note.GNU-stack,"",@progbits

阅读汇编代码可知,实际运算是:逻辑乘运算"andq"2次,异或运算"xorq"2次,
显然,编译器依据逻辑代数运算规则进行了化简,如果不化简按照字面直译,
我自己写的汇编代码如下:(andq x 3, xorq x 2)

#	rdi = a
#	rsi = b
#	rdx = c
	movq	%rsi, %rax	#rax = b
	andq	%rdi, %rax	#rax = (rdi & rax) = (a & b)
	andq	%rdx, %rsi	#rsi = (rdx & rsi) = (c & b)
	andq	%rdx, %rdi	#rdi = (rdx & rdi) = (c & a)
	xorq	%rdi, %rax	#rax = (rdi ^ rax) = ((c & a) ^ (a & b))
	xorq	%rsi, %rax	#rax = (rsi ^ rax) = ((c & b) ^ (c ^ a) ^ (a & b))
	ret			#return rax as result

两种代码孰优孰劣,可以说是一目了然,GCC只用了5条指令,后者用了6条指令,
前者在运算过程中只改变两个寄存器(rax,rdi)的数值,后者在运算中改变了四个
寄存器(rax,rsi,rdi,rdx)的数值。对这么简单的函数的编程实现吹毛求疵有意义吗?
当然有,别忘了x64架构是超标量处理器,多1条指令可能多一个超标量时间片,
相当于多4个单周期指令,更糟糕的是如果临近长周期指令,很可能打乱本来很好
的超标量分组,增加1个单周期指令多消耗两个超标量时间片都是有可能的。

转载于:https://my.oschina.net/safedead/blog/678587

你可能感兴趣的:(SHA512编程实现与优化之一:maj函数)