有人提供一个示例是否会因为错位而将指针从一种类型转换为另一种类型失败?
在对这个答案的评论中,两者都表示做了类似的事情
char * foo = ...; int bar = *(int *)foo;
如果启用了对齐检查,即使在x86上也可能导致错误.
我set $ps |= (1<<18)
在GDB中设置对齐检查标志后尝试生成错误条件,但没有任何反应.
工作(即非工作;))示例是什么样的?
答案中没有任何代码片段在我的系统上失败 - 我将尝试使用不同的编译器版本,稍后在不同的PC上.
顺便说一句,我自己的测试代码看起来像这样(现在也使用asm来设置AC
标志和未对齐的读写):
#includeint main(void) { #ifndef NOASM __asm__( "pushf\n" "orl $(1<<18),(%esp)\n" "popf\n" ); #endif volatile unsigned char foo[] = { 1, 2, 3, 4, 5, 6 }; volatile unsigned int bar = 0; bar = *(int *)(foo + 1); assert(bar == 0x05040302); bar = *(int *)(foo + 2); assert(bar == 0x06050403); *(int *)(foo + 1) = 0xf1f2f3f4; assert(foo[1] == 0xf4 && foo[2] == 0xf3 && foo[3] == 0xf2 && foo[4] == 0xf1); return 0; }
断言传递没有问题,即使生成的代码肯定包含未对齐的访问mov -0x17(%ebp), %edx
和movl $0xf1f2f3f4,-0x17(%ebp)
.
那么设置是否会AC
触发SIGBUS
?我无法在Windows XP下运行我的英特尔双核笔记本电脑而没有我测试的GCC版本(MinGW-3.4.5,MinGW-4.3.0,Cygwin-3.4.4),而codelogic和Jonathan Leffler在x86上提到了失败......
在未对齐访问将导致x86出现问题(除了使内存访问需要更长时间)之外,这种情况并不常见.以下是我听过的一些内容:
您可能不会将此视为x86问题,但SSE操作会从对齐中受益.对齐数据可用作存储器源操作数以保存指令.在Nehalem之前,未对齐加载指令movups
比movaps
在微体系结构上慢,但在Nehalem和更高版本(以及AMD Bulldozer系列)上,未对齐的16字节加载/存储与未对齐的8字节加载/存储一样有效; 如果数据恰好在运行时对齐或者没有跨越缓存行边界,则单个uop并且没有任何惩罚,否则高效的硬件支持缓存行分割.4k分裂非常昂贵(约100个周期)直到Skylake(低至~10个周期,如高速缓存线分割).有关详细信息,请参阅x86标记wiki中的https://agner.org/optimize/和性能链接.
如果它们没有充分对齐,则互锁操作(例如lock add [mem], eax
)非常慢,特别是如果它们跨越缓存行边界,因此它们不能仅在CPU内核中使用缓存锁定.在较旧的(有缺陷的)SMP系统上,它们实际上可能无法成为原子(参见https://blogs.msdn.com/oldnewthing/archive/2004/08/30/222631.aspx).
Raymond Chen讨论的另一种可能性是处理具有硬件存储内存的设备(当然是一种奇怪的情况) - https://blogs.msdn.com/oldnewthing/archive/2004/08/27/221486.aspx
我记得(但没有参考 - 所以我不确定这个)类似的问题与未对齐的访问跨越页面边界,也涉及页面错误.我会看看我是否可以为此挖掘参考.
在研究这个问题时我学到了一些新东西(我想知道$ps |= (1<<18)
几个地方提到的"GDB命令").我没有意识到x86 CPU(从486开始)能够在执行错位访问时导致异常.
来自Jeffery Richter的"Windows编程应用程序,第4版":
让我们仔细看看x86 CPU如何处理数据对齐.x86 CPU在其EFLAGS寄存器中包含一个称为AC(对齐检查)标志的特殊位标志.默认情况下,当CPU首次接通电源时,此标志设置为零.当此标志为零时,CPU会自动执行任何操作,以便成功访问未对齐的数据值.但是,如果此标志设置为1,则只要尝试访问未对齐的数据,CPU就会发出INT 17H中断.x86版本的Windows 2000和Windows 98从不改变此CPU标志位.因此,当应用程序在x86处理器上运行时,您永远不会看到应用程序中发生数据错位异常.
这对我来说是新闻.
当然,访问错位的一个大问题是,当你最终编译非x86/x64处理器的代码时,你最终必须追踪并修复一大堆东西,因为几乎所有其他32位或更大的处理器对对齐问题很敏感.
如果您阅读了Core I7架构(特别是他们的优化文献),那么英特尔实际上已经在其中放置了一个TON硬件,以使未对齐的内存访问几乎免费.据我所知,只有跨越高速缓存行边界的错位才有任何额外成本 - 即便如此,它也是最小的.就我记忆而言,AMD在访问错位(循环方式)方面也没有什么问题(虽然已经有一段时间了).
为了它的价值,我确实在eflags(AC位 - 对齐检查)中设置了那个标志,当我被带走时优化我正在进行的项目.事实证明,窗口是完全未对齐的访问 - 很多,以至于我无法在我们的代码中找到任何未对齐的内存访问,我被库和Windows代码中的这么多未对齐的访问轰炸,我没有时间继续.
也许我们可以了解到,当CPU使产品免费或成本非常低时,程序员会变得自满并做一些额外开销的事情.也许英特尔的工程师做了一些调查,发现典型的x86桌面软件每秒会进行数百万次错位访问,因此他们在CoreI7中放置了极其快速错位的访问硬件.
HTH