Python bytearray与字节列表相对应

时间:2013-06-18 07:08:14

标签: python list bytearray

我试图通过在事先知道整个字符串的长度时连接多个较短的字符串来找出创建长字节字符串(或bytearray)的最有效方法。我制作了这个脚本并得出了这些结果:

import time

MSG = b'test message'
COUNT = 30000

def bytes_list_test():  
    tStart = time.clock()
    l = []
    for i in range(COUNT):
        l.append(MSG)
    bs = b''.join(l)
    print('byte list time:', time.clock() - tStart)

def bytearray_test():
    tStart = time.clock()
    ba = bytearray()
    for i in range(COUNT):
        for c in MSG:
            ba.append(c)
    print('array time:', time.clock() - tStart)

def initialized_bytearray_test():
    tStart = time.clock()
    ba = bytearray([0x00]*len(MSG)*COUNT)
    for i in range(COUNT):
        ba[i*len(MSG):i*len(MSG)+len(MSG)] = MSG
    print('initialized array time:', time.clock() - tStart)

bytes_list_test()
bytearray_test()
initialized_bytearray_test()

结果:

byte list time:         0.0076534920117410365
array time:             0.08107178658246994
initialized array time: 0.08843219671325642

几个问题:

1)是否正在创建一个字节列表并使用join()方法按结果隐含的方式进行操作?

2)为什么使用字节列表要比使用bytearray快得多,这似乎是为这类事物设计的?

3)你会认为初始化的数组会比未初始化的数组更快,因为初始化的数组不需要调整大小(注意它偶尔执行得更好,但不是很多而且不一致)。由于切片操作,它不是更快吗?

1 个答案:

答案 0 :(得分:2)

第一个函数创建一个指向同一个对象的指针列表(不是字节列表),然后join会对COUNT进行一次内存分配和memcpy调用。

您可以通过删除临时列表并使用itertools.repeat来使第一个函数更快(在我的测试中为5倍):

def bytes_list_test_opt():  
    tStart = time.clock()
    bs = b''.join(itertools.repeat(MSG, COUNT))
    print('byte list opt time:', time.clock() - tStart)

或者,在这种特殊情况下,只需使用*bytes对象的 bs = MSG*COUNT 运算符即可:

MSG

第二个函数重复遍历extend,逐字节存储数据,并且随着bytearray的增长不得不重复重新分配内存。

你可以通过用def bytearray_test_opt(): tStart = time.clock() ba = bytearray() for i in range(COUNT): ba.extend(MSG) print('array opt time:', time.clock() - tStart) 的单个调用替换迭代来使第二个函数几乎与原始(未优化)第一个函数一样快:

bytearray

在此修改之后,第二个函数将比第一个函数慢,因为额外的重新分配(在我的测试中约为15%)。

第三个函数使用memcpy的slice赋值,它接受iterable并且似乎在进行相同的逐字节迭代,而不会认识到它们只能def initialized_bytearray_test_opt(): tStart = time.clock() L = len(MSG) ba = bytearray(L*COUNT) ofs = 0 for i in range(COUNT): ba[ofs : ofs+L] = MSG ofs += L print('initialized array opt time:', time.clock() - tStart) 个字节进入该位置。这看起来像标准库中的一个缺陷,可以修复。

从前面的优化中可以看出,与逐字节复制相比,分配花费的时间非常少,因此预分配在此处没有明显的影响。你可以节省一些时间进行更少的计算,但它也没有多大帮助:

byte list time: 0.004823000000000001
byte list opt time: 0.0008649999999999977
array time: 0.043324
array opt time: 0.005505999999999997
initialized array time: 0.05936899999999999
initialized array opt time: 0.040164000000000005

我机器的最终时间:

timeit

P.S。使用{{1}}模块执行这样的测量,它提供了更高的准确度。