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

推荐订阅源

F
Fortinet All Blogs
aimingoo的专栏
aimingoo的专栏
人人都是产品经理
人人都是产品经理
G
GRAHAM CLULEY
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
L
LangChain Blog
AWS News Blog
AWS News Blog
V
Vulnerabilities – Threatpost
博客园 - 司徒正美
Last Week in AI
Last Week in AI
P
Privacy International News Feed
C
CERT Recently Published Vulnerability Notes
Simon Willison's Weblog
Simon Willison's Weblog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Schneier on Security
Y
Y Combinator Blog
月光博客
月光博客
博客园 - Franky
T
Threatpost
Security Latest
Security Latest
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 【当耐特】
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
大猫的无限游戏
大猫的无限游戏
A
Arctic Wolf
T
The Exploit Database - CXSecurity.com
A
About on SuperTechFans
I
Intezer
C
CXSECURITY Database RSS Feed - CXSecurity.com
C
Cisco Blogs
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
美团技术团队
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Microsoft Security Blog
Microsoft Security Blog
Know Your Adversary
Know Your Adversary
IT之家
IT之家
D
Docker
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
NISL@THU
NISL@THU
博客园 - 三生石上(FineUI控件)
L
Lohrmann on Cybersecurity
小众软件
小众软件
PCI Perspectives
PCI Perspectives
N
News and Events Feed by Topic
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Google DeepMind News
Google DeepMind News
爱范儿
爱范儿
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Engineering at Meta
Engineering at Meta

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 源码阅读 - tuple
2014-08-10 · via Posts on WKLKEN THINKING

还差一篇……写完写简历>_<#


示例

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

>>> a = (1, )
>>> b = (1, )
>>> id(a) == id(b)
False

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


结构

定义

typedef struct {
    PyObject_VAR_HEAD
    PyObject *ob_item[1];

} PyTupleObject;

说明

1. PyObject_VAR_HEAD
PyTupleObject在底层是个变长对象(需要存储列表元素个数).
虽然, 在python中, tuple是不可变对象

2. PyObject *ob_item[1];
指向存储元素的数组

3.

结构

PyTupleObject

构造方法

PyAPI_FUNC(PyObject *) PyTuple_New(Py_ssize_t size);

构造

看下构造方法定义

PyObject *
PyTuple_New(register Py_ssize_t size)
{
    register PyTupleObject *op;
    Py_ssize_t i;

    // 大小为负数, return
    if (size < 0) {
        PyErr_BadInternalCall();
        return NULL;
    }

    // 如果大小=0, 空元组, 直接取free_list第一个返回
#if PyTuple_MAXSAVESIZE > 0
    if (size == 0 && free_list[0]) {
        op = free_list[0];
        Py_INCREF(op);

#ifdef COUNT_ALLOCS
        tuple_zero_allocs++;
#endif

        return (PyObject *) op;
    }

    // 如果free_list可分配, 从free_list取一个
    if (size < PyTuple_MAXSAVESIZE && (op = free_list[size]) != NULL) {
        // 上面  op = free_list[size] 取得单链表头
        // free_list指向单链表下一个元素, 对应位置阈值--
        free_list[size] = (PyTupleObject *) op->ob_item[0];
        numfree[size]--;

#ifdef COUNT_ALLOCS
        fast_tuple_allocs++;
#endif

      // 初始化 ob_size和ob_type
      /* Inline PyObject_InitVar */
#ifdef Py_TRACE_REFS
        Py_SIZE(op) = size;
        Py_TYPE(op) = &PyTuple_Type;
#endif
        _Py_NewReference((PyObject *)op);
    }
    else
#endif   // free_list不可用
    {
        // 计算空间
        Py_ssize_t nbytes = size * sizeof(PyObject *);
        /* Check for overflow */
        if (nbytes / sizeof(PyObject *) != (size_t)size ||
            (nbytes > PY_SSIZE_T_MAX - sizeof(PyTupleObject) - sizeof(PyObject *)))
        {
            return PyErr_NoMemory();
        }

        // 分配内存
        op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size);
        if (op == NULL)
            return NULL;
    }

    // 初始化ob_item每个元素
    for (i=0; i < size; i++)
        op->ob_item[i] = NULL;

    // 第一次分配空数组, 将其放入free_list第一个位置
