内存中的数据大小与磁盘上的数据大小

19 投票
2 回答
6078 浏览
提问于 2025-04-18 02:16

内存中存储数据所需的RAM和将相同数据存储在文件中所需的磁盘空间有什么区别吗?或者说没有一个通用的关系?

举个例子,假设我有十亿个浮点数。如果以二进制形式存储,这大约需要40亿个字节,也就是3.7GB的磁盘空间(不包括一些头信息之类的)。然后,如果我把这些数值读入Python中的一个列表……我应该预期这会需要多少RAM呢?

2 个回答

4

在普通的Python列表中,每个双精度数字至少需要32个字节的内存,但实际上只有8个字节用来存储这个数字,剩下的部分是为了支持Python的动态特性。

在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来确认这一点。

这意味着,在Python中,一个包含n个双精度数字的列表,光是存储指向数字对象的指针PyObject*)就需要至少8*n字节的内存,而每个数字对象还需要额外的24字节。你可以在Python的交互式环境中运行以下代码来测试:

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

然后查看Python解释器的内存使用情况(在我的x86-64电脑上,大约分配了350 MB的内存)。需要注意的是,如果你尝试:

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

你会发现大约只有80 MB的内存使用,因为列表中的所有元素都指向同一个浮点数字1.0的实例。

14

Python对象的数据大小

如果数据存储在某个Python对象中,实际上在内存中会有一些额外的数据和真实数据一起存储。

这个可以很简单地测试一下。

不同形式数据的大小

有趣的是,对于小数据来说,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')

撰写回答