Cython生成的C 代码中可能的错误

Possible bug in Cython generated C++ code

本文关键字:错误 Cython 代码      更新时间:2023-10-16

我正在尝试为__gnu_parallelal ::与在此线程中以相同的方式为numpy阵列创建一个与他们在此线程中相同的方式的Cython包装器。

这是我简化的wrapparallel.pyx:

import cython
cimport cython 
cdef extern from "<parallel/algorithm>" namespace "__gnu_parallel":
    cdef void sort[T](T first, T last) nogil 
def parallel_sort(double[::1] a):
    sort(&a[0], &a[a.shape[0] - 1])

i用:

生成C 代码
cython --cplus wrapparallel.pyx

编译并链接:

g++ -g -march=native -Ofast -fpic -c wrapparallel.cpp -o wrapparallel.o -fopenmp -I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7
g++ -g -march=native -Ofast -shared -o wrapparallel.so wrapparallel.o -lpthread -ldl  -lutil -lm  -lpython2.7 -lgomp 

现在测试它:

In [1]: import numpy as np
        from wrapparallel import parallel_sort
        a = np.random.randn(10)
        parallel_sort(a)
        a
Out[1]: array([-1.23569683, -1.05639448, -0.76990205, -0.2512839 , -0.25022328,
                0.12711458,  0.81659571,  0.92205287,  2.15019125, -0.45902146])

正如原始线程在注释中指出的那样,该代码没有对最后一个元素进行排序,评论员建议在呼叫中删除" -1"以在Pyx -File中排序。但是,此更改将无法解决任何问题,因为[A.Shape [0]]将超出范围。

这使我怀疑C 代码中可能存在问题。实际调用__gnu_parallelal ::排序发生的片段看起来像这样:

static PyObject *__pyx_pf_12wrapparallel_parallel_sort(CYTHON_UNUSED PyObject *__pyx_self, __Pyx_memviewslice __pyx_v_a) {
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  Py_ssize_t __pyx_t_1;
  int __pyx_t_2;
  Py_ssize_t __pyx_t_3;
  int __pyx_lineno = 0;
  const char *__pyx_filename = NULL;
  int __pyx_clineno = 0;
  __Pyx_RefNannySetupContext("parallel_sort", 0);
  __pyx_t_1 = 0;
  __pyx_t_2 = -1;
  if (__pyx_t_1 < 0) {
    __pyx_t_1 += __pyx_v_a.shape[0];
    if (unlikely(__pyx_t_1 < 0)) __pyx_t_2 = 0;
  } else if (unlikely(__pyx_t_1 >= __pyx_v_a.shape[0])) __pyx_t_2 = 0;
  if (unlikely(__pyx_t_2 != -1)) {
    __Pyx_RaiseBufferIndexError(__pyx_t_2);
    {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __pyx_t_3 = ((__pyx_v_a.shape[0]) - 1);
  __pyx_t_2 = -1;
  if (__pyx_t_3 < 0) {
    __pyx_t_3 += __pyx_v_a.shape[0];
    if (unlikely(__pyx_t_3 < 0)) __pyx_t_2 = 0;
  } else if (unlikely(__pyx_t_3 >= __pyx_v_a.shape[0])) __pyx_t_2 = 0;
  if (unlikely(__pyx_t_2 != -1)) {
    __Pyx_RaiseBufferIndexError(__pyx_t_2);
    {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  }
  __gnu_parallel::sort<double *>((&(*((double *) ( /* dim=0 */ ((char *) (((double *) __pyx_v_a.data) + __pyx_t_1)) )))), (&(*((double *) ( /* dim=0 */ ((char *) (((double *) __pyx_v_a.data) + __pyx_t_3)) )))));

  /* function exit code */
  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
  goto __pyx_L0;
  __pyx_L1_error:;
  __Pyx_AddTraceback("wrapparallel.parallel_sort", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  __pyx_L0:;
  __PYX_XDEC_MEMVIEW(&__pyx_v_a, 1);
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

我对C 的了解还不足以掌握这里发生的事情,所以我的问题是:呼叫__gnu_parallelal :: Sort,我如何将其更改为也包括MemoryView中的最后一个元素?H2>编辑:

sort(&a[0], &a[a.shape[0] - 1])更改为sort(&a[0], &a[a.shape[0]])的答案是正确的。但是,除非指示CYTHON编译器使用boundscheck = False指令,否则这将增加IndexError: Out of bounds on buffer access (axis 0) 。为了完整性,wrapparallel.pyx文件应该看起来像:

# cython: boundscheck = False
import cython
cimport cython 
cdef extern from "<parallel/algorithm>" namespace "__gnu_parallel":
    cdef void sort[T](T first, T last) nogil 
def parallel_sort(double[::1] a):
    sort(&a[0], &a[a.shape[0]])

谁告诉你删除 -1是正确的。排序函数期望的参数类似于range(例如range(0, 3) <-> [0, 1, 2]

因此,您需要提供排序算法的第一个指针,而该指针不在您希望排序的数组中。给定以下数据:

addr | 0x00 | 0x01 | 0x02 | 0x03 |
-----+------+------+------+------+
elem | 3.12 | 5.89 | 0.56 |    - |

您将致电sort(addr, &addr[3])

您可以想象以类似的方式在数组中迭代的类别功能:

void func(double *start, double *end) {
    for (double *current = start; current != end; current += 1) {
        double value = *current;
        // do something
    }
}

请注意,当current指针等于end时,循环停止时,end指针将永远不会被删除(访问)。

编写&a[a.shape[0]]时,编译器足够聪明,可以弄清楚您只是尝试执行指针算术,并且实际上不会放弃无效的指针。