利用和优化 SIMD 在 cython 中进行矩阵轴循环

问题描述 投票:0回答:1

以下 cython 3.0.8 代码正在带有 -O3 编译标志的支持 AVX2 的机器上编译和运行,但在从下面的 python main 使用时不会使用任何 SIMD 指令。

import numpy as np
from libc.math cimport fabs

cpdef inline double[:,:] go(double[:] stream, double[:] query):
    matrix = np.empty((len(stream), len(query)))
    cdef double [:, :] matrix_c = matrix

    stream = np.asarray(stream)
    query = np.asarray(query)

    cdef int i, j
    cdef int stream_len = len(stream)
    for i in range(stream_len):
        for j in range(len(query)):
            matrix_c[i, j] = fabs(stream[i] - query[j])

    return matrix

使用

perf
命令运行 python main,当
i
j
范围分别超过 100000 和 20 时,对 SIMD 指令的调用少于 10 次:

import timeit
import numpy as np
import go

if __name__ == '__main__':
    np.random.seed(42)
    stream = np.random.rand(100000)
    query = np.linspace(0, 1, 20)
    print(f'mean runtime is {np.mean(timeit.repeat(lambda: go(stream, query), number=10, repeat=100))}')

您对如何修改它以允许底层编译器 (x86_64-linux-gnu-gcc) 利用 SIMD 向量运算有什么建议吗?

为了检查是否正在使用 SIMD,我使用以下命令,但如果您推荐一种方法,我很乐意使用不同的方法:

 perf stat -e instructions:u,fp_arith_inst_retired.128b_packed_single:u,fp_arith_inst_retired.256b_packed_single:u,fp_arith_inst_retired.128b_packed_double:u,fp_arith_inst_retired.256b_packed_double:u -- python -m main.py

确实 perf 显示了 150 万次 SIMD 使用...


这是 cython 3.0.x,因此某些语法对某些人来说可能看起来很新。我正在 Ubuntu 上编译和运行,这是我第一次使用 cython 以及寻求显式强制使用 SIMD。尝试

-march=native
没有什么区别。


我附上了 cython 构建期间进行的 gcc 调用:

x86_64-linux-gnu-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=格式安全 -g -fwrapv -O2 -fPIC -I/.venv3.10/include -I/usr/include/python3.10 -c ....c -o build/temp.linux-x86_64-cpython-310/....o - O3 -三月=本地人

x86_64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -g -fwrapv -O2 build/temp.linux-x86_64-cpython-310/。 ...o -L/usr/lib/x86_64-linux-gnu -o build/lib.linux-x86_64-cpython-310/....cpython-310-x86_64-linux-gnu.so -O3 -march=本地人


使用

-fopt-info
标志确实会在现在更长的构建输出(数千行)中产生 24 个循环实例,但是它们与我很难将其与我自己的代码逻辑关联起来的代码行相关,例如cython 处理其内存视图的代码行。输出线如:

main.c:14454:3:优化:使用 32 字节向量循环向量化

我想如果我自己用 C 语言编写这个,我可以更直接地将优化消息与我的代码关联起来。

更新

双重矢量化已经发生:

所以只剩下看看代码能否进一步优化了。

cython simd avx auto-vectorization
1个回答
0
投票

好吧,毕竟是用SIMD,我只是疏忽了选择让perf命令监控哪些指令。由于我的代码使用双精度,这是演示 SIMD 数量的正确 perf 命令:

 perf stat -e instructions:u,fp_arith_inst_retired.128b_packed_single:u,fp_arith_inst_retired.256b_packed_single:u,fp_arith_inst_retired.128b_packed_double:u,fp_arith_inst_retired.256b_packed_double:u -- python -m main.py

当然,在这种特定情况下,监视

*_single
指令是多余的,但在其他情况下可能会派上用场(当涉及非双浮点类型时)。

我应该以某种方式追踪构建的汇编代码实际使用的策略,以了解它的效率,或者更具对比性地关闭该优化,以比较它对平均运行时间的加速程度。

感谢@PeterCordes 对此提供的所有帮助。

事实上,我不能完全排除不能通过更好的 cython 或使 cython 在 numpy 数组输入周围跳过更少的环的方式进一步优化(在单核上)。

© www.soinside.com 2019 - 2024. All rights reserved.