2013-06-18 3 views
2

Я пытаюсь найти наиболее эффективный способ создания длинной байтовой строки (или bytearray) путем конкатенации нескольких коротких строк, когда длина всей строки известна заранее. Я сделал этот сценарий и придумали с этими результатами:Python bytearray verses list of bytes

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) создает список байтов и с помощью объединения() метод путь, который подразумевается под результатами?

2) Почему использование списка байтов намного быстрее, чем использование bytearray, похоже, что оно предназначено для такого типа вещей?

3) Вы могли бы подумать, что инициализированный массив будет быстрее, чем неинициализированный массив, потому что инициализированный массив не нужно изменять размер (обратите внимание, что он иногда работал лучше, но не так много и непоследовательно). Не быстрее ли это из-за операции нарезки?

ответ

2

Первая функция создает список указателей на один и тот же объект (НЕ список байтов), тогда join выполнит одно выделение памяти и вызовет COUNT на memcpy.

Вы можете сделать первую функцию раз быстрее (5x в моем тесте), понижая временный список и с помощью itertools.repeat:

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, хранит данные байт за байтом и имеет многократно перераспределять память как ByteArray растет.

Вы можете сделать вторую функцию почти так же быстро, как оригинал (неоптимизированном) первой заменой итерации с одним вызовом extend:

def bytearray_test_opt(): 
    tStart = time.clock() 
    ba = bytearray() 
    for i in range(COUNT): 
     ba.extend(MSG) 
    print('array opt time:', time.clock() - tStart) 

После этой модификации вторая функция будет медленнее, чем первый один только из-за дополнительных перераспределений (~ 15% в моем тесте).

В третьей функции используется назначение slice bytearray, которое допускает итерацию и, похоже, выполняет одну и ту же байтовую байтовую итерацию, не признавая, что они могут только memcpy байт в место. Это выглядит как дефект в стандартной библиотеке, который может быть исправлен.

Как видно из предыдущей оптимизации, распределения занимают очень мало времени по сравнению с байтовым копированием, поэтому предварительное распределение не оказывает заметного влияния здесь. Вы можете сэкономить время делать меньше вычислений, но это не поможет значительно либо:

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 

P.S.Используйте модуль timeit для выполнения таких мер, который обеспечивает гораздо более высокую точность.