【问题标题】:Compiler constant indicating memory alignment requirement指示内存对齐要求的编译器常量
【发布时间】:2015-04-12 03:44:45
【问题描述】:

一些 CPU 架构(x86 除外)非常不喜欢在未对齐的地址读取和写入多字节数,以至于在检测到这一点时会引发 SIGBUS 异常,并迫使程序员按字节顺序手动执行所有操作。虽然对于需要此操作的平台可能无能为力,但在允许未对齐访问的平台(例如 x86)上检查对齐并执行字节操作将是愚蠢的。问题是:C/C++ 编译器是否定义了一个指示对齐要求的常量

目前,我正在使用这个:

#if defined(_M_IX86) | defined(__i386) | defined(__i386__) | defined(i386) | defined(_X86_)
    // Unaligned access is allowed.
#elif defined(_M_X64) | defined(__x86_64__) | defined(__x86_64) | defined(__amd64) | defined(__amd64__) | defined(_M_AMD64)
    // Unaligned access is allowed.
#else
    #define ALIGNED_ACCESS_ONLY
#endif

但它看起来太“自制”了:它并没有说明当前硬件平台的实际属性,而只是描述了我自己对 x86-32 和 x86-64 以及这些平台最流行的常量名称的考虑。

【问题讨论】:

  • 显而易见的解决方案是在尝试未对齐访问时捕获SIGBUS...
  • @black 我想这将是 heavy:即使捕获硬件异常很快(我强烈怀疑),无论如何安装处理程序,捕获错误并删除处理程序永远不会像简单地检查地址对齐然后相应地分支一样快。在我的情况下,非对齐访问是主要的操作模式,而不是罕见的情况。
  • 确实如此;我只是为此提出了“穷人解决方案”。您能否告诉我们您只是想知道这一点还是您正在尝试解决特定问题XY
  • @black 除了从缓冲区读取/写入数字和其他原始对象之外,绝对没有隐藏任务,可能会根据需要即时更改字节顺序。
  • "在允许非对齐访问的平台(例如 x86)上检查对齐并执行字节操作会很愚蠢" 真的很愚蠢吗?我知道 x86 允许未对齐的访问,但我认为当您实际执行未对齐的访问时会付出巨大的性能损失,因此避免它们并不一定看起来很愚蠢。如果这些值经常不对齐,那么逐字节解决方案将是可移植的,并且性能可能不会更差。

标签: c portability platform memory-alignment


【解决方案1】:

从技术上讲,我并没有回答你提出的问题,但我提出了一种解决方法,以防你的问题的答案是否定的。

如果您的代码是在配置脚本的帮助下编译的,您可以测试是否需要对齐访问。 GNU autoconf 有一个功能可以做到这一点:

http://www.gnu.org/software/autoconf-archive/ax_check_aligned_access_required.html

基本上就是编译下面这个小程序,运行一下,看看结果:

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

int main()
{
  char* string = malloc(40);
  int i;
  for (i=0; i < 40; i++)
  {
    string[[i]] = i;
  }

  {
     void* s = string;
     int* p = s+1;
     int* q = s+2;

     if (*p == *q) { return 1; }
  }
  return 0;
}

【讨论】:

  • 如果你 freed 记忆会很可爱,但加一个。
  • 为了清楚起见,代码来自 GNU autoconf,我只添加了一些空格和括号以明确 for 循环内的内容。我想你可以给他们发一个补丁。
  • DavidGrayson:有什么特别的原因他们不只是分配堆栈string?还有@Bathsheba:我知道有些人会因为我这么说而讨厌我,但 GNU 从来没有以代码质量着称。
  • 我想在某些架构上,从堆栈中读取数据的指令可能是特殊的并且有不同的对齐要求。不过这只是猜测。
  • @Bathsheba,既然可以退出,为什么还要浪费时间free()-ing?!无论哪种方式,内核都会取回内存。
【解决方案2】:

我在memcpy 的实现中看到了一种不需要任何检查的解决方案。基本上,您开始逐字节复制数据字节,直到到达所需对齐的倍数的地址。
之后,您可以开始复制字大小的数据块,并享受对齐地址带来的所有好处(循环展开、矢量化......)。

不过,您可以通过大数据块充分利用它。

显然,clanggcc 都没有定义任何宏来通知未对齐的访问。 (gcc/clang -E -dM - &lt; /dev/null -march=native)。
您可能需要考虑的一些想法:

  • 首先减少需求:使用指针时会出现问题。尽量避免这种情况,重构您处理数据的方式。
  • asm:编写依赖于平台的 asm 来加载/存储到/从未对齐的访问,尽管它高度依赖于您正在使用的平台。
  • SSE 允许非对齐访问。

【讨论】:

  • 我一次读/写一个数字,即使某些数字偶尔被分组到数组中(即使在这种情况下,它们大部分时间都是未对齐的)。
  • @AntonSamsonov 添加了一些其他选项。但是,如果没有可用的真实代码,仍然很难提取可能的解决方案。
【解决方案3】:

OP:C/C++ 编译器是否定义了一个指示对齐要求的常量?
是的。 max_align_t 是最大对齐要求的类型。

如果是sizeof(max_align_t) &gt; 1,则需要进行一些对齐。

C11之前的请看Compiler constant indicating memory alignment requirement

【讨论】:

  • 这没有回答他的问题。他问的是是否允许未对齐的访问,而不是最大对齐是多少。例如,x86 通常具有816 的最大对齐,但仍然允许以1 对齐的访问(它们只是更慢);另一方面,MIPS 和 ARM 会引发未对齐访问的错误。
  • @ Tim Čas OP 用粗体询问“C/C++ 编译器是否定义了一个指示对齐要求的常量?”。简单检查 sizeof (max_align_t) == 1 是否会指示是否需要任何对齐。
  • 它会指示对象是否应该对齐,但没有no指示是否支持未对齐的访问。在 x86 中,您应该对齐,但您不必。在 ARM 或 MIPS 中,您必须对齐(或遭受SIGBUS 错误)。 OP 询问是否有办法检查您是否必须对齐,而不是您是否应该
  • 这种误解可能源于“要求”一词的歧义:在 x86 的情况下,它不是“要求”(硬授权),而仅仅是“建议”(软建议)。
  • @Tim Čas OP 询问“C/C++ 编译器是否定义了一个指示对齐要求的常量?”。 C11类型max_align_t的sizeof是最小对齐要求。
猜你喜欢
  • 2011-02-10
  • 1970-01-01
  • 2018-01-09
  • 2017-08-19
  • 2014-12-24
  • 2012-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多