Python 虚拟机字典dict内存优化方法解析
作者:一无是处的研究僧 发布时间:2022-03-04 08:20:56
引言
在前面的文章当中我们讨论的是 python3 当中早期的内嵌数据结构字典的实现,在本篇文章当中主要介绍在后续对于字典的内存优化。
字典优化
在前面的文章当中我们介绍的字典的数据结构主要如下所示:
typedef struct {
PyObject_HEAD
Py_ssize_t ma_used;
PyDictKeysObject *ma_keys;
PyObject **ma_values;
} PyDictObject;
struct _dictkeysobject {
Py_ssize_t dk_refcnt;
Py_ssize_t dk_size;
dict_lookup_func dk_lookup;
Py_ssize_t dk_usable;
PyDictKeyEntry dk_entries[1];
};
typedef struct {
/* Cached hash code of me_key. */
Py_hash_t me_hash;
PyObject *me_key;
PyObject *me_value; /* This field is only meaningful for combined tables */
} PyDictKeyEntry;
用图示的方式表示如下图所示:
所有的键值对都存储在 dk_entries 数组当中,比如对于 "Hello" "World" 这个键值对存储过程如下所示,如果 "Hello" 的哈希值等于 8 ,那么计算出来对象在 dk_entries 数组当中的下标位 0 。
在前面的文章当中我们谈到了,在 cpython 当中 dk_entries 数组当中的一个对象占用 24 字节的内存空间,在 cpython 当中的负载因子是 23\frac{2}{3}32 。而一个 entry 的大小是 24 个字节,如果 dk_entries 的长度是 1024 的话,那么大概有 1024 / 3 * 24 = 8K 的内存空间是浪费的。为了解决这个问题,在新版的 cpython 当中采取了一个策略用于减少内存的使用。具体的设计如下图所示:
在新的字典当中 cpython 对于 dk_entries 来说如果正常的哈希表的长度为 8 的话,因为负载因子是 23\frac{2}{3}32 真正给 dk_entries 分配的长度是 5 = 8 / 3,那么现在有一个问题就是如何根据不同的哈希值进行对象的存储。dk_indices 就是这个作用的,他的长度和真正的哈希表的长度是一样的,dk_indices 是一个整型数组这个数组保存的是要保存对象在 dk_entries 当中的下标,比如在上面的例子当中 dk_indices[7] = 0,就表示哈希值求余数之后的值等于 7,0 表示对象在 dk_entries 当中的下标。
现在我们再插入一个数据 "World" "Hello" 键值对,假设 "World" 的哈希值等于 8,那么对哈希值求余数之后等于 0 ,那么 dk_indices[0] 就是保存对象在 dk_entries 数组当中的下标的,图中对应的下标为 1 (因为 dk_entries 数组当中的每个数据都要使用,因此直接递增即可,下一个对象来的话就保存在 dk_entries 数组的第 3 个(下标为 2)位置)。
内存分析
首先我们先来分析一下数组 dk_indices 的数据类型,在 cpython 的内部实现当中并没有一刀切的直接将这个数组当中的数据类型设置成 int 类型。
dk_indices 数组主要有以下几个类型:
当哈希表长度小于 0xff 时,dk_indices 的数据类型为 int8_t ,即一个元素值占一个字节。
当哈希表长度小于 0xffff 时,dk_indices 的数据类型为 int16_t ,即一个元素值占 2 一个字节。
当哈希表长度小于 0xffffffff 时,dk_indices 的数据类型为 int32_t ,即一个元素值占 4 个字节。
当哈希表长度大于 0xffffffff 时,dk_indices 的数据类型为 int64_t ,即一个元素值占 8 个字节。
与这个相关的代码如下所示:
/* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */
static inline Py_ssize_t
dictkeys_get_index(const PyDictKeysObject *keys, Py_ssize_t i)
{
Py_ssize_t s = DK_SIZE(keys);
Py_ssize_t ix;
if (s <= 0xff) {
const int8_t *indices = (const int8_t*)(keys->dk_indices);
ix = indices[i];
}
else if (s <= 0xffff) {
const int16_t *indices = (const int16_t*)(keys->dk_indices);
ix = indices[i];
}
#if SIZEOF_VOID_P > 4
else if (s > 0xffffffff) {
const int64_t *indices = (const int64_t*)(keys->dk_indices);
ix = indices[i];
}
#endif
else {
const int32_t *indices = (const int32_t*)(keys->dk_indices);
ix = indices[i];
}
assert(ix >= DKIX_DUMMY);
return ix;
}
现在来分析一下相关的内存使用情况:
哈希表长度 | 能够保存的键值对数目 | 老版本 | 新版本 | 节约内存量(字节) |
---|---|---|---|---|
256 | 256 * 2 / 3 = 170 | 24 * 256 = 6144 | 1 * 256 + 24 * 170 = 4336 | 1808 |
65536 | 65536 * 2 / 3 = 43690 | 24 * 65536 = 1572864 | 2 * 65536 + 24 * 43690 = 1179632 | 393232 |
从上面的表格我们可以看到哈希表的长度越大我们节约的内存就越大,优化的效果就越明显。
来源:https://juejin.cn/post/7214489453399343160