#if PyTuple_MAXSAVESIZE > 0
    if (size == 0) {
        free_list[0] = op;
        ++numfree[0];
        Py_INCREF(op);          /* extra INCREF so that this is never freed */
    }
#endif


#ifdef SHOW_TRACK_COUNT
    count_tracked++;
#endif

    _PyObject_GC_TRACK(op);

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

简化步骤

1. 如果size=0, 从free_list[0]取, 直接返回

2. 否则, 确认free_list[size], 是否可用, 可用获取

3. 否则, 从内存分配新的空间

4. 初始化, 返回

回收

定义

static void
tupledealloc(register PyTupleObject *op)
{
    register Py_ssize_t i;
    // 获取元素个数
    register Py_ssize_t len =  Py_SIZE(op);

    PyObject_GC_UnTrack(op);
    Py_TRASHCAN_SAFE_BEGIN(op)


    if (len > 0) {
        i = len;
        // 遍历, 析构每个元素
        while (--i >= 0)
            Py_XDECREF(op->ob_item[i]);

         // 与对象缓冲池相关
#if PyTuple_MAXSAVESIZE > 0
        if (len < PyTuple_MAXSAVESIZE &&
            numfree[len] < PyTuple_MAXFREELIST &&
            Py_TYPE(op) == &PyTuple_Type)
        {
            op->ob_item[0] = (PyObject *) free_list[len];
            numfree[len]++;
            free_list[len] = op;
            goto done; /* return */
        }
#endif

    }
    // 调用回收
    Py_TYPE(op)->tp_free((PyObject *)op);

done:
    Py_TRASHCAN_SAFE_END(op)
}

简化流程

1. 回收ob_item每个元素

2. 如果符合条件, 放入到free_list

3. 否则, 回收

定义

/* Speed optimization to avoid frequent malloc/free of small tuples */
#ifndef PyTuple_MAXSAVESIZE
#define PyTuple_MAXSAVESIZE     20
#endif

#ifndef PyTuple_MAXFREELIST
#define PyTuple_MAXFREELIST  2000
#endif

#if PyTuple_MAXSAVESIZE > 0

static PyTupleObject *free_list[PyTuple_MAXSAVESIZE];
static int numfree[PyTuple_MAXSAVESIZE];
#endif

结论

1. 作用: 优化小tuple的mall/free

2. PyTuple_MAXSAVESIZE = 20
会被缓存的tuple长度阈值, 20, 长度<20的, 才会走对象缓冲池逻辑

3. PyTuple_MAXFREELIST  2000
每种size的tuple最多会被缓存2000个

4. PyTupleObject *free_list[PyTuple_MAXSAVESIZE]
free_list, 指针数组, 每个位置, 存储了指向一个单链表头的地址

5. numfree[PyTuple_MAXSAVESIZE]
numfree, 一个计数数组, 存储free_list对应位置的单链表长度

6. free_list[0], 指向空数组, 有且仅有一个

free_list的结构

PyTupleObjectPool

回头看回收跟对象缓冲池相关的逻辑

条件:

if (len < PyTuple_MAXSAVESIZE &&         // len < 20
    numfree[len] < PyTuple_MAXFREELIST && // numfree[len] < 2000
    Py_TYPE(op) == &PyTuple_Type) // 是tuple类型

操作

op->ob_item[0] = (PyObject *) free_list[len]; //ob_item指向free_list[len] 单链表头
numfree[len]++;  // len位置计数+1
free_list[len] = op; // op变成单链表的头
goto done; /* return */

即过程

1. 如果size=0, 直接从free_list[0]取

2. 如果size!=0, 判断size<20?

3.1 size < 20, 从free_list对应size位置的单链表, 取头部第一个位置

3.2 size < 20, free_list对应size位置还没有可用对象的话, 走内存分配逻辑

4 size > 20  走内存分配逻辑


------------------

回收时

如果size<20, 且free_list对应位置单链表长度没达到上限(2000), 将对象放入到单链表头

注意

1. 回收时, ob_item都会被回收, 只是本身对象缓存下来

2. 这里free_list, 复用ob_item作为链表指针, 指向下一个位置(通用整数对象池也是复用指针的方式, 不过用的是ob_type)

changelog

2014-08-10 first version