当我分配一个巨大的 ndarray 时,numpy 空在做什么?

What is numpy empty doing under the hood when I allocate a massive ndarray?

我正在查看 space 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 中的其他什么地方出现。

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

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

当您尝试向数组元素添加 1 时会发生错误,因为该操作会修改所有内存位置,因此内核被迫将所有数组实际放入内存并失败。


我不是OS内存管理方面的专家,所以以上是我记得的OS课程。可在 here 中找到这方面的参考资料。引用自:

Linux on the other hand is seriously broken. It will by default answer "yes" to most requests for memory, in the hope that programs ask for more than they actually need.

它的意思是内核 malloc 几乎从不 return 一个 NULL,即使所需的内存太大。 "hopes" 用户实际上不会使用他们请求的所有内存,这样他就可以避免加载一些页面并适合所需的数据。显然这并不总是正确的。