python-3.x - cython ctypedef 大型双数组导致 Ubuntu 18.04 上的段错误
问题描述
ctypedef struct ReturnRows:
double[50000] v1
double[50000] v2
double[50000] v3
double[50000] v4
有效,但是
ctypedef struct ReturnRows:
double[100000] v1
double[100000] v2
double[100000] v3
double[100000] v4
失败了Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
这对我来说没有意义,因为上限应该接近专用于该处理任务的系统的可用限制。是否以某种方式设置了上限?
这是我的建设者:
from distutils.core import setup
import numpy as np
from distutils.core import setup, Extension
from Cython.Build import cythonize
file_names = ['example_cy.pyx', 'enricher_cy.pyx']
for fn in file_names:
print("cythonize %s" % fn)
setup(ext_modules = cythonize(fn),
author='CGi',
author_email='hi@so.com',
description='Utils for faster data enrichment.',
packages=['distutils', 'distutils.command'],
include_dirs=[np.get_include()])
来自问题:我如何使用结构?我迭代它,来自熊猫数据框:
cpdef ReturnRows cython_atrs(list v1, list v2, list v3, list v4):
cdef ReturnRows s_ReturnRows # Allocate memory for the struct
s_ReturnRows.v1 = [0] * 50000
s_ReturnRows.v2 = [0] * 50000
s_ReturnRows.v3 = [0] * 50000
s_ReturnRows.v4 = [0] * 50000
# tmp counters to keep track of the latest data averages and so on.
cdef int lines_over_v1 = 0
cdef double all_ranges = 0
cdef int some_index = 0
for i in range(len(v3)-1):
# trs
s_ReturnRows.v1[i] = (round(v2[i] - v3[i],2))
# A lot more calculations, why I really need this loop.
解决方案
正如链接问题@ead 建议的那样,解决方案是将变量分配在堆上而不是堆栈上(作为函数局部变量)。原因是堆栈上的空间非常有限(Linux 上约为 8MB),而堆(通常)是您 PC 上可用的任何空间。
链接的问题主要是指new
/delete
作为 C++ 这样做的方式;虽然 Cython 代码可以使用 C++,但 C 更常用,为此您使用malloc
/ free
。Cython 文档在这方面非常好,但要使用您问题中的代码进行演示:
from libc.stdlib cimport malloc, free
# note some discussion about the return-value at the end of the question...
def cython_atrs(list v1, list v2, list v3, list v4):
cdef ReturnRows *s_ReturnRows # pointer, not value
s_ReturnRows = <ReturnRows*>malloc(sizeof(ReturnRows))
try:
# all your code goes here and remains the same...
finally:
free(s_ReturnRows)
您也可以使用模块级全局变量,但您可能不想这样做。
另一种选择是使用 acdef class
而不是结构:
cdef class ReturnRows:
double[50000] v1
double[50000] v2
double[50000] v3
double[50000] v4
这是在堆上自动分配的,内存由 Python 跟踪。
您还可以使用 2D Numpy/其他 Python 库数组。这些也分配在堆上(但它对你隐藏)。优点是 Python 会跟踪内存,因此您不会忘记释放它。第二个优点是您可以轻松更改数组大小而无需重新复杂化。如果您发明了一些特别无意义的微基准来分配大量数组并且什么都不做,您可能会发现性能差异,但对于大多数普通代码您不会。通过类型化的 memoryview 访问应该和 C 指针一样快。您可以找到很多比较速度/其他功能的问题,但实际上您应该只选择您认为最容易编写的一个(structs
可能是 C,也许)。
函数中的返回ReturnRows
增加了复杂性(即使它没有崩溃,也使您现有的代码也令人怀疑)。您应该编写一个cdef
函数并返回 a ReturnRows*
,并将解除分配移动到调用函数,或者您应该编写一个def
函数并返回一个有效的 Python 对象。这可能会将您推向 Numpy 数组作为更好的解决方案,或者可能是一个cdef
类。
每当从 Python 调用它时,您当前的函数将做的是将其转换为ReturnRows
Python 字典(包含数组的 Python 列表)的巨大且低效的转换。这可能不是你想要的。
推荐阅读
- powerapps - PowerApps 如何根据选择的不同列表框项目禁用和启用不同的下拉菜单
- html - Angular7 only allow number input to accept 2 digit
- python - 是否可以访问 matplotlib 艺术家的光栅化表示?
- python - 如何使 python 中的 Image.open 与根目录一起工作
- javascript - (节点:13040)UnhandledPromiseRejectionWarning:DiscordAPIError:无法发送空消息
- javascript - 有没有办法将每个值添加到 JS 中 for 循环中的第一个值?
- java - 倒数计时器,完成,在 Android Studio 中暂停
- javascript - 使用jQuery从具有相同类的特定div中提取值
- scmmanager - 与外部用户的身份验证失败
- reactjs - 如何点击鼠标打开下一页