当我分配一个庞大的ndarray时,什么是numpy空在幕后?

时间:2015-04-08 14:02:00

标签: python c memory-management numpy linux-kernel

我正在研究numpy数组在内存中消耗了多少空间,我注意到了一种奇特的行为:

当我跑x = np.empty((1000000, 7, 64, 64), dtype=np.uint8)

我的16GB内存电脑没有崩溃。相反,它分配了2GB内存,顺利航行。

这个numpy阵列的重量应该是26.70 GB,但似乎有些懒惰。当我添加一个,然后懒惰立即停止,我的程序挂起,他们得到MemoryError

我想知道numpy是如何做到这一点的。

我看了一下numpy.core.multiarray,发现numpy/core/src/multiarray/multiarraymodule.c这段代码似乎是空的定义:

static PyObject *
array_empty(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
{

    static char *kwlist[] = {"shape","dtype","order",NULL};
    PyArray_Descr *typecode = NULL;
    PyArray_Dims shape = {NULL, 0};
    NPY_ORDER order = NPY_CORDER;
    npy_bool is_f_order;
    PyArrayObject *ret = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&", kwlist,
                PyArray_IntpConverter, &shape,
                PyArray_DescrConverter, &typecode,
                PyArray_OrderConverter, &order)) {
        goto fail;
    }

    switch (order) {
        case NPY_CORDER:
            is_f_order = NPY_FALSE;
            break;
        case NPY_FORTRANORDER:
            is_f_order = NPY_TRUE;
            break;
        default:
            PyErr_SetString(PyExc_ValueError,
                            "only 'C' or 'F' order is permitted");
            goto fail;
    }

    ret = (PyArrayObject *)PyArray_Empty(shape.len, shape.ptr,
                                            typecode, is_f_order);

    PyDimMem_FREE(shape.ptr);
    return (PyObject *)ret;

 fail:
    Py_XDECREF(typecode);
    PyDimMem_FREE(shape.ptr);
    return NULL;
}

我想知道如何在C中实现这种懒惰,以及它会在numpy中弹出的其他地方。

1 个答案:

答案 0 :(得分:4)

请注意,内核可能会执行延迟分配。即malloc实际上并没有保留内存。当第一次访问内存时,将发生页面错误,内核将执行实际分配(并且可能决定仅分配该内存页而不是整个数组)。

换句话说:C不是懒惰的。这是推迟分配的内核。

当您尝试向数组元素添加一个时会发生错误,因为该操作会修改所有内存位置,因此内核被强制实际适合内存中的所有数组并失败。


我不是操作系统内存管理方面的专家,所以以上是我记得的操作系统课程。可以找到一个参考文献here。引用它:

  另一方面,Linux严重受损。它将默认回答   大多数内存请求都是“是”,希望程序要求   比实际需要更多。

它的含义是内核malloc几乎永远不会返回NULL,即使所需的内存太大。它“希望”用户实际上不会使用他们请求的所有内存,这样他就可以避免加载某些页面并且无论如何都能够满足所需的数据。显然,情况并非总是如此。