【问题标题】:python - using ctypes and SSE/AVX SOMETIMES segfaultspython - 使用 ctypes 和 SSE/AVX SOMETIMES segfaults
【发布时间】:2013-09-14 17:09:31
【问题描述】:

+ 我正在尝试使用 AVX 优化一段 python 代码。我正在使用 ctypes 来访问 C++ 函数。有时功能段错误,有时没有。我认为这可能与对齐方式有关? 也许任何人都可以帮助我,我有点卡在这里。

Python-代码:

from ctypes import *
import numpy as np
#path_cnt
path_cnt = 16
c_path_cnt = c_int(path_cnt)

#ndarray1
ndarray1      = np.ones(path_cnt,dtype=np.float32,order='C')
ndarray1.setflags(align=1,write=1)
c_ndarray1     = stock.ctypes.data_as(POINTER(c_float))

#ndarray2
ndarray2    = np.ones(path_cnt,dtype=np.float32,order='C');
ndarray2.setflags(align=1,write=1)
c_ndarray2  = max_vola.ctypes.data_as(POINTER(c_float))

#call function
finance = cdll.LoadLibrary(".../libfin.so")
finance.foobar.argtypes = [c_void_p, c_void_p,c_int]
finance.foobar(c_ndarray1,c_ndarray2,c_path_cnt)
x=0
while x < path_cnt:   
    print  c_stock[x]
    x+=1

C++ 代码

extern "C"{
int foobar(float * ndarray1,float * ndarray2,int path_cnt)
 {
     for(int i=0;i<path_cnt;i=i+8)
     {
         __m256 arr1                = _mm256_load_ps(&ndarray1[i]);
         __m256 arr2                    = _mm256_load_ps(&ndarray2[i]);
         __m256 add                 = _mm256_add_ps(arr1,arr2);
         _mm256_store_ps(&ndarray1[i],add);
     }
     return 0;
 }
}

现在奇怪的输出行为,在终端中进行两次调用会产生不同的结果!

tobias@tobias-Lenovo-U310:~/workspace/finance$ python finance.py 
Segmentation fault (core dumped)
tobias@tobias-Lenovo-U310:~/workspace/finance$ python finance.py 
2.0
2.0
2.0
2.0
2.0
2.0
2.0
2.0

提前致谢!

【问题讨论】:

  • 我没有看到 CPUID 检查。我知道您可能确定存在 AVX,但您仍应检查。

标签: python segmentation-fault ctypes sse avx


【解决方案1】:

有对齐和未对齐的加载指令。如果您违反对齐规则,对齐的会出错,但它们更快。未对齐的接受任何地址并在内部进行加载/移位以获取您想要的数据。您正在使用对齐版本_mm256_load_ps,并且可以直接切换到未对齐版本_mm256_loadu_ps,无需任何中间分配。

一个好的向量化编译器将包括一个引入循环来到达一个对齐的地址,然后是一个用于处理对齐数据的主体,然后是一个最终循环来清理任何落后者。

【讨论】:

  • 好答案。一些注意事项:对于最近的(Nehalem 及之后的)英特尔 x86 架构,128 位未对齐负载的运行速度与对齐负载相同。对齐加载指令 (MOVPS) 相对于其未对齐对应指令 (MOVUPS) 的一个小优势是对齐指令的编码短一个字节。在某些对指令缓存大小敏感的情况下,这可能会产生小的性能改进。然而,在现代架构中,256 位访问对对齐更敏感(尽管这可能在 Haswell 中有所改进)。
  • 我最初的问题仍然存在,为什么会出现分段错误以及是否有可能在 python 端强制对齐!我已经启用了numpy的对齐选项
  • @toebs:就像我说的,_mmXXX_load_XXX 指令需要对齐,如果您传递未对齐的地址,则会出错(导致您的进程接收SIGSEGV 并崩溃)。我不知道你是否可以强制 Python 提供这种对齐方式,但我对此表示怀疑。
【解决方案2】:

好吧,我觉得我找到了一个 sultion,它不是很优雅,但至少它有效! 应该是更好的方法,有人有什么建议吗?

extern "C"{
int foobar(float * ndarray1,float * ndarray2,int path_cnt)
 {
     float * test = (float*)_mm_malloc(path_cnt*sizeof(float),32);
     float * test2 = (float*)_mm_malloc(path_cnt*sizeof(float),32);
     //copy to aligned memory(this part is kinda stupid)
     for(int i=0;i<path_cnt;i++)
     {
        test[i] = stock[i];
        test2[i] = max_vola[i];

     }
     for(int i=0;i<path_cnt;i=i+8)
     {
         __m256 arr1                = _mm256_load_ps(&test1[i]);
         __m256 arr2                    = _mm256_load_ps(&test2[i]);
         __m256 add                 = _mm256_add_ps(arr1,arr2);
         _mm256_store_ps(&test1[i],add);
     }
  //and copy everything back!
   for(int i=0;i<path_cnt;i++)
    {
    stock[i] = test[i];   
    }
     return 0;
 }
}

【讨论】:

  • 如果它确实适用于未对齐的指令版本,那么在进行操作之前只复制 32 个数据元素似乎会产生很多开销。
猜你喜欢
  • 1970-01-01
  • 2013-03-21
  • 2014-06-04
  • 1970-01-01
  • 2016-10-29
  • 2017-07-25
  • 2023-03-11
  • 2014-01-04
  • 1970-01-01
相关资源
最近更新 更多