Как избежать ненужного копирования фрагментов в Python - PullRequest
6 голосов
/ 24 февраля 2010

Существует ли распространенная идиома, позволяющая избежать бессмысленного копирования фрагментов в таких случаях:

>>> a = bytearray(b'hello')
>>> b = bytearray(b'goodbye, cruel world.')
>>> a.extend(b[14:20])
>>> a
bytearray(b'hello world')

Мне кажется, что при создании среза b[14:20] происходит ненужная копия. Вместо того, чтобы создавать новый фрагмент в памяти, чтобы дать extend, я хочу сказать «использовать только этот диапазон текущего объекта».

Некоторые методы помогут вам с параметрами среза, например count:

>>> a = bytearray(1000000)       # a million zero bytes
>>> a[0:900000].count(b'\x00')   # expensive temporary slice
900000
>>> a.count(b'\x00', 0, 900000)  # helpful start and end parameters
900000

но многие, как extend в моем первом примере, не имеют этой функции.

Я понимаю, что для многих приложений речь идет о микрооптимизации, поэтому, прежде чем кто-либо спросит, да, я профилировал свое приложение, и для моего случая это стоит беспокоиться.

У меня есть одно «решение» ниже, но любые лучшие идеи приветствуются.

Ответы [ 2 ]

5 голосов
/ 24 февраля 2010

Создание объекта buffer позволяет избежать копирования фрагмента, но для коротких фрагментов эффективнее просто сделать копию:

>>> a.extend(buffer(b, 14, 6))
>>> a
bytearray(b'hello world')

Здесь только одна копия сделана из памяти, но стоимость создания объекта buffer больше, чем стирает экономию. Это должно быть лучше для больших ломтиков, хотя. Я не уверен, насколько большим должен быть срез, чтобы этот метод был более эффективным в целом.

Обратите внимание, что для Python 3 (и, возможно, в Python 2.7) вам потребуется объект memoryview:

>>> a.extend(memoryview(b)[14:20])
2 голосов
/ 24 февраля 2010

itertools имеет islice. У islice нет метода подсчета, поэтому он полезен в других случаях, когда вы хотите избежать копирования фрагмента. Как вы указали - у графа есть механизм для этого в любом случае

>>> from itertools import islice
>>> a = bytearray(1000000)
>>> sum(1 for x in islice(a,0,900000) if x==0)
900000
>>> len(filter(b'\x00'.__eq__,islice(a,0,900000)))
900000

>>> a=bytearray(b"hello")
>>> b = bytearray(b'goodbye, cruel world.')
>>> a.extend(islice(b,14,20))
>>> a
bytearray(b'hello world')
...