Archive for Juni, 2012

XML parser performance in PyPy

Montag, Juni 25th, 2012

I recently showed some benchmark results comparing the XML parser performance in CPython 3.3 to that in PyPy 1.7. Here’s an update for PyPy 1.9 that also includes the current state of the lxml port to that platform, parsing a 3.4MB document style XML file.

CPython 3.3pre:

Initial Memory usage: 11332
xml.etree.ElementTree.parse done in 0.041 seconds
Memory usage: 21468 (+10136)
xml.etree.cElementTree.parse done in 0.041 seconds
Memory usage: 21464 (+10132)
xml.etree.cElementTree.XMLParser.feed(): 25317 nodes read in 0.041 seconds
Memory usage: 21736 (+10404)
lxml.etree.parse done in 0.032 seconds
Memory usage: 28324 (+16992)
drop_whitespace.parse done in 0.030 seconds
Memory usage: 25172 (+13840)
lxml.etree.XMLParser.feed(): 25317 nodes read in 0.037 seconds
Memory usage: 30608 (+19276)
minidom tree read in 0.492 seconds
Memory usage: 29852 (+18520)

PyPy without JIT warming:

Initial Memory usage: 42156
xml.etree.ElementTree.parse done in 0.452 seconds
Memory usage: 44084 (+1928)
xml.etree.cElementTree.parse done in 0.450 seconds
Memory usage: 44080 (+1924)
xml.etree.cElementTree.XMLParser.feed(): 25317 nodes read in 0.457 seconds
Memory usage: 47920 (+5768)
lxml.etree.parse done in 0.033 seconds
Memory usage: 58688 (+16536)
drop_whitespace.parse done in 0.033 seconds
Memory usage: 55536 (+13384)
lxml.etree.XMLParser.feed(): 25317 nodes read in 0.055 seconds
Memory usage: 64724 (+22564)
minidom tree read in 0.541 seconds
Memory usage: 59456 (+17296)

PyPy with JIT warmup:

Initial Memory usage: 646824
xml.etree.ElementTree.parse done in 0.341 seconds
xml.etree.cElementTree.parse done in 0.345 seconds
xml.etree.cElementTree.XMLParser.feed(): 25317 nodes read in 0.342 seconds
lxml.etree.parse done in 0.026 seconds
drop_whitespace.parse done in 0.025 seconds
lxml.etree.XMLParser.feed(): 25317 nodes read in 0.039 seconds
minidom tree read in 0.383 seconds

What you can quickly see is that lxml performs equally well on both (actually slightly faster on PyPy) and beats the other libraries on PyPy by more than an order of magnitude. The absolute numbers are fairly low, though, way below a second for the 3.4MB file. It’ll be interesting to see some more complete benchmarks at some point that also take some realistic processing into account.

Remark: cElementTree is just an alias for the plain Python ElementTree on PyPy and ElementTree uses cElementTree in the background in CPython 3.3, which is why both show the same performance. The memory sizes were measured in forked processes, whereas the PyPy JIT numbers were measured in a repeatedly running process in order to take advantage of the JIT compiler. Note the substantially higher memory load of PyPy here.

Update: I originally reported the forked memory size with the non-forked performance for PyPy. The above now shows both separately. A more real-world comparison would likely yield an even higher memory usage on PyPy than the numbers above, which were mostly meant to give an idea of the memory usage of the in-memory tree (i.e. the data impact).