不是未对齐的指针(在最好的情况下)应该会降低性能并且在最坏的情况下会使程序崩溃(假设编译器足够好以编译无效的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
答案 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);
}