惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog RSS Feed
V2EX - 技术
V2EX - 技术
P
Privacy & Cybersecurity Law Blog
T
The Exploit Database - CXSecurity.com
美团技术团队
WordPress大学
WordPress大学
博客园 - 司徒正美
S
Securelist
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - Franky
Attack and Defense Labs
Attack and Defense Labs
Security Latest
Security Latest
L
LINUX DO - 最新话题
NISL@THU
NISL@THU
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
腾讯CDC
Y
Y Combinator Blog
The Hacker News
The Hacker News
Security Archives - TechRepublic
Security Archives - TechRepublic
IT之家
IT之家
T
Threatpost
Hugging Face - Blog
Hugging Face - Blog
Scott Helme
Scott Helme
S
SegmentFault 最新的问题
Cyberwarzone
Cyberwarzone
C
Cisco Blogs
阮一峰的网络日志
阮一峰的网络日志
U
Unit 42
B
Blog
Microsoft Azure Blog
Microsoft Azure Blog
P
Proofpoint News Feed
小众软件
小众软件
V
Vulnerabilities – Threatpost
J
Java Code Geeks
V
Visual Studio Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
Arctic Wolf
博客园 - 【当耐特】
Microsoft Security Blog
Microsoft Security Blog
S
Security @ Cisco Blogs
雷峰网
雷峰网
Help Net Security
Help Net Security
The Last Watchdog
The Last Watchdog
Recent Announcements
Recent Announcements
G
Google Developers Blog
C
CERT Recently Published Vulnerability Notes
T
Troy Hunt's Blog
MyScale Blog
MyScale Blog

Posts on WKLKEN THINKING

apisix 中的 lrucache apisix 中的服务发现机制 apisix 中的负载均衡 apisix etcd机制 聊聊框架 关于 k8s 的 zero downtime deployment 一些建议 apisix 遇到的一些问题 关于在除夕前一天换了一个洗衣机的故事 Django DRF 性能优化 DRF 的一些实践 Part1: Serializer DRF继承关系图 Better Code: 关于接口的灵活性 新的仓库: wklken/naming 缓存使用的一些经验 Better Code: 抽象: 可扩展性与可维护性的抉择 Better Code: 异常时, 该提示用户哪些信息? Better Code: 更好的异常日志打印 Go: some libs Go: go-redis/cache升级的坑 Go: logrus性能提升 Go: gin validation 远程办公的一点总结 Go: 开发过程中的一些bug 项目管理实践: 风险驱动开发 Go: 一种error wrap调用链处理方式 漫谈技术选型 Go: 基于 apitest 做handler层单元测试 Go: go-sql-driver interpolateparams参数优化 [分享]深度工作 你需要更多的思考时间 Django项目重构小结 工作七年小结: 学习,生活及其他 [分享]bash日常: bash-utils 极客时间推广海报 2017总结: 予时光以意义 k8s APIServer源码: api注册详细细节 k8s APIServer源码: api注册主体流程 k8s APIServer源码: 服务启动 k8s APIServer源码: go-restful框架 重构 - 读书笔记(Python示例) 写给新人的沟通建议 vim 杂谈 - 关于快速编辑 vim 杂谈 - 关于移动 读书笔记-重构: 章11 处理概括关系 读书笔记-重构: 章10 简化函数调用 读书笔记-重构: 章9 简化表达式 读书笔记-重构: 章8 重新组织数据 读书笔记-重构: 章7 在对象之间搬移特性 读书笔记-重构: 章6 重新组织函数 Python 代码规范小结 [分享]关于vim ElasticSearch集群部署文档 Logstash+ElasticSearch处理mysql慢查询日志 [分享]关于代码调试DE那些事 Logstash+ElasticSearch+Kibana- 实现相对通用的数据收集分析 ELK维护的一些点(二) [分享]Python源码剖析-数据结构 一些Centos Python生产环境的部署命令 摘录<<6个月学会任何一种外语>> ELK 维护的一些点 也许是一个新的开始 一些vim的个性化配置 读书笔记-调试九法 这段时间的一些想法 Python 源码阅读 - 垃圾回收机制 我为什么要写博客 APUE笔记-第一章 UNIX基础知识 Python源码阅读-闭包的实现 Python源码阅读-内存管理机制(二) Python源码阅读-内存管理机制(一) Python-基础-数据结构小结 '活动'设计的一些trick 一些简单的Python测试题 我的tmux配置及说明【k-tmux】 Review and Restart 工作四周年小结 vim插件: surround & repeat[成对符号编辑] vim插件: gundo[时光机] vim插件: expand-region[区域选中] vim插件: quickrun[快速执行] vim插件: trailing-whitespace[行尾空格处理] vim插件: closetag[成对标签补全] vim插件: ctrlp[文件搜索] vim插件: airline[状态栏增强] vim插件: theme[主题] vim插件: tagbar[大纲式导航] vim插件: nerdcommenter[快速注释] vim插件: rainbow_parentheses[括号高亮] vim插件: syntastic[语法检查] vim插件: delimitmate[符号自动补全] vim插件: matchit[成对标签跳转] vim插件: easy-align[快速对齐] vim插件: multiple-cursors[多光标操作] vim插件: vim-signature[快速标记跳转] vim插件: easymotion[快速跳转] vim插件: vundle[管理插件] Elasticsearch几个问题的解决 分享一份 Vim 简介PPT k-vim 更新9.0版本 关于知识管理工具的思考
Python 源码阅读 - int
2014-08-06 · via Posts on WKLKEN THINKING

