【问题标题】:Serialize a group of integers using cython使用 cython 序列化一组整数
【发布时间】:2018-01-21 20:12:04
【问题描述】:

我在Pyrobuf 页面上看到了这个示例代码,它序列化一个整数的速度比struct.pack 快约3 倍:

def ser2(): 
    cdef int x = 42 
    return (<char *>&x)[:sizeof(int)]

我想知道如何为一组整数做到这一点。 我看到 cython 有int[:]array.array 类型,但我仍然不明白如何获取整数列表,并获得与struct.pack('i', *num_list) 相同(但更快)的结果。 map() 对我来说似乎没有更快的工作,我想知道应该怎么做。

【问题讨论】:

    标签: python serialization struct cython


    【解决方案1】:

    我假设您想加快以下速度(Python3):

    import struct 
    lst=list(range(100))  #any other size
    struct.pack('i'*len(lst), *lst)
    

    没有 struct 和 cython 你可以在 python 中实现如下:

    import array
    bytes(array.array('i', lst))
    

    然而这比struct-module 慢一些:

    >>> %timeit struct.pack('i'*len(lst), *lst)
    2.38 µs ± 9.48 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
    >>> %timeit bytes(array.array('i',lst))
    3.94 µs ± 92 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
    

    不过,cython 可用于加快array 的创建速度,有关文档,请参阅here(arrays)here(str/bytes)

     %%cython
     import array
     from cpython cimport array
     def ser_int_list(lst):
        cdef Py_ssize_t n=len(lst)
        cdef array.array res=array.array('i')
        array.resize(res, n)  #preallocate memory
        for i in range(n):
           res.data.as_ints[i]=lst[i]            #lst.__get__() needs Python-Integer, so let i be a python-integer (not cdef)
        return res.data.as_chars[:n*sizeof(int)] # str for python2, bytes for python3
    

    时序表现如下:

    #list_size    struct-code    cython-code    speed-up
         1           343 ns         238 ns        1.5
        10           619 ns         283 ns         2
       100          2.38 µs        2.38 µs        3.5
      1000          21.6 µs        5.11 µs         4
     10000           266 µs        47.5 µs        5.5 
    

    即cython 提供了一些加速,从用于小型列表的 1.5 到用于大型列表的 5.5

    也许这可以进一步调整,但我希望你明白。


    测试代码:

    import struct
    for n in [1, 10,10**2, 10**3, 10**4]:
        print ("N=",n)
        lst=list(range(n))
        print("struct:")
        %timeit struct.pack('i'*len(lst), *lst)
        print("cython:")
        %timeit ser_int_list(lst)
    

    【讨论】:

    • 谢谢!但是,bytes(res) 不是我需要的,而是 res.tostring()。您能否建议我如何使用与单个 int 类似的转换技巧来“获取”底层字节?我怀疑它可能会更快
    • @Jay 抱歉,我假设是 Python3。我编辑了我的答案,因此它适用于 Python2(返回 str)和 Python3(返回字节)。但是,和int-example一样,内存是在后台复制的。用str
    • @Jay 如果你想避免复制,我认为你需要有一个稍微不同的界面——我认为你不能用str 做到这一点。但是,您应该先计时,以检查它是否值得:我猜测非常小的列表的开销可能在 10% 左右,而对于较大的列表则更少。
    • 谢谢,现在可以了。不幸的是,在尝试这个时,我没有看到 cython 函数和 python 自己的 array.array('i', lst).tostring() 之间的时间差。猜猜 cython 在这种特定情况下不会增加任何好处,尽管 cython 技术非常有用
    猜你喜欢
    • 2019-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-31
    • 1970-01-01
    相关资源
    最近更新 更多