猜你喜欢
- 一、前言return一直中,每中语言中其没没有很大差别,就不多说了。(shell语言return的是退出状态,可能差别是比较大的)最早看到y
- 前言一个函数就可以接收另一个函数作为参数,简言之,函数的参数能够接收别的函数,这种函数就称之为高阶函数JavaScript 的高阶函数跟 S
- 1、安装所需工具pip install pipreqs2、进入到python项目主目录pipreqs ./3、完成上面命令会生成requir
- Linux sort命令用于将文本文件内容加以排序。sort可针对文本文件的内容,以行为单位来排序。在 Linux、BSD 或 Mac 的终
- 四、 用户注册(reg.php)在看用户注册之流程之前,我把表的用途做个简单说明,现在只是大概的说明,后面我们再仔细了解,大家可以记下这个说
- python窃取摄像头照片源码+获取授权码方法+py打包成exe教你用python做一个属于自己的窃取摄像头照片的软件。需要安装python
- 如下所示:#-*- encoding:utf-8 -*-import csvimport sys,osimport pymysql def
- 数据描述每条数据项储存在列表中,最后一列储存结果多条数据项形成数据集data=[[d1,d2,d3...dn,result],
- 本文实例讲述了Python实现的视频播放器功能。分享给大家供大家参考,具体如下:# -*- coding:utf-8 -*-#! pytho
- 引言本篇是以python的视角介绍相关的函数还有自我使用中的一些问题,本想在这篇之前总结一下opencv编译的全过程,但遇到了太多坑,暂时不
- 列表的逆序遍历a = [1,3,6,8,9]print("通过下标逆序遍历1:")for i in a[::-1]: &
- 前言VScode是一个相当优秀的IDE,具备开源、跨平台、模块化、插件丰富、启动时间快、颜值高、可高度定制等等优秀的特质,不愧是微软爸爸的私
- 本项目是利用五年左右的世界地震数据,通过python的pandas库、matplotlib库、basemap库等进行数据可视化,绘制出地震散
- 这是一个神奇的组件,通过名字我们可以看出来,这个组件的功能就是把model和form组合起来,对,你没猜错,相信自己的英语水平。先来一个简单
- 效果图:代码如下:<html><head><title>颜色变换</title><st
- 卷积在pytorch中有两种实现,一种是torch.nn.Conv2d(),一种是torch.nn.functional.conv2d(),
- 没注意到MooTools的Cookie类在写的时候自己做了一次encode,在读的时候做了一次decode,在一般的情况下,这个不会有什么问
- 说明可以调取k8s API 接口的工具有很多,这里我就介绍下client-gogitlab上client-go项目地址: https://g
- picker-view 相关文章:微信小程序 Button 微信小程序 radio 微信小程序 slider 微信小程序 switch微信小
- 之前的文章介绍了python抓取网页数据并将数据保存到本地excel文件,后续可以将数据保存到数据库(SqlServer、mysql等)中,