【问题标题】:Allocating memory for __m256i [duplicate]为 __m256i 分配内存 [重复]
【发布时间】:2017-02-20 18:02:40
【问题描述】:

我遇到过这么神秘的分段错误。

#include <stdio.h>
#include <immintrin.h>
struct Box{
    __m256i L;
};
int main()
{
    struct Box *result=NULL;
    result=(struct Box *)malloc(sizeof(struct Box));
    (*result).L=(*result).L;
}

使用标志-msse4.2 -march=corei7-avx编译

它在我的 Mac 上运行良好(OS X EI Caption 10.11.6,GCC 4.8.4)。 但它给了我 Amazon EC2 机器上的分段错误(Ubuntu 14.04,GCC 4.8.4)。

当我稍作改动时:

#include <stdio.h>
#include <immintrin.h>
struct Box{
    __m256i L;
};
int main()
{
    struct Box result[1];
    (*result).L=(*result).L;
}

它将能够在 Ubuntu 机器上运行。

有人对此有任何解释吗?

【问题讨论】:

  • malloc 不保证 32B 对齐的内存。

标签: c ubuntu gcc x86 avx


【解决方案1】:

对齐、Malloc 行为和堆栈分配

@Peter Cordes 的评论解决了部分问题。

特别是在您的 OSX 机器上,您“总是”获得正确对齐的内存,以满足为 __m256i 数据类型(即 supposed to be 32 byte aligned)生成的程序集的对齐需求。 (我把“总是”放在引号中,因为 malloc 不能保证它。你可能刚刚对 OSX 的 malloc 感到幸运。多次运行相同的代码往往会得到相同的对齐,这与重复调用 @987654328 不同@在一个程序中。实际上如下:OS X上的编译器生成不同的asm)

在 Ubuntu 上,malloc 返回的内存地址没有得到合适的对齐。 (有关原因的详细信息,请参见下文。)

您将第二个代码 sn-p 称为slight change

int main()
{
    struct Box result[1];
    (*result).L=(*result).L;
}

它实际上与第一个使用malloc 的sn-p 有很大不同,因为编译器(此处为gcc)知道数据类型Box 的对齐要求(以及扩展__m256i)在堆栈上分配内存时。因此在这种情况下没有段错误的风险,因为编译器提供了正确的对齐。

您可以操作malloc 返回的基指针,如本文https://stackoverflow.com/a/227900/3516034 中所述。我会让你看看那里的细节,但总之你可以做类似的事情

struct Box *result=NULL;
void *mem = malloc(2 * sizeof(struct Box));
result = (struct Box *)((uintptr_t)mem + offset);

offset 允许您探索对齐和段错误。打印出您最终用于result 的指针地址可能对您有用,例如printf("0x%08" PRIXPTR "\n", (uintptr_t)result);(同样来自该帖子)。

指令差异

最后,我可以在 Ubuntu 和 OSX 上重现它。我实际上看到我的 OSX malloc 调用给出 16 字节 对齐而不是 32 字节对齐。我在 Ubuntu 上看到了 16 字节对齐(在同一硬件上的 VM 中),这 导致了段错误。当我手动对齐到 32 个字节时,段错误消失了。

所以问题的根本原因是系统没有生成相同的汇编指令。我得到了带有-S 选项的程序集到 gcc。在 OSX 上,我看到 vmovaps 与 XMM 操作数一起使用了 4 次,在 Ubuntu 上 vmovdqa 与 YMM 操作数一起使用了两次,以移动 __m256i

vmovdqavmovaps 要求它们的内存操作数自然对齐(即 YMM 为 32B,XMM 为 16B)。所以在 OSX 上生成的程序集只需要 16B 对齐,即使 __m256i 是 32B。

【讨论】:

  • 我很确定代码总是在我的 Mac 上正常运行。看起来这与你的理论相矛盾。根据您的理论,它也应该给出 Segfault,因为它给出 16 字节对齐而不是 32 字节对齐。
  • 比这更复杂。正如我在“指令差异”部分中解释的那样,OSX 上的 gcc 正在生成只需要 16 字节 对齐的汇编指令,即使 C 数据类型 __m256i 需要 32 字节对齐。这就是它在您的 Mac 上工作的原因 - 生成的二进制文件只需要 16 字节对齐,而不是 32 字节。 Ubuntu 构建生成需要 32 字节对齐的指令。
  • 这就解释了一切。谢谢!
猜你喜欢
  • 1970-01-01
  • 2016-10-08
  • 2012-12-31
  • 2012-12-23
  • 1970-01-01
  • 1970-01-01
  • 2011-06-23
  • 2018-06-17
  • 2021-12-08
相关资源
最近更新 更多