62
задан 23 May 2017 в 14:45

2 ответа

Скажем, у Вас есть этот код для создания строки от трех строк:

x = 'foo'
x += 'bar'  # 'foobar'
x += 'baz'  # 'foobarbaz'

В этом случае, Python сначала должен выделить и создать 'foobar', прежде чем он сможет выделить и создать 'foobarbaz'.

Так для каждого +=, который называют, все содержание строки и независимо от того, что становится добавленным к ней, должно быть скопировано в совершенно новый буфер памяти. Другими словами, если Вы имеете N строки, к которым присоединятся, необходимо выделить приблизительно N, временные строки и первая подстрока копируются ~N времена. Последняя подстрока только копируется однажды, но в среднем, каждая подстрока становится скопированной ~N/2 времена.

С .join, Python может играть много приемов, так как промежуточные строки не должны быть созданы. CPython выясняет, в каком количестве памяти он нуждается впереди и затем выделяет правильно измеренный буфер. Наконец, это затем копирует каждую часть в новый буфер, что означает, что каждая часть только копируется однажды.

<час>

существуют другие жизнеспособные подходы, которые могли привести к лучшей производительности для += в некоторых случаях. Например, если внутреннее строковое представление на самом деле rope или если время выполнения достаточно на самом деле умно, чтобы так или иначе выяснить, что временные строки бесполезны к программе и оптимизируют их далеко.

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

75
ответ дан 31 October 2019 в 14:20

Я думаю, что это поведение лучше всего объяснено в строковая буферная глава .

Lua Для перезаписи того объяснения в контексте Python, давайте запустимся с невинного фрагмента кода (производная той в документах Lua):

s = ""
for l in some_list:
  s += l

Предполагают, что каждый l - 20 байтов, и эти s был уже проанализирован к размеру 50 КБ. Когда Python конкатенирует s + l, он создает новую строку с 50 020 байтами и копирует 50 КБ от s в эту новую строку. Таким образом, для каждой новой строки программа перемещает 50 КБ памяти и рост. После чтения 100 новых строк (только 2 КБ) отрывок уже переместил больше чем 5 МБ памяти. Ко всем неприятностям после присвоения

s += l

старая строка является теперь мусором. После двух циклов цикла существует две старых строки, делающие в общей сложности больше чем 100 КБ мусора. Так, языковой компилятор решает запустить свой сборщик "мусора" и освобождает тех 100 КБ. Проблема состоит в том, что это произойдет, каждые два цикла и программа запустят ее сборщик "мусора" две тысячи раз прежде, чем прочитать целый список. Даже со всей этой работой, ее использование памяти будет большим несколько из размера списка.

И, в конце:

Эта проблема не специфична для Lua: Другие языки с истинной сборкой "мусора", и где строки являются неизменными объектами, представляют подобное поведение, при этом Java является самым известным примером. (Java предлагает структуру StringBuffer для улучшения проблемы.)

строки Python также неизменные объекты .

7
ответ дан 31 October 2019 в 14:20

Другие вопросы по тегам:

Похожие вопросы: