Размер данных в памяти по сравнению с диском

Как оперативная память, необходимая для хранения данных в памяти, сравнивается с дисковым пространством, необходимым для хранения одних и тех же данных в файле? Или нет обобщенной корреляции?

Например, скажем, у меня просто есть миллиард значений с плавающей запятой. Сохраняется в двоичной форме, это будет 4 миллиарда байт или 3,7 ГБ на диске (не включая заголовки и т.д.). Тогда скажите, что я прочитал эти значения в списке на Python... сколько RAM я должен ожидать, чтобы потребовать?

Ответ 1

Размер данных объекта Python

Если данные хранятся в некотором объекте python, в фактических данных в памяти будет добавлено немного больше данных.

Это может быть легко протестировано.

The size of data in various forms

Интересно отметить, что, во-первых, накладные расходы на объект python значительны для небольших данных, но быстро становятся незначительными.

Вот код iPython, используемый для генерации графика

%matplotlib inline
import random
import sys
import array
import matplotlib.pyplot as plt

max_doubles = 10000

raw_size = []
array_size = []
string_size = []
list_size = []
set_size = []
tuple_size = []
size_range = range(max_doubles)

# test double size
for n in size_range:
    double_array = array.array('d', [random.random() for _ in xrange(n)])
    double_string = double_array.tostring()
    double_list = double_array.tolist()
    double_set = set(double_list)
    double_tuple = tuple(double_list)

    raw_size.append(double_array.buffer_info()[1] * double_array.itemsize)
    array_size.append(sys.getsizeof(double_array))
    string_size.append(sys.getsizeof(double_string))
    list_size.append(sys.getsizeof(double_list))
    set_size.append(sys.getsizeof(double_set))
    tuple_size.append(sys.getsizeof(double_tuple))

# display
plt.figure(figsize=(10,8))
plt.title('The size of data in various forms', fontsize=20)
plt.xlabel('Data Size (double, 8 bytes)', fontsize=15)
plt.ylabel('Memory Size (bytes)', fontsize=15)
plt.loglog(
    size_range, raw_size, 
    size_range, array_size, 
    size_range, string_size,
    size_range, list_size,
    size_range, set_size,
    size_range, tuple_size
)
plt.legend(['Raw (Disk)', 'Array', 'String', 'List', 'Set', 'Tuple'], fontsize=15, loc='best')

Ответ 2

В простом списке Python для каждого номера двойной точности требуется не менее 32 байт памяти, но для хранения фактического числа используется только 8 байтов, остальное необходимо для поддержки динамического характера Python.

Объект float, используемый в CPython, определен в floatobject.h:

typedef struct {
    PyObject_HEAD
    double ob_fval;
} PyFloatObject;

где PyObject_HEAD макрос, который расширяется до структуры PyObject:

typedef struct _object {
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
} PyObject;

Поэтому каждый объект с плавающей запятой в Python хранит два поля размера указателя (так что каждый из них занимает 8 байтов в 64-битной архитектуре), кроме 8-байтового двойника, давая 24 байта памяти, выделенной кучей на номер. Это подтверждается sys.getsizeof(1.0) == 24.

Это означает, что список n удваивается в Python занимает не менее 8*n байт памяти только для хранения указателей (PyObject*) для числовых объектов, а для каждого числового объекта требуется дополнительные 24 байта. Чтобы протестировать его, попробуйте запустить следующие строки в Python REPL:

>>> import math
>>> list_of_doubles = [math.sin(x) for x in range(10*1000*1000)]

и посмотреть использование памяти интерпретатора Python (я получил около 350 МБ выделенной памяти на моем компьютере x86-64). Обратите внимание, что если вы попытались:

>>> list_of_doubles = [1.0 for __ in range(10*1000*1000)]

вы получили бы около 80 МБ, потому что все элементы в списке относятся к тому же экземпляру числа с плавающей запятой 1.0.