如何正确地将 numpy 数组传递给 Cython 函数?

Posted

技术标签:

【中文标题】如何正确地将 numpy 数组传递给 Cython 函数?【英文标题】:how to pass numpy array to Cython function correctly? 【发布时间】:2014-02-26 22:48:33 【问题描述】:

这在很多地方都有描述,但我根本无法让它工作。我正在从 Cython 调用 C++ 函数:

cimport numpy as np
cdef extern from "test.h" namespace "mytest":
   void test(double *A, int m)

cdef int foo():
  cdef np.ndarray[double,mode="c"] a = np.array([1,2,3,4,5],dtype=float)
  # pass ptr to first element of 'a'
  test(&a[0], len(a))
  return 0

foo()

test.cpp 只是:

#include <stdio.h>
namespace mytest 
    void test(double *A, int m)
    
    for (int i = 0; i < m; i++)
    
        printf("%d is %f\n", i, A[i]);
    
    

test.h 只有:

namespace mytest 
  void test(double *A, int m);

这似乎可行,但什么时候需要np.ascontiguousarray?这样做就够了吗:

cdef np.ndarray[double,mode="c"] a = np.array([1,2,3,4,5],dtype=float)

或者你需要:

cdef np.ndarray[double,mode="c"] a = np.ascontiguousarray(np.array([1,2,3,4,5],dtype=float))

其次,更重要的是,这如何推广到二维数组?

处理二维数组

这是我将 2d numpy 数组传递给 C++ 的尝试,但它不起作用:

cdef np.ndarray[double,mode="c",ndim=2] a = np.array([[1,2],[3,4]],dtype=float)

称为:

test(&a[0,0], a.shape[0], a.shape[1])

在cpp代码中:

void test(double *A, int m, int n) 
 
  printf("reference 0,0 element\n");
  printf("%f\n", A[0][0]);

更新:正确答案

正确的答案是对数组使用线性索引,而不是[][] 语法。打印二维数组的正确方法是:

for (int i = 0; i < m; i++)

    for (int j = 0; j < n; j++)
    
    printf("%d, %d is %f\n", i, j, A[i*m + j]);
    

【问题讨论】:

相关:Best Practices for passing numpy data pointer to C @J.F.Sebastian:谢谢你,我一直在阅读那个帖子,但这让我更加困惑。我基本上是在尝试“&arr[0]”方法,因为它对我来说最有意义,但我还没有看到任何有效的例子。 (我不想使用 ctypes) 在您的二维示例中,在我看来,您正在取消引用指针 A 两次。对于 2D 数组,您可能必须手动进行索引运算。例如,如果您有一个 C 连续 m x n 数组,并且您想要执行 NumPy 的 A[i,j]' you would have to do A[m*i+j]` 的 C 等效项,而不是 A[0][0]。两次取消引用指针可能会使 Python 崩溃。 m 是行数。当您指定指针的类型时,会注意数据类型。我将举一个简单的例子。 @user248237dfsf:我很喜欢线程中的建议,使用typed memoryviews (&amp;s[0] syntax to pass to C)。 【参考方案1】:

对于二维数组,您只需要 ndim 关键字:

cdef np.ndarray[double, mode="c", ndim=2]

结果可能会也可能不会与原始数据共享内存。如果它与原始共享内存,则该数组可能不连续,或者可能具有不寻常的跨步配置。在这种情况下,直接将缓冲区传递给 C/C++ 将是灾难性的。

您应该始终使用ascontiguousarray,除非您的 C/C++ 代码准备好处理非连续数据(在这种情况下,您需要将所有相关的步幅数据从 Cython 传递到C 函数)。如果输入数组已经是连续的,则不会进行复制。确保将兼容的dtype 传递给ascontiguousarray,以免冒第二个副本的风险(例如,必须从连续的float 数组转换为连续的double 数组)。

【讨论】:

@nneoneo:我有 ndim,但在原帖中有错字,抱歉。添加 ndim=2 是创建数组所必需的,但我仍然不知道如何从 C 访问它。如何在 C 端访问它?你能举个例子吗? 如果您添加了ascontiguousarray,它似乎应该可以工作;什么似乎不起作用? @nneoneo:尝试将二维数组传递给 C++ 函数并使用 A[i][j] 打印其元素 - 它不起作用。双索引只会产生垃圾。你明白我的意思吗? 哦,我明白了。除非你有一个double **(一个指针数组)或一个double [][](一个声明的二维数组),否则你不能在 C 中像这样的双索引。否则,只有double *,您必须手动索引:A[i*n + j] 在 C 中,你必须使用一维索引,是的。这在处理连续数据的缓冲区时很典型。 (在多维 C 数组上,例如 double [][],C 编译器本质上是通过将数组维度乘以适当的索引来生成等效的一维索引操作)。

以上是关于如何正确地将 numpy 数组传递给 Cython 函数?的主要内容,如果未能解决你的问题,请参考以下文章

将带有字符串的结构化 numpy 数组传递给 cython 函数

将 3D numpy 数组从 cython 传递到 C++

如何正确地将 C 数组传递给 C#?

在 C# 中如何正确地将 int 数组传递给函数

Cython 中 numpy 数组掩码的性能

将 numpy 数组传递给 C++