zoukankan      html  css  js  c++  java
  • 一个配置表优化的想法

    今天下班在班车上想了一个关于配置表存储的小优化,起因是早上的时候发现了一个bug,这个bug是由于在运行时动态更改了一个列表配置导致的。

    其实关于这种运行时“偷偷”改配置的问题我之前也有考虑过,这种应该是一不小心就会写出的,这不终于都出了一个。

    至于如何预防这种问题,我认为在python里面似乎也没有什么好的解决方法,因为它不像c++有const语义,但有一个稍尽人事的预防措施就是把列表型的配置读成元组(tuple)。而由此衍生出的一个想法便是:把配置表中所有的列表型配置都读成共享的元组,即只要是其内容一致的那么内存中就只会存一份,类似于string intern

    在将这个想法付诸实践之前,有一些问题是需要搞清楚的。因为如果我想要做的,python都帮我做的七七八八了,那我就不需要在此花费无用的精力了。因此首要问题是,python的元组是否有共享机制?以及其对何种元组是会共享的?

    显然不可能是所有的元组都使用共享策略,但我知道空的元组是会被共享的。

    稍微阅读了一下python的元组源码,在tupleobject.c中

    空闲链表:

    /* Speed optimization to avoid frequent malloc/free of small tuples */
    #ifndef PyTuple_MAXSAVESIZE
    #define PyTuple_MAXSAVESIZE     20  /* Largest tuple to save on free list */
    #endif
    #ifndef PyTuple_MAXFREELIST
    #define PyTuple_MAXFREELIST  2000  /* Maximum number of tuples of each size to save */
    #endif
    
    #if PyTuple_MAXSAVESIZE > 0
    /* Entries 1 up to PyTuple_MAXSAVESIZE are free lists, entry 0 is the empty
       tuple () of which at most one instance will be allocated.
    */
    static PyTupleObject *free_list[PyTuple_MAXSAVESIZE];
    static int numfree[PyTuple_MAXSAVESIZE];
    #endif

    创建元组的代码:

    PyObject *
    PyTuple_New(register Py_ssize_t size)
    {
        register PyTupleObject *op;
        Py_ssize_t i;
        if (size < 0) {
            PyErr_BadInternalCall();
            return NULL;
        }
    #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;
        }
        if (size < PyTuple_MAXSAVESIZE && (op = free_list[size]) != NULL) {
            free_list[size] = (PyTupleObject *) op->ob_item[0];  /*从空闲链表中取一个长度相等的空闲的元组*/
            numfree[size]--;
    #ifdef COUNT_ALLOCS
            fast_tuple_allocs++;
    #endif
            /* Inline PyObject_InitVar */
    #ifdef Py_TRACE_REFS
            Py_SIZE(op) = size;
            Py_TYPE(op) = &PyTuple_Type;
    #endif
            _Py_NewReference((PyObject *)op);
        }
        else
    #endif
        {
            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;
        }
        for (i=0; i < size; i++)
            op->ob_item[i] = NULL;
    #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;
    }

    删除元组:

    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)
    }

    通过上述代码可以发现,在CPython中实际上只会对空的元组使用共享策略。并且空的元组只要创建了就永远不会回收其内存,因为为其引用计数特别的+1,导致空元组的引用计数永远不会为0。

    而对于小的元组(长度小于PyTuple_MAXSAVESIZE=20),python有池机制。当这类元组触发回收时,会先考虑将其链入一个空闲链表。具体操作是,先根据元组的长度,找到对应的空闲链表,然后查看这个空闲链表的当前已经有多少个元组(数组numfree记录链表元素个数),如果未超过上限(上限为PyTuple_MAXFREELIST=2000),则将其链入,否则直接释放其内存。

    这样看来,python是几乎没有元组的共享策略。

  • 相关阅读:
    magent + memcached 集群测试
    SQL Server 2000/2005检测存储过程名是否存在,存在删除
    ASP.NET在线用户列表精确版——解决用户意外退出在线列表无法及时更新问题
    使用asp.net/c# ajax 乱码的解决办法
    清空删除mssql数据库日志原文网址:http://admin.88443.net/article.
    net2.0下的简繁转换
    创建和注册自定义 httpModules 模块
    监控用户是否关闭浏览器
    Asp.net(Ajax)表单验证 函数包
    IE自带的网页过渡特效
  • 原文地址:https://www.cnblogs.com/adinosaur/p/7487549.html
Copyright © 2011-2022 走看看