【发布时间】:2016-07-09 13:44:41
【问题描述】:
我编写了一个函数,它使用 CFFI 将 numpy 数组传递到 C 代码中。它利用缓冲区协议和 memoryview 有效地传递数据而不复制它。但是,这意味着您需要传递 C 连续数组并确保您使用正确的类型。 Numpy 提供了一个函数 numpy.ascontiguous, 来执行此操作。所以我遍历参数,并应用这个函数。下面的实现是有效的,并且可能是普遍感兴趣的。但是,考虑到它被调用的次数,它很慢。 (任何关于如何加快速度的通用 cmets 都会有所帮助。)
然而,实际的问题是当你用生成器推导替换第一个列表推导时,或者如果你重构代码以便在第二个推导中调用 np.ascontigous,传递给 C 代码的指针不再指向numpy 数组的开始。我认为它没有被调用。我正在迭代理解并且只使用返回值,为什么使用列表理解或生成器理解会改变任何东西?
def cffi_wrap(cffi_func, ndarray_params, pod_params, return_shapes=None):
"""
Wraps a cffi function to allow it to be called on numpy arrays.
It uss the numpy buffer protocol and and the cffi buffer protocol to pass the
numpy array into the c function without copying any of the parameters.
You will need to pass dimensions into the C function, which you can do using
the pod_params.
Parameters
----------
cffi_func : c function
This is a c function declared using cffi. It must take double pointers and
plain old data types. The arguments must be in the form of numpy arrays,
plain old data types, and then the returned numpy arrays.
ndarray_params : iterable of ndarrays
The numpy arrays to pass into the function.
pod_params : tuple of plain old data
This plain old data objects to pass in. This may include for example
dimensions.
return_shapes : iterable of tuples of positive ints
The shapes of the returned objects.
Returns
-------
return_vals : ndarrays of doubles.
The objects to be calculated by the cffi_func.
"""
arr_param_buffers = [np.ascontiguousarray(param, np.float64)
if np.issubdtype(param.dtype, np.float)
else np.ascontiguousarray(param, np.intc) for param in ndarray_params]
arr_param_ptrs = [ffi.cast("double *", ffi.from_buffer(memoryview(param)))
if np.issubdtype(param.dtype, np.float)
else ffi.cast("int *", ffi.from_buffer(memoryview(param)))
for param in arr_param_buffers]
if return_shapes is not None:
return_vals_ptrs = tuple(ffi.new("double[" + str(np.prod(shape)) + "]")
for shape in return_shapes)
returned_val = cffi_func(*arr_param_ptrs, *pod_params, *return_vals_ptrs)
return_vals = tuple(np.frombuffer(ffi.buffer(
return_val))[:np.prod(shape)].reshape(shape)
for shape, return_val in zip(return_shapes, return_vals_ptrs))
else:
returned_val = cffi_func(*arr_param_ptrs, *pod_params)
return_vals = None
if returned_val is not None and return_vals is not None:
return_vals = return_vals + (returned_val,)
elif return_vals is None:
return_vals = (returned_val,)
if len(return_vals) == 1:
return return_vals[0]
else:
return return_vals
【问题讨论】:
-
请注意,虽然您可以通过依赖缓冲区协议来避免一轮复制,但我认为没有特别的理由可以假设
np.ascontiguousarray()不会复制数据。这可能是代码运行缓慢的原因之一。通过传递未修改的 numpy 数组对象,并使用 Numpy 的 C API 在 C 端访问它们,您可能会获得更好的结果。我想这与 cffi 的配合不太好,但这是值得考虑的事情。 -
我想这是可能的。我认为,尽管我猜可能是错误的,但 np.ascontiguous 仅在必要时复制。大多数时候,它不应该做任何事情。如果你分析代码,慢的部分是 np.issubdtype() 和强制转换。选角,我真的无法摆脱。问题来自于我不应该调用 np.issubdtype() 两次。
-
“它停止工作”---你能更准确一点吗?它是如何失败的?
-
传递给 C 代码的指针不再有效。它们不再指向 numpy 数组的开头。
标签: python c numpy pypy python-cffi