为__m256i分配内存

3

我遇到了一个神秘的分段错误。

#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),会出现Segmentation fault。

当我进行轻微更改:

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

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

有人能解释一下吗?


2
malloc不能保证分配的内存是32字节对齐的。 - Peter Cordes
1个回答

4

对齐、Malloc行为和堆栈分配

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

具体而言,在你的OSX机器上,你总是能够获得正确对齐的内存,这符合生成用于__m256i数据类型的汇编所需的对齐方式,该类型需要32字节对齐。(我用引号括起来的“总是”是因为这并不能用malloc保证。你可能只是在OSX的malloc中运气好。同一程序的多次运行往往会获得相同的对齐方式,而不像在一个程序中重复调用malloc。实际上,参见下文:OS X上的编译器生成不同的asm)

在Ubuntu上,malloc返回的内存地址没有适当的对齐方式。(有关原因详见下文)

你把第二段代码片段称作slight change

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

这段代码与使用malloc的第一个片段实际上有很大的不同,因为编译器(在此处为gcc)在在堆栈上分配内存时意识到了数据类型Box(以及其扩展的__m256i)的对齐要求。因此,在这种情况下没有崩溃的风险,因为编译器提供正确的对齐方式。

你可以像这篇帖子中所解释的那样操纵malloc返回的基指针https://dev59.com/pHVC5IYBdhLWcg3wpSzf#227900。我让您查看详细信息,但简单来说,您可以执行以下操作:

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(在同一硬件上的 VM 中)上也看到了 16 字节对齐,这是导致段错误的原因。当我手动对齐到 32 字节时,段错误消失了。

因此,你的问题的根本原因在于系统没有生成相同的汇编指令。我使用 gcc 的 -S 选项得到了汇编代码。在 OSX 上,我看到了使用 XMM 操作数的 vmovaps 4 次,而在 Ubuntu 上则使用 YMM 操作数两次的 vmovdqa 来移动一个 __m256i

vmovdqavmovaps 要求它们的内存操作数是自然对齐的(即 YMM 为 32B,XMM 为 16B)。因此,在 OSX 上生成的汇编只需要 16 字节对齐,尽管 __m256i 是 32 字节。


我非常确定这段代码在我的Mac上始终能够正常运行。看起来这与你的理论相矛盾。根据你的理论,它也应该会产生Segfault,因为它只提供了16字节对齐而不是32字节对齐。 - Ruiyu Zhu
这比那更加复杂。正如我在“指令差异”部分中所解释的那样,即使 C 数据类型 __m256i 需要 32 字节对齐,但您的 OSX 上的 gcc 生成的汇编指令只需要 16 字节 对齐。这就是为什么它在您的 Mac 上工作 - 生成的二进制文件只需要 16 字节对齐,而不是 32 字节对齐。Ubuntu 构建生成需要 32 字节对齐的指令。 - Phil
那解释了一切。谢谢! - Ruiyu Zhu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接