今天写了个测试itoa性能的程序,做了1千万次itoa操作,主要代码如下:
char buf[64];
unsigned long t = GetTickCount();
for (int i = 0; i <= 10000000; ++i) {
itoa(i, buf, 10);
}
unsigned long t2 = GetTickCount();
printf("last=%s, %u/n", buf, t2 - t);
分别使用VC++2003和C++BUILDER6编译,打开所有速度优化选项,结果发现性能相差很大。
VC最快用了2547ms,BCB最快用了4016ms。想想C运行库应该都是高度优化过的,怎么会有这么大的性能差距呢?
为了一探究竟,我翻出了itoa的源码,稍做修改(去掉了正负号处理)代码如下:
void myxtoa (unsigned long val, char *buf, unsigned radix)
{
char *p; /* pointer to traverse string */
char *firstdig; /* pointer to first digit */
char temp; /* temp char */
unsigned digval; /* value of digit */
p = buf;
firstdig = p; /* save pointer to first digit */
do {
digval = (unsigned) (val % radix);
val /= radix; /* get next digit */
/* convert to ascii and store */
if (digval > 9)
*p++ = (char) (digval - 10 + 'a'); /* a letter */
else
*p++ = (char) (digval + '0'); /* a digit */
} while (val > 0);
/* We now have the digit of the number in the buffer, but in reverse
order. Thus we reverse them now. */
*p-- = '/0'; /* terminate string; p points to last digit */
do {
temp = *p;
*p = *firstdig;
*firstdig = temp; /* swap *p and *firstdig */
--p;
++firstdig; /* advance to next two digits */
} while (firstdig < p); /* repeat until halfway */
}
我使用myxtoa这个函数代替C运行库中的itoa,再编译测试,VC和BCB的编译结果都比原来快了几十毫秒(去掉了符号处理当然快了)。看来BCB对这段代码优化得不如VC好。查看BCB生成的汇编代码后发现,BCB对val /= radix的编译结果是通过存储器读操作数的,没有放到寄存器中。为了测试是该语句影响了性能,将val /= radix语句注释掉,在VC和BCB下重新编译,发现BCB的运行结果居然比VC快了2倍多,看来两个编译器对寄存器变量使用的不同造成了性能上的差异。
为了证实自己的想法,我用汇编重写了myxtoa,用BCB编译,果然现在BCB版本跑的VC一样快了,基本两者都在几毫秒差距间波动。进一步使用__fastcall优化调用后,发现BCB版比VC版快了50-60ms,当然VC也能如此优化。下面是我改写的汇编代码BCB版(VC使用此代码只需对寄存器变量稍做修改即可):
void __fastcall myxtoa (unsigned long val, char *buf, unsigned radix)
{
__asm {
mov ebx, edx
mov esi, ebx
}
do {
__asm {
xor edx, edx
div ecx
}
if (_EDX > 9) {
_EDX += ('a' - 10);
}
else {
_EDX += '0';
}
__asm mov [ebx], dl
++_EBX;
} while (_EAX > 0);
/* We now have the digit of the number in the buffer, but in reverse
order. Thus we reverse them now. */
__asm mov byte ptr [ebx], 0
--_EBX;
do {
__asm {
mov cl, [ebx]
mov al, [esi]
mov [ebx], al
mov [esi], cl
}
--_EBX;
++_ESI;
} while (_ESI < _EBX); /* repeat until halfway */
}
总结,对寄存器变量的优化安排将有效提升程序性能,不过一般这种事情交给C++编译器就可以了,现在PC上的编译器已经足够强了。