这个版本怎么样?
int total = 0;
for (unsigned mask = i & 0xFFFF, j = 0; mask != 0; mask >>= 1, j++){
total += (mask & 0x0001) * value[j];
}
我已将
mask
复制为
i
的副本,限制在16位无符号范围内,但代码检查
mask
的最后一位是否设置,如果设置,则将数组值乘以该位。这应该会更快,因为每次迭代的操作较少,只需要主循环分支和条件。此外,如果
i
开始时较小,循环可以提前退出。
这说明了为什么测量很重要。我正在使用过时的Sun SPARC。我编写了一个测试程序,如所示,采用来自问题的两个竞争者作为测试0和测试1,以及我的答案作为测试2。然后运行定时测试。打印“sum”是为了进行合理性检查-确保算法都给出相同的答案。
64位未优化:
gcc -m64 -std=c99 -I$HOME/inc -o x x.c -L$HOME/lib/sparcv9 -ljl -lposix4
Test 0: (sum = 1744366) 7.973411 us
Test 1: (sum = 1744366) 10.269095 us
Test 2: (sum = 1744366) 7.475852 us
很好:我的比原版略快,而升级版则更慢。
64位优化:
gcc -O4 -m64 -std=c99 -I$HOME/inc -o x x.c -L$HOME/lib/sparcv9 -ljl -lposix4
Test 0: (sum = 1744366) 1.101703 us
Test 1: (sum = 1744366) 1.915972 us
Test 2: (sum = 1744366) 2.575318 us
糟糕 - 我的版本现在变得极其缓慢。优化程序非常好!
32位优化:
gcc -O4 -std=c99 -I$HOME/inc -o x x.c -L$HOME/lib -ljl -lposix4
Test 0: (sum = 1744366) 0.839278 us
Test 1: (sum = 1744366) 1.905009 us
Test 2: (sum = 1744366) 2.448998 us
32位未优化:
gcc -std=c99 -I$HOME/inc -o x x.c -L$HOME/lib -ljl -lposix4
Test 0: (sum = 1744366) 7.493672 us
Test 1: (sum = 1744366) 9.610240 us
Test 2: (sum = 1744366) 6.838929 us
相同的代码在(32位)Cygwin和不那么老旧的笔记本电脑上(32位,经过优化)运行
Test 0: (sum = 1744366) 0.557000 us
Test 1: (sum = 1744366) 0.553000 us
Test 2: (sum = 1744366) 0.403000 us
现在我的代码是最快的。这就是为什么你要进行测量!这也说明了为什么专门进行基准测试的人会感到沮丧。
测试工具(如果需要,可以提供timer.h和timer.c代码):
#include <stdio.h>
#include "timer.h"
static volatile int value[] =
{
12, 36, 79, 21, 31, 93, 24, 15,
56, 63, 20, 47, 62, 88, 9, 36,
};
static int test_1(int i)
{
int total = 0;
for (unsigned short mask = 0x0001, j = 0; mask != 0; mask <<= 1, j++)
{
if (i & mask)
total += value[j];
}
return(total);
}
static int test_2(int i)
{
int total = 0;
for (unsigned short mask = 0x0001, j = 0; mask != 0; mask <<= 1, j++)
{
total += ((i & mask) != 0) * value[j];
}
return(total);
}
static int test_3(int i)
{
int total = 0;
for (unsigned mask = i & 0xFFFF, j = 0; mask != 0; mask >>= 1, j++)
{
total += (mask & 0x0001) * value[j];
}
return(total);
}
typedef int(*func_pointer)(int);
static func_pointer test[] = { test_1, test_2, test_3 };
#define DIM(x)(sizeof(x)/sizeof(*(x)))
int main()
{
int i, j, k;
char buffer[32];
for (i = 0; i < DIM(test); i++)
{
Clock t;
long sum = 0;
clk_init(&t);
clk_start(&t);
for (j = 0; j < 0xFFFF; j += 13)
{
int rv;
for (k = 0; k < 1000; k++)
rv = (*test[i])(j);
sum += rv;
}
clk_stop(&t);
printf("Test %d: (sum = %ld) %9s us\n", i, sum,
clk_elapsed_us(&t, buffer, sizeof(buffer)));
}
}
我还没有花时间弄清楚为什么我的代码在优化后变得更慢。
!!(i & mask)
代替((i & mask) != 0)
。 "!!" 是对 ! 运算符的滥用,通过两次应用它来创建一个“转换为bool”运算符。这不应该改变生成的汇编代码,但这是一种常见的习惯用法,并且对我来说更可读。 - kquinn