未对齐的指针性能

时间:2010-06-16 21:53:12

标签: c pointers alignment

不是未对齐的指针(在最好的情况下)应该会降低性能并且在最坏的情况下会使程序崩溃(假设编译器足够好以编译无效的c程序)。

嗯,以下代码似乎在对齐和未对齐版本之间没有任何性能差异。那是为什么?

/* brutality.c */

#ifdef BRUTALITY
    xs = (unsigned long *) ((unsigned char *) xs + 1);
#endif

...

/* main.c */

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

#define size_t_max ((size_t)-1)
#define max_count(var) (size_t_max / (sizeof var))

int main(int argc, char *argv[]) {

    unsigned long sum, *xs, *itr, *xs_end;
    size_t element_count = max_count(*xs) >> 4;

    xs = malloc(element_count * (sizeof *xs));
    if(!xs) exit(1);

    xs_end = xs + element_count - 1; sum = 0;

    for(itr = xs; itr < xs_end; itr++)
        *itr = 0;

#include "brutality.c"

    itr = xs;
    while(itr < xs_end)
        sum += *itr++;

    printf("%lu\n", sum);

    /* we could free the malloc-ed memory here */
    /* but we are almost done                  */
    exit(0);
}

使用

在两台独立的机器上进行编译和测试
gcc -pedantic -Wall -O0 -std=c99 main.c
for i in {0..9}; do time ./a.out; done

7 个答案:

答案 0 :(得分:3)

我过去在Win32机器上测试了一段时间,并没有注意到32位机器的大量惩罚。但是,在64位上,它明显变慢了。例如,我运行了以下代码。在32位机器上,打印的时间几乎没有改变。但是在64位机器上,未对齐访问的时间几乎是两倍。时间遵循代码。

#define UINT unsigned __int64
#define ENDPART QuadPart
#else
#define UINT unsigned int
#define ENDPART LowPart
#endif


int main(int argc, char *argv[])
{
   LARGE_INTEGER startCount, endCount, freq;
   int i;
   int offset;
   int iters = atoi(argv[1]);
   char *p = (char*)malloc(16);
   double *d;

   for ( offset = 0; offset < 9; offset++ )
      {
      d = (double*)( p + offset );
      printf( "Address alignment = %u\n", (unsigned int)d % 8 );
      *d = 0;
      QueryPerformanceFrequency(&freq);
      QueryPerformanceCounter(&startCount);
      for(i = 0; i < iters; ++i)
         *d = *d + 1.234;
      QueryPerformanceCounter(&endCount);

      printf( "Time:  %lf\n",
             (double)(endCount.ENDPART-startCount.ENDPART)/freq.ENDPART );
      }
}

以下是64位计算机上的结果。我将代码编译为32位应用程序。

[P:\t]pointeralignment.exe 100000000
Address alignment = 0
Time:  0.484156
Address alignment = 1
Time:  0.861444
Address alignment = 2
Time:  0.859656
Address alignment = 3
Time:  0.861639
Address alignment = 4
Time:  0.860234
Address alignment = 5
Time:  0.861539
Address alignment = 6
Time:  0.860555
Address alignment = 7
Time:  0.859800
Address alignment = 0
Time:  0.484898

答案 1 :(得分:2)

x86架构始终能够处理未对齐的访问,因此您永远不会遇到崩溃。其他处理器可能没那么幸运。

你可能没有看到任何时间差异,因为循环受内存限制;它只能以可以从RAM中获取数据的速度运行。您可能认为未对齐将导致RAM被访问两次,但第一次访问将其置于缓存中,第二次访问可以与从RAM获取下一个值重叠。

答案 2 :(得分:1)

您假设是x86或x64架构。例如,在MIPS上,您的代码可能会导致发出SIGBUS(总线故障)信号。在其他体系结构中,非对齐访问通常比对齐访问慢,但是,它非常依赖于体系结构。

答案 3 :(得分:0)

x86或x64?

未对齐的指针在x86中是一个杀手,其中64位架构几乎不容易发生崩溃,甚至根本没有降低性能。

答案 4 :(得分:0)

可能是因为那么多字节的malloc返回NULL。至少这就是它对我的作用。

答案 5 :(得分:0)

您从未在发布的代码中定义BRUTALITY。你确定你正在以“野蛮”模式进行测试吗?

答案 6 :(得分:0)

也许为了malloc这么庞大的缓冲区,系统会在磁盘上分页内存。这可能会淹没小的差异。尝试一个小得多的缓冲区和一个大的程序循环计数。

我在这里和评论中提出了mods并在我的系统上测试了(一个疲惫的,4岁,32位笔记本电脑)。代码如下所示。我确实得到了可衡量的差异,但只有3%左右。我保持我的更改是成功的,因为你的问题表明你没有任何区别(

抱歉,我正在使用Windows并使用我熟悉的特定于Windows的GetTickCount()API,因为我经常进行计时测试,并享受那个名不副实的API的简单性(它实际上从系统启动后返回毫秒)。

/* main.cpp */

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#define BRUTALITY

int main(int argc, char *argv[]) {
    unsigned long i, begin, end;
    unsigned long sum, *xs, *itr, *xs_begin, *xs_end;
    size_t element_count = 100000;

    xs = (unsigned long *)malloc(element_count * (sizeof *xs));
    if(!xs) exit(1);
    xs_end = xs + element_count - 1;
    #ifdef BRUTALITY
    xs_begin = (unsigned long *) ((unsigned char *) xs + 1);
    #else
    xs_begin = xs;
    #endif

    begin = GetTickCount();
    for( i=0; i<50000; i++ )
    {
        for(itr = xs_begin; itr < xs_end; itr++)
            *itr = 0;

        sum = 0;
        itr = xs_begin;
        while(itr < xs_end)
            sum += *itr++;
    }
    end = GetTickCount();

    printf("sum=%lu elapsed time=%lumS\n", sum, end-begin );

    free(xs);
    exit(0);
}
相关问题