==========================

代码我也仅仅是粗粗读了一遍, 可能出现疏漏和理解错误, 发现了望指出哈.

今天面了一家靠谱的创业公司, 可惜不是Python向的, 想继续玩Python是有代价的, 选择余地太窄了……

话说写文章很耗时间, 这个花了两个多小时….主要还是自个绘图渣效率低:(

准备找工作事宜很占时间, 后面只能慢慢来了(好像还很多很多的样子)


示例

>>> a = 1
>>> b = 1
>>> id(a) == id(b)
True

>>> c = 257
>>> d = 257
>>> id(c) == id(d)
False

#在python2.x中, 对于大的序列生成, 建议使用xrange(100000) 而不是range(100000), why?

源码位置 Include/intobject.h | Objects/intobject.c


PyIntObject

typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;

结构

PyIntObject


几个构造方法

# 从字符串, 生成PyIntObject对象
PyAPI_FUNC(PyObject *) PyInt_FromString(char*, char**, int);

# 从Py_UNICODE, 生成PyIntObject对象
#ifdef Py_USING_UNICODE
PyAPI_FUNC(PyObject *) PyInt_FromUnicode(Py_UNICODE*, Py_ssize_t, int);
#endif

# 从long值, 生成PyIntObject对象
PyAPI_FUNC(PyObject *) PyInt_FromLong(long);

PyAPI_FUNC(PyObject *) PyInt_FromSize_t(size_t);
PyAPI_FUNC(PyObject *) PyInt_FromSsize_t(Py_ssize_t);

这几个方法, 只需要关注

# 因为大家最后都调用这个方法完成对象生成
PyAPI_FUNC(PyObject *) PyInt_FromLong(long);

具体的构造方法 PyInt_FromLong

这个方法的定义

PyObject *
PyInt_FromLong(long ival)
{
    register PyIntObject *v;

    /* MARK: 如果, 值在小整数范围内, 直接从小整数对象池获取得到对象 */

    #if NSMALLNEGINTS + NSMALLPOSINTS > 0
    if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {

        /* MARK: small_ints是什么后面说 */
        v = small_ints[ival + NSMALLNEGINTS];
        // 引用+1
        Py_INCREF(v);

        /* 这里先忽略, 计数 */
        #ifdef COUNT_ALLOCS
            if (ival >= 0)
                quick_int_allocs++;
            else
                quick_neg_int_allocs++;
        #endif

        // 返回
        return (PyObject *) v;
    }
    #endif

    // 如果free_list还不存在, 或者满了
    if (free_list == NULL) {
        // 新建一块PyIntBlock, 并将空闲空间链表头部地址给free_list
        if ((free_list = fill_free_list()) == NULL)
            // 如果失败, 返回
            return NULL;
    }

    // 从free_list分出一个位置存放新的整数

    /* Inline PyObject_New */
    // 使用单向链表头位置
    v = free_list;

    // free_list指向单向链表下一个位置
    free_list = (PyIntObject *)Py_TYPE(v);

    // 初始化对象, 类型为PyInt_type, 值为ival
    PyObject_INIT(v, &PyInt_Type);
    v->ob_ival = ival;

    // 返回
    return (PyObject *) v;
}

注意这里的Py_TYPE()方法, 在我们第一篇文章里面有提到, 不知道的回去复习下对象的数据结构

#define Py_TYPE(ob)             (((PyObject*)(ob))->ob_type)

简而言之:

1. 先判断数值是否是小整数, 是的话从小整数对象池里面直接返回
(这个池固定大小, 下一点讲)

2. 如果不是, 从通用整数对象池里面取一个, 初始化返回
(如果这时候通用整数对象池还不存在或者已经满了, 新建一个池加入维护. 通用整数对象池后面讲)

小整数对象池

先看定义

#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS           257
#endif

#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS           5
#endif

#if NSMALLNEGINTS + NSMALLPOSINTS > 0
/* References to small integers are saved in this array
   so that they can be shared.
   The integers that are saved are those in the range
   -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/

static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
#endif

其实, 小整数对象池就是一个PyIntObject指针数组(注意是指针数组), 大小=257+5=262, 范围是[-5, 257) 注意左闭右开. 即这个数组包含了262个指向PyIntObject的指针.

结构

small_ints

创建整数时, 如果在[-5, 257)范围, 直接返回已经存在的整数对象指针, 所以我们看到开头的例子, id比较一个true/一个false

小整数对象池, 在一开始就初始化了, 其初始化代码

int
_PyInt_Init(void)
{
    PyIntObject *v;
    int ival;

    // 注意这里, free_list再次出现

#if NSMALLNEGINTS + NSMALLPOSINTS > 0

    // 循环, 逐一生成
    for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++) {
          if (!free_list && (free_list = fill_free_list()) == NULL)
                  return 0;

        // 注意这段代码, 和上面PyInt_FromLong那段代码一样的
        /* PyObject_New is inlined */
        v = free_list;
        free_list = (PyIntObject *)Py_TYPE(v);
        PyObject_INIT(v, &PyInt_Type);
        v->ob_ival = ival;

        // 放到数组里
        small_ints[ival + NSMALLNEGINTS] = v;
    }
