Generator Comprehension 和 List Comprehension 的迭代方式不同
Generator Comprehension and List Comprehension iterate differently
我编写了一个函数,使用 CFFI 将 numpy 数组传递给 C 代码。它利用缓冲协议和内存视图有效地传递数据而无需复制数据。但是,这意味着您需要传递 C 连续数组并确保使用正确的类型。 Numpy 提供了一个函数 numpy.ascontiguous,
来执行此操作。所以我遍历参数,并应用这个函数。下面的实现是有效的,并且可能会引起普遍的兴趣。但是,考虑到它被调用的次数,它很慢。 (任何关于如何加快速度的一般性评论都会有所帮助。)
但是,实际问题是当您将第一个列表理解替换为生成器理解时,或者如果您重构代码以便在第二个中调用 np.ascontigous
,则传递给 C 代码的指针不再指向 numpy 数组的开头。我认为它没有被调用。我正在迭代理解并仅使用 return 值,为什么使用列表理解或生成器理解会改变任何东西?
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
我只是在猜测,但错误可能来自 keepalives:使用 arr_param_buffers
列表理解,如在您发布的代码中,那么只要这个局部变量存在(即在cffi_wrap()),所有创建的numpy数组都是存活的。这允许您在下一行执行 ffi.from_buffer(memoryview(...))
并确保它们都是指向有效数据的指针。
如果你用生成器表达式替换arr_param_buffers
,它会一个一个生成新的numpy数组,对它们调用ffi.from_buffer(memoryview(param))
,然后扔掉它们。 ffi.from_buffer(x)
returns 一个应该使 x
保持活动状态的对象,但据我所知,也许 x == memoryview(nd)
本身并不能使 numpy 数组 nd
保持活动状态。
我编写了一个函数,使用 CFFI 将 numpy 数组传递给 C 代码。它利用缓冲协议和内存视图有效地传递数据而无需复制数据。但是,这意味着您需要传递 C 连续数组并确保使用正确的类型。 Numpy 提供了一个函数 numpy.ascontiguous,
来执行此操作。所以我遍历参数,并应用这个函数。下面的实现是有效的,并且可能会引起普遍的兴趣。但是,考虑到它被调用的次数,它很慢。 (任何关于如何加快速度的一般性评论都会有所帮助。)
但是,实际问题是当您将第一个列表理解替换为生成器理解时,或者如果您重构代码以便在第二个中调用 np.ascontigous
,则传递给 C 代码的指针不再指向 numpy 数组的开头。我认为它没有被调用。我正在迭代理解并仅使用 return 值,为什么使用列表理解或生成器理解会改变任何东西?
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
我只是在猜测,但错误可能来自 keepalives:使用 arr_param_buffers
列表理解,如在您发布的代码中,那么只要这个局部变量存在(即在cffi_wrap()),所有创建的numpy数组都是存活的。这允许您在下一行执行 ffi.from_buffer(memoryview(...))
并确保它们都是指向有效数据的指针。
如果你用生成器表达式替换arr_param_buffers
,它会一个一个生成新的numpy数组,对它们调用ffi.from_buffer(memoryview(param))
,然后扔掉它们。 ffi.from_buffer(x)
returns 一个应该使 x
保持活动状态的对象,但据我所知,也许 x == memoryview(nd)
本身并不能使 numpy 数组 nd
保持活动状态。