C86上的64位循环性能

ise*_*dev 40 c algorithm performance 64-bit

我需要使用原始套接字的一些IPv4 ICMP处理代码的Internet Checksum功能(一个补码校验和),我偶然发现了我无法在64位Intel处理器上解释的行为(使用gcc 4.8.2).我想知道是否有人可以对此有所了解.

我使用32位累加器实现了第一个校验和功能,并执行了16位和.然后我使用64位累加器和32位求和实现了相同的功能,认为更少的总和将导致更快的执行.结果是第一个实现的运行速度是第二个实现的两倍(使用O3优化).我简直无法弄清楚为什么......

下面的代码实际上并没有执行准确的校验和(我简化了它),但说明了问题.两者都编译为在64位本机平台上运行64位(LP64:短16位,int 32位,长64位,指针64位).

  1. 32位累加器和16位和

    unsigned short
    cksum_16_le(unsigned char* data, size_t size)
    {
        unsigned short word;
        unsigned int sum = 0;
        unsigned int i;
    
        for(i = 0; i < size - 1; i += 2)
            sum += *((unsigned short*) (data + i));
    
        sum = (sum & 0xffff) + (sum >> 16);
        sum = (sum & 0xffff) + (sum >> 16);
    
        return ~sum;
    }
    
    Run Code Online (Sandbox Code Playgroud)

在相同的10k数据上调用50,000个函数:~1.1秒.

  1. 64位累加器和32位和

    unsigned short
    cksum_32_le(unsigned char* data, size_t size)
    {
        unsigned long word;
        unsigned long sum = 0;
        unsigned int i;
    
        for(i = 0; i < size - 3; i += 4)
            sum += *((unsigned int*) (data + i));
    
        sum = (sum & 0xffffffff) + (sum >> 32);
        sum = (sum & 0xffffffff) + (sum >> 32);
        sum = (sum & 0xffff) + (sum >> 16);
        sum = (sum & 0xffff) + (sum >> 16);
    
        return ~sum;
    }
    
    Run Code Online (Sandbox Code Playgroud)

在相同的10k数据上调用50,000个函数:~2.2秒.

更新:

似乎问题是由于硬件问题.运行内存诊断显示偶尔出现总线奇偶校验错误(不确定为什么这会比16位版本影响32位版本,但是你去了).代码在其他服务器上按预期运行.将在接下来的几个小时内删除问题(与硬件相关,它不再特别有用).

最后更新:

有趣的是,用for循环替换while循环并使用O3优化进行编译(如下面针对64位累加器情况所示)使得32位和64位累加器情况以相同的速度运行.这是因为编译器执行一些循环展开(奇怪的是,它不会展开for循环)并使用mmx寄存器执行求和...

uint64_t sum = 0;
const uint32_t* dptr = (const uint32_t*) data;

while (size > 3)
{
    sum += (uint32_t) *dptr++;
    size -= 4;
}
Run Code Online (Sandbox Code Playgroud)

Epi*_*Gen -3

sum += *((unsigned int*) (data + i)); 
Run Code Online (Sandbox Code Playgroud)

我不喜欢这样的演员阵容

64 位累加器和 32 位和

因为你写了:

两者均编译为在 64 位本机平台上运行的 64 位(LP64:short 16 位、int 32 位、long >64 位、指针 64 位)。

我建议使用 (unsigned long*)。有些人建议检查反汇编代码中实际发生的情况。我认为这是因为你的 int* 带有长累加器。

如果没有 O2<>O3 标志呢?你能显示一下正常编译模式下的速度是多少吗?