【问题标题】:c SIMD - how do I do memory alignment properly in this case?c SIMD - 在这种情况下,我该如何正确地进行内存对齐?
【发布时间】:2021-10-31 20:34:31
【问题描述】:

我在一个测试程序中有以下两个结构:

typedef struct fltptr {
    size_t size;
    float *a;
    float *b;
} fptr;

struct fltWrap256 {
    __m256 *m256;
    __m128 *m128;
    float  *m32;
};

typedef struct fltwrap {
    struct fltWrap256 a;
    struct fltWrap256 b;
} fwrap;

“fwrap”的重点是提供一种“SIMD”方式来访问“fptr”实例的相等但大小可变的浮点“数组”。 SIMD_point_to( fptr* , fwrap* ) 函数将适当地指向包装器实例的成员指针:

void SIMD_point_to( fptr *v, fwrap *S )
{
    S->a.m256 = (__m256 *)(v->a);
    S->b.m256 = (__m256 *)(v->b);

    uint8_t rem = v->size % 8;
    size_t offset = v->size - rem;
    S->a.m128 = (__m128 *)(v->a + offset);
    S->b.m128 = (__m128 *)(v->b + offset);

    rem %= 4;
    offset = v->size - rem;
    S->a.m32 = v->a + offset;
    S->b.m32 = v->b + offset;
}

视觉示例:

let fptr.a/b = [XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX]
           m256*^(8x3)        (4x1)m128*^   ^m32*(1x3)

let fptr.a/b = [XXXXX]
      (8x0)m256*^   ^m32*(1x1)
      (4x1)m128*^

但是因为 SIMD 需要 16 字节对齐,所以在分配 fptr.x 时需要使用 aligned_alloc( )。至少这是我认为我需要做的。

int main( )
{
    fptr test;
    //allocated size explicitly a multiple of 16(4x24=96) as required. Working size will be 23 floats.
    test.a = aligned_alloc(16, 16, sizeof(float)*24 );
    test.b = aligned_alloc(16, 16, sizeof(float)*24 );
    test.size = 23;

    /* Filling test.a/b */
    float A[23] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f, 20.0f, 21.0f, 22.0f, 23.0f };

    float B[23] = { 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f, 110.0f, 111.0f, 112.0f, 113.0f, 114.0f, 115.0f, 116.0f, 117.0f, 118.0f, 119.0f, 120.0f, 121.0f, 122.0f, 123.0f };

    memcpy( test.a, A, sizeof(float)*23 );
    memcpy( test.b, B, sizeof(float)*23 );

    fwrap wrap;
    SIMD_point_to( &test, &wrap );

    float __attribute__(( aligned(16) )) out[8];
    /*
    __m256 mout = _mm256_add_ps( wrap.a.m256[0], wrap.b.m256[0] ); //Seg Fault here
    __m256_store_ps( out, mout );
    */
    /*__m256_store_ps( out, wrap.a.m256[1] );*/ //Another here

    __m256_storeu_ps( out, wrap.b.m256[1] ); //THIS WORKS!

    for( int i = 0; i < 8; i++ )
        printf("%f\n", out[i]);
    /*
    out[] contains the second set of 8 floats pointed to by    'wrap.b.m256'.
    */
}

storeu 不会抛出错误,但这只是意味着我做错了什么。有什么建议吗?

编辑:有趣的是,使用 '-O3' (gcc) 进行编译可以解决 store 的段错误,但不能解决 _mm256_add_ps

【问题讨论】:

  • 编辑问题以提供minimal reproducible example。显示重现问题的完整代码;不要只是描述它。
  • 请粘贴SIMD_point_to
  • 我的错,请参阅更新的操作。 @tstanisl
  • 传递 3 个指针的结构似乎比指针 + 长度更糟糕,并且当您真正想要循环它时进行动态计算,除非您只将这些用于本地变量或用于内联。
  • 在 GCC 命令行中添加 -fno-strict-aliasing 标志是否有效?

标签: c pointers malloc simd memory-alignment


【解决方案1】:

来自man aligned_alloc

aligned_alloc() 函数与 memalign() 相同,只是增加了 size 应为对齐倍数的限制。

我猜aligned_alloc 会返回NULL,因为sizeof(float)*23 不能被16 整除。这会在使用指针时导致段错误。

只需将 sizeof(float)*23 舍入到 16 的下一个倍数。

一种方法是:

(sizeof(float)*23 + 16 - 1) / 16 * 16

其他问题是float out[8]; 的正确对齐。 要强制对齐与 __m256 兼容,可以使用 C11 中的 _Alignas 关键字。

_Alignas (__m256) float out[8];

编辑

正如评论中所建议的,对齐要求可以写成更漂亮的形式:

#include <stdalign.h>
...
alignas (__m256) float out[8];

【讨论】:

  • 啊,我忽略了在操作中提到这并不能解决问题。将编辑。
  • aligned_alloc 也不返回 NULL,当我避免对“storeu”负责时,out[] 将包含 fptr.a 的前 8 个浮点数。问题似乎在于对齐。
  • 这将避免出现问题,所以我已经相应地编辑了我的代码/问题,谢谢,但它在 '_mm256_add_ps' 抛出了一个段错误,它没有触及 []。
  • 您也可以#include &lt;stdalign.h&gt; 允许alignas(作为C 中_Alignas 的#define),使您的代码易于移植到C++。
【解决方案2】:

为什么你只尝试通过16 对齐? __m256 需要 32 字节对齐

【讨论】:

  • 32 字节?环顾四周后似乎是这种情况。我不知道并假设所有 SIMD 寄存器都需要 16 字节。现在可以了!
猜你喜欢
  • 1970-01-01
  • 2014-05-10
  • 1970-01-01
  • 1970-01-01
  • 2022-12-04
  • 1970-01-01
  • 1970-01-01
  • 2021-09-27
  • 1970-01-01
相关资源
最近更新 更多