#endif

    return 1;
}

代码很眼熟吧, 觉得不眼熟回上面看代码

结论

1. 小整数对象池缓存 [-5, 257) 内的整数对象, 数值在这个范围的整数对象有且只存在一个...

2. 小整数对象池, 只是一个指针数组, 其真正对象依赖通用整数对象池

通用整数对象池1 - 基础结构PyIntBlock

首先, 有个数据结构PyIntBlock

#define BLOCK_SIZE      1000    /* 1K less typical malloc overhead */
#define BHEAD_SIZE      8       /* Enough for a 64-bit pointer */
#define N_INTOBJECTS    ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))


struct _intblock {
    struct _intblock *next;
    PyIntObject objects[N_INTOBJECTS];
};

typedef struct _intblock PyIntBlock;

回忆一下PyIntObject结构(1个int, 1指针, 1个long), size=4+4+4(先这么算), N_INTOBJECTS = 82

结构

PyIntBlock

通用整数对象池2 - 创建过程及运行时结构

有两个指针

# 指向一个block
static PyIntBlock *block_list = NULL;

# 指向一个PyIntObject
static PyIntObject *free_list = NULL;

生成过程的定义

// 初始化一个PyIntBlock
static PyIntObject *
fill_free_list(void)
{
    PyIntObject *p, *q;
    // 建立一个新的block
    /* Python's object allocator isn't appropriate for large blocks. */
    p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));

    // 建立失败(内存耗光了)
    if (p == NULL)
        return (PyIntObject *) PyErr_NoMemory();

    // block_list指向新的PyIntBlock节点
    ((PyIntBlock *)p)->next = block_list;
    block_list = (PyIntBlock *)p;

    /* Link the int objects together, from rear to front, then return
       the address of the last int object in the block. */

    // p=block里面 PyIntObjects数组头地址, q是尾地址
    p = &((PyIntBlock *)p)->objects[0];
    q = p + N_INTOBJECTS;

    // 从尾部开始向首部移动, 利用对象里的ob_type指针(相当于使用这个字段, ob_type不作为原来的用途), 建立起一个单向链表
    // 这个单向链表的头部是数组的最后一个
    while (--q > p)
        Py_TYPE(q) = (struct _typeobject *)(q-1);
    Py_TYPE(q) = NULL; // 单向链表最后一个元素的next指向null

    // 返回单向链表的头地址!!!
    return p + N_INTOBJECTS - 1;

}

新建第一个时, 只有一个

PyIntBlock

从里面拿整数时, 取free_list指向的节点, 然后free_list指向链表下一个节点

当一个block用完了之后, 即free_list=NULL, 此时要新建另一个PyIntBlock

新建第二个

PyIntBlock

通用整数对象池3 - 删除一个整数时

定义

#define PyInt_CheckExact(op) ((op)->ob_type == &PyInt_Type)

static void
int_dealloc(PyIntObject *v)
{
    // 是整数类型, 将对象放入free_list单向链表头
    if (PyInt_CheckExact(v)) {
        Py_TYPE(v) = (struct _typeobject *)free_list;
        free_list = v;
    }
    else
        Py_TYPE(v)->tp_free((PyObject *)v); //不是整数类型, 对应类型析构
}

可以看到, 回收的时候, 把空间给放回到free_list了, 后面接着用

block_list维护着所有PyIntBlock列表, 查看源码注释可以看到

PyIntBlocks are never returned to the
 system before shutdown (PyInt_Fini).

即, PyIntBlock申请的所有内存, 在Python结束之前, 都不会被释放

所以, 使用range(100000), 运行后, 虽然程序结束了, 但是整数占用空间还在.

建议对大范围的序列生成使用xrange

python3.x不用担心这个问题

changelog

2014-08-07 first version