由汇编内核的MD5算法编写谈代码优化
2010-06-23 20:41:03 来源:WEB开发网二、内存拷贝优化
再观察一下MD5C里面的一段代码:
static void MD5_memcpy (unsigned char *output, unsigned int *input, unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i++)
output[i] = input[i];
}
这处的为什么要修改是非常明显的,for循环是非常慢的,我们一般可以把类似的代码替换成为C的库函数或者操作系统的标准函数,如:
CopyMemory ()
memcpy()
这种内存代码你也千万不要尝试自己去实现,那将是一种灾难,在每个操作系统中,内存拷贝可以说是非常频繁的,所以系统的内存拷贝函数基本上都是非常完美的,不信的话你可以自己写一段内存拷贝函数,然后和系统的内存拷贝函数比较一下就知道了,具体原代码可以参考linux中string.lib的实现。
这处代码是特别值得注意的地方,如果你在你的代码的运算密集处写出了类似MD5_memcpy的代码的化,那么性能将会急剧的下降,对你的系统将是灾难性的。
三、使用汇编优化
下面又一段MD5C里面的代码,这种代码每一次md5加密要运算64次,而我们要穷举8位数字的话,就需要运算64000000次,将下面的代码用汇编展开以后大概是23行的汇编代码,也就是这个转换将耗费147200000000,即1472亿个指令周期。
//参考test/test1的原代码
#define UINT4 unsigned int
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define FF(a, b, c, d, x, s, ac) {
(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac);
(a) = ROTATE_LEFT ((a), (s));
(a) += (b);
}
3.1 减少内存的访问次数
把test1的代码反汇编得到代码如下:
mov eax,dword ptr [ebp-8]
and eax,dword ptr [ebp-0Ch]
mov ecx,dword ptr [ebp-8]
not ecx <-
and ecx,dword ptr [ebp-10h]
or eax,ecx <-
add eax,dword ptr [ebp-14h]
add eax,dword ptr [ebp-1Ch]
mov edx,dword ptr [ebp-4]
add edx,eax <-
mov dword ptr [ebp-4],edx
mov eax,dword ptr [ebp-4]
mov ecx,dword ptr [ebp-18h]
shl eax,cl <-
mov ecx,20h <-
sub ecx,dword ptr [ebp-18h]
mov edx,dword ptr [ebp-4]
shr edx,cl <-
or eax,edx <-
mov dword ptr [ebp-4],eax
mov eax,dword ptr [ebp-4]
add eax,dword ptr [ebp-8]
mov dword ptr [ebp-4],eax
从上面的代码可以看到,23条编译后的汇编代码中有16条是涉及内存访问指令,考虑到逻辑运算和四则运算的平均指令周期是4,而内存访问指令平均指令周期是10,所以上叙的内存访问指令将消耗整个程序时间的七成以上。所以,个人认为汇编的优化,其实质就是减少内存的访问,能够在寄存器内部完成的就不要反复的访问内存,此处我采取强制定义的方式使得这段程序基本不访问内存。
考虑到 FF(a, b, c, d, x, s, ac) 中s,ac是常数,x是一个数组,不好强制定义成寄存器,所以这里只强制定义了a,b,c,d四个变量为寄存器,以及tmp1,tmp2两个寄存器类型的临时变量。//参考test/test2的原代码
#define a esi
#define b edi
#define c edx
#define d ebx
#define tmp1 eax
#define tmp2 ecx
更多精彩
赞助商链接