Slepování stringů v Pythonu

2. 4. 2007 12:22 (aktualizováno) beda

Každý kdo se alespoň trochu vážně zabývá programováním v Pythonu asi narazil na doporučení neslepovat větší množství stringů dohromady pomocí „+“. Doporučená metoda je ukládat řetězce do listu a ten potom jednoduše slepit pomocí "".join( list_stringu), popř použít cStringIO nebo StringIO.
Kolega nedávno zkoušel měřit jaký je mezi těmito metodami rozdíl a překvapivě byly všechny výsledky velmi podobné. Po krátkém pátrání jsem narazil na vysvětlení – v Pythonu 2.4 bylo slepování pomocí „+“ optimalizováno. Rychlá zkouška se starší verzí Pythonu potvrdila, že v Pythonu 2.3 a nižších se slepování pomocí „+“ skutečně protáhne. Optimalizace v Pythonu 2.4 je navíc implementována pouze v klasickém C-Pythonu a nemusí být dostupná v ostatních implementacích, jako je Jython etc.
Podle mého názoru spočívá problém starších verzí v tom, že se při slepování celý string zkopíruje na nové místo v paměti a ten starý je při nejbližší příležitosti požrán garbage collectorem.
Na závěr porovnání výsledků pro různé verze Pythonu a různé metody slepování stringů.
Nejdřív jednoduchý testovací kód:

   import cStringIO
import StringIO
from time import time

t = time()
s1 = ""
for i in range( 50000):
s1 += "sasasdfasfasdfwaw211EWEFC235V23C"
print "+: ", time()-t


t = time()
s1 = []
for i in range( 50000):
s1.append( "sasasdfasfasdfwaw211EWEFC235V23C")
"".join( s1)
print "join: ", time()-t


t = time()
s1 = cStringIO.StringIO()
for i in range( 50000):
s1.write("sasasdfasfasdfwaw211EWEFC235V23C")
s1.getvalue()
print "cStringIO:", time()-t


t = time()
s1 = StringIO.StringIO()
for i in range( 50000):
s1.write("sasasdfasfasdfwaw211EWEFC235V23C")
s1.getvalue()
print "StringIO: ", time()-t

Výsledky pro Python 2.4:

   +:         0.050950050354
join: 0.0404708385468
cStringIO: 0.0536410808563
StringIO: 0.226967096329

Výsledky pro Python 2.3:

   +:         65.8520188332
join: 0.0464727878571
cStringIO: 0.0614099502563
StringIO: 0.233929157257

Tyto výsledky jsou pro případ kdy se 50000 slepuje za sebe stejný krátký řetězec. To není zas tak moc ve chvíli kdy např. zpracováváte automaticky nějaké textové dokumenty apod. Navíc sčítání stringů ve starších verzích Pythonu vykazuje značně nelineární nárůst času s velikostí stringů. Když uvedený kód spustíte s 2× více opakováními, čas se protáhne cca 4×.

Závěr

I když je asi stále jistější pro slepování většího množství stringů používat "".join() nebo cStringIO(), slepování pomocí „+“ je pro novější C-Python srovnatelné. Použití StringIO oproti cStringIO se pro tento případ rozhodně nevyplatí – StringIO je cca 4× pomalejší.

Sdílet