代码之家  ›  专栏  ›  技术社区  ›  Andrej Kesely

在Cython中使用C联合和SSE内旋导致sigsegv。

  •  3
  • Andrej Kesely  · 技术社区  · 6 年前

    typedef union my_mat4
    {
        float Elements[4][4];
    #ifdef MAT4_MATH__USE_SSE
        __m128 Rows[4];
    #endif
    } my_mat4;
    
    static inline my_mat4 init_my_mat4(void)
    {
        my_mat4 Result = {0};
        return (Result);
    }
    

    我将此代码用mat4.pxd包装,如下所示:

    cdef extern from "mat4.h":
        ctypedef union my_mat4:
            float Elements[4][4]
    
        my_mat4 init_my_mat4()
    
    cdef class MyClass:
        cdef my_mat4 m
        cdef object o
    

    cdef class MyClass:
        def __cinit__(self):
            self.m = init_my_mat4()
    

    my_class = MyClass()
    

    python退出并显示消息

    但是,当我禁用 从C联合中,程序正常运行。即使我搬走了 从我的课上,一切正常。

    谢谢,

    只有当模块 导入并在Cython扩展模块中编译uuM128,因此:

    import decimal
    my_class = MyClass()
    

    编辑2:

    正如@petercordes所写,几乎可以肯定这是错位问题。解决方法是移除 __m128 load_vec() 或类似的宏 _mm_loadu_ps

    编辑4:

    unaligned__m128 定义为 typedef float __attribute((vector_size(16), aligned(4))) unaligned__m128;

    1 回复  |  直到 6 年前
        1
  •  1
  •   Peter Cordes    6 年前

    几乎可以肯定。

    __m128 movaps 加载/存储它,或将它用作其他SSE指令的内存操作数(如 addps xmm0, [mem] 16字节对齐。

    float Elements[4][4] union 对象,这些对象违反了联合对其最对齐的成员进行足够对齐的要求。


    gcc -O3 -march=native _ M128型 vmovaps vmovups

    现代硬件具有高效的未对齐负载支持,但缓存线拆分仍然不理想。指令计数也更糟,因为对于avx,编译器必须使用单独的 movups addps xmm0,[内存]

    在C中,移除 ,并使用 _mm_loadu_ps() 进行未对齐的加载。

    typedef struct my_mat4 { float Elements[4][4]; } my_mat4;
    
    static inline
    __m128 load_vec(const struct my_mat4 *m4, size_t idx) {
        _mm_loadu_ps(&m4->Elements[idx][0]);
    }
    

    使用GNU C:用未对齐的版本重新定义联合 _ M128型

    让python对齐对象是最有效的方法,但如果不这样做,则只需对对象进行一次更改即可编译现有代码:

    _ M128型 根据GNU C本机向量定义 in xmmintrin.h#69 .(支持GNU扩展的其他编译器是兼容的,至少clang是兼容的。)

    typedef float __m128 attribute ((vector_size (16), may_alias));
    

    标题 已经 定义未对齐的 __m128_u 它还使用 aligned(1) .我们可以用 aligned(4) 以确保它至少与 float 边界,以防万一。

    这仅仅是因为相同向量类型的不同对齐版本是可以自由转换的,所以将其传递给intrinsics的代码编译时不会发出警告(即使在 -Wall )。

    typedef float __attribute((vector_size(16), aligned(4))) unaligned__m128;
    // I left out may_alias, only matters if you're using unaligned__m128* to load from non-float data.
    // Probably doesn't hurt code-gen if you aren't using unaligned__m128* at all, just objects
    
       //#define __m128 unaligned__m128   // not needed
    
    typedef union my_mat4 {
         float Elements[4][4];
         unaligned__m128 Rows[4];
    } my_mat4;
    

    使用这种类型的函数编译得很好( gcc8.1 on the Godbolt compiler explorer )。(你也可以写 m4->Rows[1] + m4->Rows[2] ,即使在C不是C++中,因为GNU C本地向量将C操作符映射到每个元素操作。

    __m128 use_row(union my_mat4 *m4) {
        __m128 tmp = _mm_add_ps(m4->Rows[1], m4->Rows[2]);
        m4->Rows[3] = tmp;
        return tmp;
    }
    

    -O3 (不-三月),我们得到

        movups  xmm0, XMMWORD PTR [rdi+32]    # unaligned loads
        movups  xmm1, XMMWORD PTR [rdi+16]
        addps   xmm0, xmm1
        movups  XMMWORD PTR [rdi+48], xmm0    # unaligned store
        ret
    

    -mavx -march=haswell

    use_row(my_mat4*):
        vmovups xmm1, XMMWORD PTR [rdi+32]
        vaddps  xmm0, xmm1, XMMWORD PTR [rdi+16]    # unaligned memory source is ok for AVX
        vmovups XMMWORD PTR [rdi+48], xmm0
        ret
    

    How to remove "noise" from GCC/clang assembly output? )。


    MAT4_MATH__USE_SSE struct {int foo; my_mat4 m4; }; my_mat4

    如果您已经解决了让Python对齐对象的问题

    #include <stdalign.h>
    
    // give the same alignment regardless of whether the macro is defined.
    typedef union my_mat4
    {
        alignas(16) float Elements[4][4];
    #ifdef MAT4_MATH__USE_SSE
        __m128 Rows[4];
    #endif
    } my_mat4;
    

    如果不希望在宏未定义时保证对齐,则不执行此操作。