- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我在 Cython 中使用了很多 3D 内存 View ,例如
cython.declare(a='double[:, :, ::1]')
a = np.empty((10, 20, 30), dtype='double')
我经常想遍历 a
的所有元素。我可以使用像这样的三重循环来做到这一点
for i in range(a.shape[0]):
for j in range(a.shape[1]):
for k in range(a.shape[2]):
a[i, j, k] = ...
如果我不关心索引 i
、j
和 k
,那么做一个平面循环会更有效,比如
cython.declare(a_ptr='double*')
a_ptr = cython.address(a[0, 0, 0])
for i in range(size):
a_ptr[i] = ...
这里我需要知道数组中元素的数量 (size
)。这是由 shape
属性中元素的乘积给出的,即 size = a.shape[0]*a.shape[1]*a.shape[2]
,或者更一般的 size = np.prod(np.asarray(a).shape)
。我发现这两个都很难写,而且(尽管很小)计算开销困扰着我。最好的方法是使用内存 View 的内置 size
属性,size = a.size
。但是,出于我无法理解的原因,这会导致未优化的 C 代码,这从 Cython 生成的注释 html 文件中可以明显看出。具体来说,size = a.shape[0]*a.shape[1]*a.shape[2]
生成的 C 代码很简单
__pyx_v_size = (((__pyx_v_a.shape[0]) * (__pyx_v_a.shape[1])) * (__pyx_v_a.shape[2]));
从size = a.size
生成的C代码是
__pyx_t_10 = __pyx_memoryview_fromslice(__pyx_v_a, 3, (PyObject *(*)(char *)) __pyx_memview_get_double, (int (*)(char *, PyObject *)) __pyx_memview_set_double, 0);; if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 2238, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_10);
__pyx_t_14 = __Pyx_PyObject_GetAttrStr(__pyx_t_10, __pyx_n_s_size); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 2238, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_14);
__Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0;
__pyx_t_7 = __Pyx_PyIndex_AsSsize_t(__pyx_t_14); if (unlikely((__pyx_t_7 == (Py_ssize_t)-1) && PyErr_Occurred())) __PYX_ERR(0, 2238, __pyx_L1_error)
__Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0;
__pyx_v_size = __pyx_t_7;
为了生成上面的代码,我已经通过 compiler directives 启用了所有可能的优化。 ,这意味着无法优化 a.size
生成的笨重 C 代码。在我看来,size
“属性”并不是真正的预先计算的属性,但实际上是在查找时进行计算。此外,这种计算比简单地在 shape
属性上取乘积要复杂得多。我在 docs 中找不到任何解释的提示.
这种行为的解释是什么?我是否有比写出 a.shape[0]*a.shape[1]*a.shape[2]
更好的选择,如果我真的很关心这个微优化?
最佳答案
通过查看生成的 C 代码,您已经可以看到 size
是一个属性,而不是一个简单的 C 成员。这是 original Cython-code for memory-views :
@cname('__pyx_memoryview')
cdef class memoryview(object):
...
cdef object _size
...
@property
def size(self):
if self._size is None:
result = 1
for length in self.view.shape[:self.view.ndim]:
result *= length
self._size = result
return self._size
很容易看出,产品只计算一次然后缓存。显然它对 3 维数组没有太大作用,但对于更高维数的缓存可能变得非常重要(正如我们将看到的,最多有 8 个维度,所以它不是那么清楚地切割,这个缓存是否真的很值得)。
可以理解延迟计算 size
的决定 - 毕竟,并不总是需要/使用 size
并且人们不想为此付费。显然,如果您经常使用 size
,这种懒惰是要付出代价的——这就是 cython 做出的权衡。
我不会过多地讨论调用 a.size
的开销——与从 python 调用 cython 函数的开销相比,这不算什么。
例如,@danny 的测量仅测量此 python 调用开销,而不测量不同方法的实际性能。为了说明这一点,我加入了第三个函数:
%%cython
...
def both():
a.size+a.shape[0]*a.shape[1]*a.shape[2]
它做了两倍的工作,但是
>>> %timeit mv_size
22.5 ns ± 0.0864 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
>>> %timeit mv_product
20.7 ns ± 0.087 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
>>>%timeit both
21 ns ± 0.39 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
一样快。另一方面:
%%cython
...
def nothing():
pass
不是更快:
%timeit nothing
24.3 ns ± 0.854 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
简而言之:我会使用 a.size
因为可读性,假设优化不会加速我的应用程序,除非分析证明有什么不同。
整个故事:变量a
是__Pyx_memviewslice
类型而不是__pyx_memoryview
类型思考。结构 __Pyx_memviewslice
具有以下定义:
struct __pyx_memoryview_obj;
typedef struct {
struct __pyx_memoryview_obj *memview;
char *data;
Py_ssize_t shape[8];
Py_ssize_t strides[8];
Py_ssize_t suboffsets[8];
} __Pyx_memviewslice;
这意味着,shape
可以通过 Cython 代码非常有效地访问,因为它是一个简单的 C 数组(顺便说一句。我问自己,如果有超过 8 个维度会发生什么? - 答案是:维度不能超过 8 个)。
成员 memview
是内存所在的位置,__pyx_memoryview_obj
是 C-Extension,它是从我们上面看到的 cython 代码生成的,如下所示:
/* "View.MemoryView":328
*
* @cname('__pyx_memoryview')
* cdef class memoryview(object): # <<<<<<<<<<<<<<
*
* cdef object obj
*/
struct __pyx_memoryview_obj {
PyObject_HEAD
struct __pyx_vtabstruct_memoryview *__pyx_vtab;
PyObject *obj;
PyObject *_size;
PyObject *_array_interface;
PyThread_type_lock lock;
__pyx_atomic_int acquisition_count[2];
__pyx_atomic_int *acquisition_count_aligned_p;
Py_buffer view;
int flags;
int dtype_is_object;
__Pyx_TypeInfo *typeinfo;
};
所以,Pyx_memviewslice
并不是真正的 Python 对象 - 它是一种方便的包装器,它缓存重要数据,如 shape
和 stride
因此可以快速且廉价地访问这些信息。
当我们调用 a.size
时会发生什么?首先,调用 __pyx_memoryview_fromslice
,它会执行一些额外的引用计数和一些其他操作,并从 __Pyx_memviewslice
对象返回成员 memview
。
然后在这个返回的 memoryview 上调用属性 size
,它访问 _size
中的缓存值,如上面的 Cython 代码所示。
看起来好像 python 程序员为 shape
、strides
和 suboffsets
等重要信息引入了快捷方式,但不是为size
这可能不是那么重要 - 这就是在 shape
的情况下 C 代码更清晰的原因。
关于python - Cython:内存 View 的大小属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49919768/
c 不做边界检查。那么cython是如何检查是否编译成c的呢? %%cython --annotate cimport cython @cython.boundscheck(True) cpdef m
可以直接声明用于 Cython 构造函数? 据我了解,这是可能的: # Cython cdef int[3] li = [1, 2, 3] # C++ int[3] li = {1, 2, 3} 但
所以,如果你有一个头文件。 %%file test.h struct mystruct{ int i; int j; }; 然后你将它包装在 Cython 中: cdef extern fr
我正在构建一个独立于平台的 cython 项目,我想根据正在使用的编译器传递编译器参数。我可以猜测基于平台的编译器,或者假设它与用于 Python 的编译器相同,但不能保证匹配。通常我注入(injec
我使用诗歌构建我的 cython 包。我在所有函数和类中都有 NumPy 风格的文档字符串。我现在要做的是添加 Sphinx 自动文档并发布在 Read the Docs。 我已阅读此主题 How d
赛通 libcpp模块包含 priority_queue 的模板,这很好,除了一件事:我不能通过自定义比较器(或者,至少,我不知道如何)。 我需要这个,因为我需要 priority_queue做一个a
以下代码定义了一个简单的 Cython 函数(为方便起见,使用 Ipython 魔法)。 %load_ext cython %%cython def f(float x, float y=2):
我正在尝试使用 cython 进行复数计算。在示例代码中,我想计算复数的复指数函数。问题是我不知道如何将我的整数乘以虚数单位。python的虚数单位1.0j乘以cython执行时报错。 这是我的代码:
在这里停留在一些基本的 Cython 上 - 在 Cython 中定义字符串数组的规范且有效的方法是什么? 具体来说,我想定义一个定长常量数组char . (请注意,此时我不想引入 NumPy。) 在
是否有可能,如果是,如何确定 Cython 中整数数据类型的大小(以位为单位)? 我正在尝试做这样的事情,以获得整数大小: cdef WORD_BITS = 0 IF sizeof(unsigned
我只是想打印 cython 变量的地址,但我无法绕过错误消息: cdef int myvar print &myvar 抛出 Cannot convert 'int *' to Python obje
我有一个 C 头文件,它在宏中定义了一个函数。我需要从 Cython 调用它。有没有办法在 Cython 中使用宏并使其完全扩展?我已经有了 C 类型的参数。 我尝试像使用函数一样使用 cdef,我认
令人惊讶的是,我似乎找不到通过名称获取结构体元素的单个示例(无论是在网络上还是在 cython 示例中)。 所以我收到了一个指向 C 函数结构体的指针,并且想要一一访问这些元素并将它们重新打包到 py
我尝试围绕 C++ 库编写一个 Cython 包装器 http://primesieve.org/ 它包装了一个函数count。到目前为止,它可以正确安装 python setup.py instal
我正在尝试将 cython 模块 data.pyx 导入另一个 cython 模块 user.pyx。一切都编译得很好,但是当我尝试在 python 模块中调用 user.pyx 时,我收到错误“Im
更新:内存 View 获胜。Cython 使用类型化内存 View :0.0253449 特别感谢 lothario,他指出了几个关键的变化。 荒谬。当然现在的问题是,似乎不能对它们做太多算术(加法和
我有一个使用 memoryview 数组的 cython 模块,即... double[:,:] foo 我想使用多处理并行运行这个模块。但是我得到了错误: PicklingError: Can't
我正在尝试使用 Cython 加速 PEP 484 类型的 python 脚本。我想保持一些语义和可读性。 之前,我有一个 Flags = int def difference(f1: Flags,
这个问题已经有答案了: Collapse multiple submodules to one Cython extension (5 个回答) 已关闭 3 年前。 我在一个包中有多个 .py 文件
我已经能够在我的 .pyx 脚本上使用 cython 在 linux 上创建一个 .so 文件。我也可以成功地在我的 python 解释器上进行导入。 我的问题是如何在不使用 cython 的情况下将
我是一名优秀的程序员,十分优秀!