【问题标题】:cython ctypedef large double arrays lead to segfault on Ubuntu 18.04cython ctypedef 大型双数组导致 Ubuntu 18.04 上的段错误
【发布时间】:2019-12-27 22:07:24
【问题描述】:
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.

【问题讨论】:

  • 这能回答你的问题吗? Segmentation fault on large array sizes
  • @eod 谢谢,但遗憾的是不是真的。提出的唯一真正的解决方案是使用ulimit -s unlimited 禁用操作系统的内存限制,这基本上不是一个好主意。我正在寻找一种方法来实际存储这些大量数据,并符合操作系统内存管理。
  • 如果不知道如何分配这些结构(您没有显示),这不是一个非常有用的问题。链接问题给出的更有用的解决方案是在堆而不是堆栈上分配(C++ 中的 new,但 Cython 中可能是 malloc
  • @DavidW 添加了一些有关处理的信息。也许主要问题可能导致:是否可以预先分配和使用像这样更大的区域,或者这可能更适合其他数据结构(也许比如2darrays?)。我有兴趣最终将超过 2 亿行存储到一个旋转数据结构中,并且认为原生 c 类型最合适。

标签: python-3.x memory-management segmentation-fault cython ubuntu-18.04


【解决方案1】:

作为the linked question @ead suggested,解决方案是将变量分配在堆上而不是堆栈上(作为函数局部变量)。原因是堆栈上的空间非常有限(Linux 上约为 8MB),而堆(通常)是您 PC 上可用的任何空间。

链接的问题主要是指new/delete作为C++的做法;虽然 Cython 代码可以使用 C++,但 C 更常用,为此您使用 malloc/freeCython documentation is pretty good on this 但是用你问题中的代码来演示它:

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)

您也可以使用模块级全局变量,但您可能不想这样做。

另一种选择是使用cdef 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 函数并返回一个ReturnRows*,并将解除分配移动到调用函数,或者您应该编写一个def 函数并返回一个有效的Python 对象。这可能会将您推向 Numpy 数组作为更好的解决方案,或者可能是 cdef 类。

您当前的函数所做的是将ReturnRows 转换为 Python 字典(包含数组的 Python 列表)的巨大且低效的转换,无论何时从 Python 调用它。这可能不是你想要的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-17
    • 1970-01-01
    • 1970-01-01
    • 2019-03-24
    • 1970-01-01
    • 2020-04-17
    相关资源
    最近更新 更多