什么是“堆栈alignment”?

什么是堆栈alignment? 为什么使用? 它可以通过编译器设置来控制吗?

这个问题的细节是从尝试使用msfc使用ffmpeg库时面临的一个问题,但是我真正感兴趣的是解释什么是“堆栈alignment”。

细节:

  • 当运行我的msvc complied程序链接到avcodec我得到以下错误:“编译器没有alignment堆栈variables。libavcodec已被编译错误”,其次是在avcodec.dll崩溃。
  • avcodec.dll没有编译与msvc,所以我无法看到里面发生了什么。
  • 当运行ffmpeg.exe和使用相同的avcodec.dll一切正常。
  • ffmpeg.exe没有用msvc编译,它被编译为gcc / mingw(与avcodec.dll相同)

谢谢,

记忆中variables的alignment(短历史)。

以前的电脑有8位数据总线。 这意味着每个时钟周期8位信息可以被处理。 那很好。

然后来了16位电脑。 由于向下兼容等问题,保留了8位字节,引入了16位字。 每个字是2个字节。 每个时钟周期16位信息可以被处理。 但这提出了一个小问题。

我们来看一下内存映射:

+----+ |0000| |0001| +----+ |0002| |0003| +----+ |0004| |0005| +----+ | .. | 

每个地址都有一个可以单独访问的字节。 但是单词只能在偶数地址上获取。 所以,如果我们在0000读取一个字,我们读取0000和0001的字节。但是如果我们想要读取位置0001的字,我们需要两次读取访问。 首先0000,0001然后0002,0003,我们只保留0001,0002。

当然,这需要一些额外的时间,这并不感激。 所以这就是为什么他们发明了alignment。 所以我们将字variables存储在字边界和字节variables中。

例如,如果我们有一个字节字段(B)和一个字段(W)(以及一个非常天真的编译器)的结构,我们得到以下结果:

 +----+ |0000| B |0001| W +----+ |0002| W |0003| +----+ 

哪个不好玩 但是当使用字alignment时,我们发现:

 +----+ |0000| B |0001| - +----+ |0002| W |0003| W +----+ 

这里的内存牺牲了访问速度。

你可以想象,当使用双字(4字节)或四字(8字节)时,这更重要。 这就是为什么大多数现代编译器可以在编译程序时select使用哪种alignment方式。

一些CPU体系结构需要各种数据types的特定alignment,如果不遵守这个规则,将会抛出exception。 在标准模式下,x86不需要基本数据types,但可能会受到性能损失(请查询www.agner.org获取低级优化提示)。

但是, SSE指令集(通常用于高性能)的audio/video处理有严格的alignment要求,如果您尝试在未alignment的数据上使用SSE指令集(除非在某些处理器上使用更慢的未alignment版本)。

你的问题可能是一个编译器期望调用者保持alignment的堆栈,而另一个期望被调用者在必要时alignment堆栈。

编辑 :至于为什么发生exception,DLL中的例程可能要使用一些临时堆栈数据上的SSE指令,并失败,因为两个不同的编译器不同意调用约定。

IIRC,堆栈alignment是当variables被放置在堆栈上“alignment”到特定数目的字节时。 所以,如果使用16位堆栈alignment方式,堆栈中的每个variables将从一个字节开始,该字节是函数中当前堆栈指针的2个字节的倍数。

这意味着如果使用<2个字节的variables,比如char(1个字节),那么在它和下一个variables之间会有8位未使用的“填充”。 这允许基于可变位置的假设进行某些优化。

当调用函数时,将parameter passing给下一个函数的一种方法是将它们放在堆栈上(而不是将它们直接放入寄存器中)。 不pipe是否使用alignment方式都很重要,因为调用函数将variables放在堆栈上,通过调用函数使用偏移量读取。 如果调用函数将variablesalignment,并且被调用的函数期望它们是非alignment的,则被调用的函数将无法find它们。

看来,msvc编译代码是不同意variablesalignment。 尝试编译与所有优化closures。

据我所知,编译器通常不会alignment堆栈中的variables。 该库可能取决于编译器不支持的某些编译器选项集。 正常的解决方法是将需要alignment的variables声明为静态的,但是如果在其他人的代码中这样做,则需要确保它们的variables稍后在函数中初始化,而不是在声明。

 // Some compilers won't align this as it's on the stack... int __declspec(align(32)) needsToBe32Aligned = 0; // Change to static int __declspec(align(32)) needsToBe32Aligned; needsToBe32Aligned = 0; 

或者,find一个编译器开关来alignment堆栈上的variables。 很明显,我在这里使用的“__declspec”align语法可能不是你的编译器使用的。