C 英特尔(x86_64)64位与32位整数算术性能差异

C 英特尔(x86_64)64位与32位整数算术性能差异,c,linux,performance,x86,cpu-architecture,C,Linux,Performance,X86,Cpu Architecture,我正在测试某个程序,突然发现了一个意想不到的异常 我编写了一个计算素数的简单程序,并使用pthreadsapi来并行化这个工作负载 在进行了一些测试之后,我发现如果我使用uint64_t作为计算和循环的数据类型,那么程序运行所需的时间要比使用uint32_t时长得多 以下是我运行的代码: #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <pthread.h>

我正在测试某个程序,突然发现了一个意想不到的异常

我编写了一个计算素数的简单程序,并使用pthreadsapi来并行化这个工作负载

在进行了一些测试之后,我发现如果我使用uint64_t作为计算和循环的数据类型,那么程序运行所需的时间要比使用uint32_t时长得多

以下是我运行的代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <pthread.h>

#define UINT uint64_t
#define SIZE (1024 * 1024)

typedef struct _data
{
    UINT start;
    UINT len;
    int t;
    UINT c;
}data;

int isprime(UINT x)
{
    uint8_t flag = 1;

    if(x < 2)
        return 0;

    for(UINT i = 2;i < x/2; i++)
    {
        if(!(x % i ))
        {
            flag = 0;
            break;
        }
    }

    return flag;
}

void* calc(void *p)
{
    data *a = (data*)p;

    //printf("thread no. %d has start: %lu length: %lu\n",a->t,a->start,a->len);

    for(UINT i = a->start; i < a->len; i++)
    {
        if(isprime(i))
            a->c++;
    }

    //printf("thread no. %d found %lu primes\n", a->t,a->c);
    
    pthread_exit(NULL);
}

int main(int argc,char **argv)
{   
    pthread_t *t;
    data *a;
    uint32_t THREAD_COUNT;

    if(argc < 2)
        THREAD_COUNT = 1;
    else
        sscanf(argv[1],"%u",&THREAD_COUNT);
    
    t = (pthread_t*)malloc(THREAD_COUNT * sizeof(pthread_t));
    a = (data*)malloc(THREAD_COUNT * sizeof(data));

    printf("executing the application on %u thread(s).\n",THREAD_COUNT);

    for(uint8_t i = 0; i < THREAD_COUNT; i++)
    {
        a[i].t = i;
        a[i].start = i * (SIZE / THREAD_COUNT);
        a[i].len = a[i].start + (SIZE / THREAD_COUNT);
        a[i].c = 0;
    }
    
    for(uint8_t i = 0; i < THREAD_COUNT; i++)
        pthread_create(&t[i],NULL,calc,(void*)&a[i]);
    for(uint8_t i = 0; i < THREAD_COUNT; i++)
        pthread_join(t[i],NULL);

    free(a);
    free(t);
    
    return 0;
}

#包括
#包括
#包括
#包括
#定义UINT uint64\u t
#定义大小(1024*1024)
类型定义结构数据
{
UINT启动;
单透镜;
int t;
UINT c;
}数据;
int iPrime(UINT x)
{
uint8_t标志=1;
if(x<2)
返回0;
对于(UINT i=2;it,a->start,a->len);
对于(UINT i=a->start;ilen;i++)
{
如果(i)
a->c++;
}
//printf(“线程编号%d找到%lu素数\n”,a->t,a->c);
pthread_exit(NULL);
}
int main(int argc,字符**argv)
{   
pthread_t*t;
数据*a;
uint32螺纹计数;
如果(argc<2)
线程计数=1;
其他的
sscanf(argv[1]、“%u”和线程计数);
t=(pthread_t*)malloc(THREAD_COUNT*sizeof(pthread_t));
a=(数据*)malloc(线程计数*大小(数据));
printf(“在%u个线程上执行应用程序。\n”,线程数);
对于(uint8\u t i=0;i
我在uint32_t和uint64_t之间更改了UINT宏,编译并运行了该程序,并在linux上使用time命令确定了其运行时间

我发现uint64\u t和uint32\u t的运行时之间存在重大差异

使用uint32时,程序运行时间46s,而使用uint64时,程序运行时间2m49s

我在这里写了一篇关于它的博客:

如果你想了解更多信息,可以查看这篇文章


这背后可能有什么问题?x86_64上的64位算术是否比32位算术慢?

一般来说,64位算术与32位算术一样快,忽略了较大的操作数占用更多内存和BW等因素,而在x86-64上寻址完整的64位寄存器需要更长的指令


但是,您成功地找到了该规则的少数例外之一,即用于计算除法的
div
指令。

如果将
struct\u data
填充为64字节大小,问题是否会消失?较小的结构意味着同一64字节缓存线上有更多的结构,这将影响性能。@MikelRychliski对于uint32\t,结构的大小是16字节,对于uint64\t,结构的大小是32字节,根据您的建议,我将两者都填充为64字节。再次运行测试后,未观察到任何差异,使用uint64时,单线程上需要2m49s,而uint32时,单线程上需要46s。请参阅哪项确认64位除法(由您的
x%i
调用)的成本大约是32位除法的3-4倍。因此,这似乎与您观察到的一致。重复:,稍微放一放,对于像在主函数中那样执行数组索引的循环,为了获得最佳性能,您通常希望使循环计数器的类型为
size\u t
。有趣的事实:这是英特尔效应(在IceLake之前的CPU上)。在AMD上,
div r64
div r32
如果输入数字相同,则具有(几乎?)相同的性能。(请参见链接副本)