summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Behnel <stefan_ml@behnel.de>2021-07-04 23:38:10 +0200
committerStefan Behnel <stefan_ml@behnel.de>2021-07-04 23:45:29 +0200
commit32d52bee3ea4117b0fcb4dab994b707c7aba9d3a (patch)
treef2b6dad24a7676ee89887e2f34cadb8d94a1900e
parent885765dc99124199e686b9fabd162872624dfbf0 (diff)
downloadpython-lxml-32d52bee3ea4117b0fcb4dab994b707c7aba9d3a.tar.gz
Update benchmark results in doc/performance.txt to lxml 4.6.3.
-rw-r--r--doc/performance.txt297
1 files changed, 145 insertions, 152 deletions
diff --git a/doc/performance.txt b/doc/performance.txt
index 1a0c9ad6..6e01812b 100644
--- a/doc/performance.txt
+++ b/doc/performance.txt
@@ -88,18 +88,11 @@ very easy to add as tiny test methods, so if you write a performance test for
a specific part of the API yourself, please consider sending it to the lxml
mailing list.
-The timings presented below compare lxml 3.1.1 (with libxml2 2.9.0) to the
+The timings presented below compare lxml 4.6.3 (with libxml2 2.9.10) to the
latest released versions of ElementTree (with cElementTree as accelerator
-module) in the standard library of CPython 3.3.0. They were run
-single-threaded on a 2.9GHz 64bit double core Intel i7 machine under
-Ubuntu Linux 12.10 (Quantal). The C libraries were compiled with the
-same platform specific optimisation flags. The Python interpreter was
-also manually compiled for the platform. Note that many of the following
-ElementTree timings are therefore better than what a normal Python
-installation with the standard library (c)ElementTree modules would yield.
-Note also that CPython 2.7 and 3.2+ come with a newer ElementTree version,
-so older Python installations will not perform as good for (c)ElementTree,
-and sometimes substantially worse.
+module) in the standard library of CPython 3.8.10. They were run
+single-threaded on a 2.3GHz 64bit double core Intel i5 machine under
+Ubuntu Linux 20.04 (Focal).
.. _`bench_etree.py`: https://github.com/lxml/lxml/blob/master/benchmark/bench_etree.py
.. _`bench_xpath.py`: https://github.com/lxml/lxml/blob/master/benchmark/bench_xpath.py
@@ -141,50 +134,50 @@ is native to libxml2. While 20 to 40 times faster than (c)ElementTree
lxml is still more than 10 times as fast as the much improved
ElementTree 1.3 in recent Python versions::
- lxe: tostring_utf16 (S-TR T1) 7.9958 msec/pass
- cET: tostring_utf16 (S-TR T1) 83.1358 msec/pass
+ lxe: tostring_utf16 (S-TR T1) 5.8763 msec/pass
+ cET: tostring_utf16 (S-TR T1) 38.0461 msec/pass
- lxe: tostring_utf16 (UATR T1) 8.3222 msec/pass
- cET: tostring_utf16 (UATR T1) 84.4688 msec/pass
+ lxe: tostring_utf16 (UATR T1) 6.0940 msec/pass
+ cET: tostring_utf16 (UATR T1) 37.8058 msec/pass
- lxe: tostring_utf16 (S-TR T2) 8.2297 msec/pass
- cET: tostring_utf16 (S-TR T2) 87.3415 msec/pass
+ lxe: tostring_utf16 (S-TR T2) 6.1204 msec/pass
+ cET: tostring_utf16 (S-TR T2) 40.0257 msec/pass
- lxe: tostring_utf8 (S-TR T2) 6.5677 msec/pass
- cET: tostring_utf8 (S-TR T2) 76.2064 msec/pass
+ lxe: tostring_utf8 (S-TR T2) 4.7486 msec/pass
+ cET: tostring_utf8 (S-TR T2) 30.3330 msec/pass
- lxe: tostring_utf8 (U-TR T3) 1.1952 msec/pass
- cET: tostring_utf8 (U-TR T3) 22.0058 msec/pass
+ lxe: tostring_utf8 (U-TR T3) 1.2028 msec/pass
+ cET: tostring_utf8 (U-TR T3) 8.9505 msec/pass
The difference is somewhat smaller for plain text serialisation::
- lxe: tostring_text_ascii (S-TR T1) 2.7738 msec/pass
- cET: tostring_text_ascii (S-TR T1) 4.7629 msec/pass
+ lxe: tostring_text_ascii (S-TR T1) 2.4126 msec/pass
+ cET: tostring_text_ascii (S-TR T1) 3.1371 msec/pass
- lxe: tostring_text_ascii (S-TR T3) 0.8273 msec/pass
- cET: tostring_text_ascii (S-TR T3) 1.5273 msec/pass
+ lxe: tostring_text_ascii (S-TR T3) 0.8945 msec/pass
+ cET: tostring_text_ascii (S-TR T3) 1.2043 msec/pass
- lxe: tostring_text_utf16 (S-TR T1) 2.7659 msec/pass
- cET: tostring_text_utf16 (S-TR T1) 10.5038 msec/pass
+ lxe: tostring_text_utf16 (S-TR T1) 2.5816 msec/pass
+ cET: tostring_text_utf16 (S-TR T1) 7.3011 msec/pass
- lxe: tostring_text_utf16 (U-TR T1) 2.8017 msec/pass
- cET: tostring_text_utf16 (U-TR T1) 10.5207 msec/pass
+ lxe: tostring_text_utf16 (U-TR T1) 2.7902 msec/pass
+ cET: tostring_text_utf16 (U-TR T1) 7.4139 msec/pass
The ``tostring()`` function also supports serialisation to a Python
unicode string object, which is currently faster in ElementTree
-under CPython 3.3::
+under CPython 3.8::
- lxe: tostring_text_unicode (S-TR T1) 2.6896 msec/pass
- cET: tostring_text_unicode (S-TR T1) 1.0056 msec/pass
+ lxe: tostring_text_unicode (S-TR T1) 2.5883 msec/pass
+ cET: tostring_text_unicode (S-TR T1) 1.1873 msec/pass
- lxe: tostring_text_unicode (U-TR T1) 2.7366 msec/pass
- cET: tostring_text_unicode (U-TR T1) 1.0154 msec/pass
+ lxe: tostring_text_unicode (U-TR T1) 2.8777 msec/pass
+ cET: tostring_text_unicode (U-TR T1) 1.1592 msec/pass
- lxe: tostring_text_unicode (S-TR T3) 0.7997 msec/pass
- cET: tostring_text_unicode (S-TR T3) 0.3154 msec/pass
+ lxe: tostring_text_unicode (S-TR T3) 0.6495 msec/pass
+ cET: tostring_text_unicode (S-TR T3) 0.4494 msec/pass
- lxe: tostring_text_unicode (U-TR T4) 0.0048 msec/pass
- cET: tostring_text_unicode (U-TR T4) 0.0160 msec/pass
+ lxe: tostring_text_unicode (U-TR T4) 0.0050 msec/pass
+ cET: tostring_text_unicode (U-TR T4) 0.0131 msec/pass
For parsing, lxml.etree and cElementTree compete for the medal.
Depending on the input, either of the two can be faster. The (c)ET
@@ -192,14 +185,14 @@ libraries use a very thin layer on top of the expat parser, which is
known to be very fast. Here are some timings from the benchmarking
suite::
- lxe: parse_bytesIO (SAXR T1) 13.0246 msec/pass
- cET: parse_bytesIO (SAXR T1) 8.2929 msec/pass
+ lxe: parse_bytesIO (SAXR T1) 15.2328 msec/pass
+ cET: parse_bytesIO (SAXR T1) 7.5498 msec/pass
- lxe: parse_bytesIO (S-XR T3) 1.3542 msec/pass
- cET: parse_bytesIO (S-XR T3) 2.4023 msec/pass
+ lxe: parse_bytesIO (S-XR T3) 1.5039 msec/pass
+ cET: parse_bytesIO (S-XR T3) 2.1725 msec/pass
- lxe: parse_bytesIO (UAXR T3) 7.5610 msec/pass
- cET: parse_bytesIO (UAXR T3) 11.2455 msec/pass
+ lxe: parse_bytesIO (UAXR T3) 8.7409 msec/pass
+ cET: parse_bytesIO (UAXR T3) 12.4905 msec/pass
And another couple of timings `from a benchmark`_ that Fredrik Lundh
`used to promote cElementTree`_, comparing a number of different
@@ -277,26 +270,26 @@ rather close to each other, usually within a factor of two, with
winners well distributed over both sides. Similar timings can be
observed for the ``iterparse()`` function::
- lxe: iterparse_bytesIO (SAXR T1) 17.9198 msec/pass
- cET: iterparse_bytesIO (SAXR T1) 14.4982 msec/pass
+ lxe: iterparse_bytesIO (SAXR T1) 20.9262 msec/pass
+ cET: iterparse_bytesIO (SAXR T1) 10.3736 msec/pass
- lxe: iterparse_bytesIO (UAXR T3) 8.8522 msec/pass
- cET: iterparse_bytesIO (UAXR T3) 12.9857 msec/pass
+ lxe: iterparse_bytesIO (UAXR T3) 11.0531 msec/pass
+ cET: iterparse_bytesIO (UAXR T3) 13.2461 msec/pass
However, if you benchmark the complete round-trip of a serialise-parse
cycle, the numbers will look similar to these::
- lxe: write_utf8_parse_bytesIO (S-TR T1) 19.8867 msec/pass
- cET: write_utf8_parse_bytesIO (S-TR T1) 80.7259 msec/pass
+ lxe: write_utf8_parse_bytesIO (S-TR T1) 19.3429 msec/pass
+ cET: write_utf8_parse_bytesIO (S-TR T1) 35.5511 msec/pass
- lxe: write_utf8_parse_bytesIO (UATR T2) 23.7896 msec/pass
- cET: write_utf8_parse_bytesIO (UATR T2) 98.0766 msec/pass
+ lxe: write_utf8_parse_bytesIO (UATR T2) 22.8314 msec/pass
+ cET: write_utf8_parse_bytesIO (UATR T2) 42.3915 msec/pass
- lxe: write_utf8_parse_bytesIO (S-TR T3) 3.0684 msec/pass
- cET: write_utf8_parse_bytesIO (S-TR T3) 24.6122 msec/pass
+ lxe: write_utf8_parse_bytesIO (S-TR T3) 3.4230 msec/pass
+ cET: write_utf8_parse_bytesIO (S-TR T3) 11.1156 msec/pass
- lxe: write_utf8_parse_bytesIO (SATR T4) 0.3495 msec/pass
- cET: write_utf8_parse_bytesIO (SATR T4) 1.9610 msec/pass
+ lxe: write_utf8_parse_bytesIO (SATR T4) 0.4215 msec/pass
+ cET: write_utf8_parse_bytesIO (SATR T4) 0.9992 msec/pass
For applications that require a high parser throughput of large files,
and that do little to no serialization, both cET and lxml.etree are a
@@ -379,30 +372,30 @@ The same tree overhead makes operations like collecting children as in
a shallow copy of their list of children, lxml has to create a Python
object for each child and collect them in a list::
- lxe: root_list_children (--TR T1) 0.0038 msec/pass
- cET: root_list_children (--TR T1) 0.0010 msec/pass
+ lxe: root_list_children (--TR T1) 0.0033 msec/pass
+ cET: root_list_children (--TR T1) 0.0007 msec/pass
- lxe: root_list_children (--TR T2) 0.0455 msec/pass
- cET: root_list_children (--TR T2) 0.0050 msec/pass
+ lxe: root_list_children (--TR T2) 0.0596 msec/pass
+ cET: root_list_children (--TR T2) 0.0055 msec/pass
This handicap is also visible when accessing single children::
- lxe: first_child (--TR T2) 0.0424 msec/pass
- cET: first_child (--TR T2) 0.0384 msec/pass
+ lxe: first_child (--TR T2) 0.0615 msec/pass
+ cET: first_child (--TR T2) 0.0548 msec/pass
- lxe: last_child (--TR T1) 0.0477 msec/pass
- cET: last_child (--TR T1) 0.0467 msec/pass
+ lxe: last_child (--TR T1) 0.0603 msec/pass
+ cET: last_child (--TR T1) 0.0563 msec/pass
... unless you also add the time to find a child index in a bigger
list. ET and cET use Python lists here, which are based on arrays.
The data structure used by libxml2 is a linked tree, and thus, a
linked list of children::
- lxe: middle_child (--TR T1) 0.0710 msec/pass
- cET: middle_child (--TR T1) 0.0420 msec/pass
+ lxe: middle_child (--TR T1) 0.0918 msec/pass
+ cET: middle_child (--TR T1) 0.0513 msec/pass
- lxe: middle_child (--TR T2) 1.7393 msec/pass
- cET: middle_child (--TR T2) 0.0396 msec/pass
+ lxe: middle_child (--TR T2) 2.3277 msec/pass
+ cET: middle_child (--TR T2) 0.0484 msec/pass
Element creation
@@ -412,18 +405,18 @@ As opposed to ET, libxml2 has a notion of documents that each element must be
in. This results in a major performance difference for creating independent
Elements that end up in independently created documents::
- lxe: create_elements (--TC T2) 1.0045 msec/pass
- cET: create_elements (--TC T2) 0.0753 msec/pass
+ lxe: create_elements (--TC T2) 0.8178 msec/pass
+ cET: create_elements (--TC T2) 0.0668 msec/pass
Therefore, it is always preferable to create Elements for the document they
are supposed to end up in, either as SubElements of an Element or using the
explicit ``Element.makeelement()`` call::
- lxe: makeelement (--TC T2) 1.0586 msec/pass
- cET: makeelement (--TC T2) 0.1483 msec/pass
+ lxe: makeelement (--TC T2) 0.8020 msec/pass
+ cET: makeelement (--TC T2) 0.0618 msec/pass
- lxe: create_subelements (--TC T2) 0.8826 msec/pass
- cET: create_subelements (--TC T2) 0.0827 msec/pass
+ lxe: create_subelements (--TC T2) 0.7782 msec/pass
+ cET: create_subelements (--TC T2) 0.0865 msec/pass
So, if the main performance bottleneck of an application is creating large XML
trees in memory through calls to Element and SubElement, cET is the best
@@ -440,11 +433,11 @@ requires lxml to do recursive adaptations throughout the moved tree structure.
The following benchmark appends all root children of the second tree to the
root of the first tree::
- lxe: append_from_document (--TR T1,T2) 1.0812 msec/pass
- cET: append_from_document (--TR T1,T2) 0.1104 msec/pass
+ lxe: append_from_document (--TR T1,T2) 1.3409 msec/pass
+ cET: append_from_document (--TR T1,T2) 0.0539 msec/pass
- lxe: append_from_document (--TR T3,T4) 0.0155 msec/pass
- cET: append_from_document (--TR T3,T4) 0.0060 msec/pass
+ lxe: append_from_document (--TR T3,T4) 0.0203 msec/pass
+ cET: append_from_document (--TR T3,T4) 0.0031 msec/pass
Although these are fairly small numbers compared to parsing, this easily shows
the different performance classes for lxml and (c)ET. Where the latter do not
@@ -455,19 +448,19 @@ with the size of the tree that is moved.
This difference is not always as visible, but applies to most parts of the
API, like inserting newly created elements::
- lxe: insert_from_document (--TR T1,T2) 3.9763 msec/pass
- cET: insert_from_document (--TR T1,T2) 0.1459 msec/pass
+ lxe: insert_from_document (--TR T1,T2) 4.9999 msec/pass
+ cET: insert_from_document (--TR T1,T2) 0.0696 msec/pass
or replacing the child slice by a newly created element::
- lxe: replace_children_element (--TC T1) 0.0749 msec/pass
- cET: replace_children_element (--TC T1) 0.0081 msec/pass
+ lxe: replace_children_element (--TC T1) 0.0653 msec/pass
+ cET: replace_children_element (--TC T1) 0.0098 msec/pass
as opposed to replacing the slice with an existing element from the
same document::
- lxe: replace_children (--TC T1) 0.0052 msec/pass
- cET: replace_children (--TC T1) 0.0036 msec/pass
+ lxe: replace_children (--TC T1) 0.0069 msec/pass
+ cET: replace_children (--TC T1) 0.0043 msec/pass
While these numbers are too small to provide a major performance
impact in practice, you should keep this difference in mind when you
@@ -481,14 +474,14 @@ deepcopy
Deep copying a tree is fast in lxml::
- lxe: deepcopy_all (--TR T1) 3.1650 msec/pass
- cET: deepcopy_all (--TR T1) 53.9973 msec/pass
+ lxe: deepcopy_all (--TR T1) 4.0150 msec/pass
+ cET: deepcopy_all (--TR T1) 2.4621 msec/pass
- lxe: deepcopy_all (-ATR T2) 3.7365 msec/pass
- cET: deepcopy_all (-ATR T2) 61.6267 msec/pass
+ lxe: deepcopy_all (-ATR T2) 4.7412 msec/pass
+ cET: deepcopy_all (-ATR T2) 2.8064 msec/pass
- lxe: deepcopy_all (S-TR T3) 0.7913 msec/pass
- cET: deepcopy_all (S-TR T3) 13.6220 msec/pass
+ lxe: deepcopy_all (S-TR T3) 1.1363 msec/pass
+ cET: deepcopy_all (S-TR T3) 0.5484 msec/pass
So, for example, if you have a database-like scenario where you parse in a
large tree and then search and copy independent subtrees from it for further
@@ -504,31 +497,31 @@ traversal of the XML tree and especially if few elements are of
interest or the target element tag name is known, the ``.iter()``
method is a good choice::
- lxe: iter_all (--TR T1) 1.0529 msec/pass
- cET: iter_all (--TR T1) 0.2635 msec/pass
+ lxe: iter_all (--TR T1) 1.3881 msec/pass
+ cET: iter_all (--TR T1) 0.2708 msec/pass
- lxe: iter_islice (--TR T2) 0.0110 msec/pass
- cET: iter_islice (--TR T2) 0.0050 msec/pass
+ lxe: iter_islice (--TR T2) 0.0124 msec/pass
+ cET: iter_islice (--TR T2) 0.0036 msec/pass
- lxe: iter_tag (--TR T2) 0.0079 msec/pass
- cET: iter_tag (--TR T2) 0.0112 msec/pass
+ lxe: iter_tag (--TR T2) 0.0105 msec/pass
+ cET: iter_tag (--TR T2) 0.0083 msec/pass
- lxe: iter_tag_all (--TR T2) 0.1822 msec/pass
- cET: iter_tag_all (--TR T2) 0.5343 msec/pass
+ lxe: iter_tag_all (--TR T2) 0.7262 msec/pass
+ cET: iter_tag_all (--TR T2) 0.4537 msec/pass
This translates directly into similar timings for ``Element.findall()``::
- lxe: findall (--TR T2) 1.7176 msec/pass
- cET: findall (--TR T2) 0.9973 msec/pass
+ lxe: findall (--TR T2) 4.0147 msec/pass
+ cET: findall (--TR T2) 0.9193 msec/pass
- lxe: findall (--TR T3) 0.3967 msec/pass
- cET: findall (--TR T3) 0.2525 msec/pass
+ lxe: findall (--TR T3) 0.4113 msec/pass
+ cET: findall (--TR T3) 0.2377 msec/pass
- lxe: findall_tag (--TR T2) 0.2258 msec/pass
- cET: findall_tag (--TR T2) 0.5770 msec/pass
+ lxe: findall_tag (--TR T2) 0.7253 msec/pass
+ cET: findall_tag (--TR T2) 0.4904 msec/pass
- lxe: findall_tag (--TR T3) 0.1085 msec/pass
- cET: findall_tag (--TR T3) 0.1919 msec/pass
+ lxe: findall_tag (--TR T3) 0.1092 msec/pass
+ cET: findall_tag (--TR T3) 0.1757 msec/pass
Note that all three libraries currently use the same Python
implementation for ``.findall()``, except for their native tree
@@ -548,38 +541,38 @@ provides more than one way of accessing it and you should take care which part
of the lxml API you use. The most straight forward way is to call the
``xpath()`` method on an Element or ElementTree::
- lxe: xpath_method (--TC T1) 0.3982 msec/pass
- lxe: xpath_method (--TC T2) 7.8895 msec/pass
- lxe: xpath_method (--TC T3) 0.0477 msec/pass
- lxe: xpath_method (--TC T4) 0.3982 msec/pass
+ lxe: xpath_method (--TC T1) 0.2763 msec/pass
+ lxe: xpath_method (--TC T2) 5.3439 msec/pass
+ lxe: xpath_method (--TC T3) 0.0315 msec/pass
+ lxe: xpath_method (--TC T4) 0.2587 msec/pass
This is well suited for testing and when the XPath expressions are as diverse
as the trees they are called on. However, if you have a single XPath
expression that you want to apply to a larger number of different elements,
the ``XPath`` class is the most efficient way to do it::
- lxe: xpath_class (--TC T1) 0.0713 msec/pass
- lxe: xpath_class (--TC T2) 1.1325 msec/pass
- lxe: xpath_class (--TC T3) 0.0215 msec/pass
- lxe: xpath_class (--TC T4) 0.0722 msec/pass
+ lxe: xpath_class (--TC T1) 0.0610 msec/pass
+ lxe: xpath_class (--TC T2) 0.6981 msec/pass
+ lxe: xpath_class (--TC T3) 0.0141 msec/pass
+ lxe: xpath_class (--TC T4) 0.0432 msec/pass
Note that this still allows you to use variables in the expression, so you can
parse it once and then adapt it through variables at call time. In other
cases, where you have a fixed Element or ElementTree and want to run different
expressions on it, you should consider the ``XPathEvaluator``::
- lxe: xpath_element (--TR T1) 0.1101 msec/pass
- lxe: xpath_element (--TR T2) 2.0473 msec/pass
- lxe: xpath_element (--TR T3) 0.0267 msec/pass
- lxe: xpath_element (--TR T4) 0.1087 msec/pass
+ lxe: xpath_element (--TR T1) 0.0598 msec/pass
+ lxe: xpath_element (--TR T2) 0.9737 msec/pass
+ lxe: xpath_element (--TR T3) 0.0167 msec/pass
+ lxe: xpath_element (--TR T4) 0.0606 msec/pass
While it looks slightly slower, creating an XPath object for each of the
expressions generates a much higher overhead here::
- lxe: xpath_class_repeat (--TC T1 ) 0.3884 msec/pass
- lxe: xpath_class_repeat (--TC T2 ) 7.6182 msec/pass
- lxe: xpath_class_repeat (--TC T3 ) 0.0465 msec/pass
- lxe: xpath_class_repeat (--TC T4 ) 0.3877 msec/pass
+ lxe: xpath_class_repeat (--TC T1 ) 0.2658 msec/pass
+ lxe: xpath_class_repeat (--TC T2 ) 5.0316 msec/pass
+ lxe: xpath_class_repeat (--TC T3 ) 0.0319 msec/pass
+ lxe: xpath_class_repeat (--TC T4 ) 0.2749 msec/pass
Note that tree iteration can be substantially faster than XPath if
your code short-circuits after the first couple of elements were
@@ -589,25 +582,25 @@ regardless of how much of it will actually be used.
Here is an example where only the first matching element is being
searched, a case for which XPath has syntax support as well::
- lxe: find_single (--TR T2) 0.0184 msec/pass
- cET: find_single (--TR T2) 0.0052 msec/pass
+ lxe: find_single (--TR T2) 0.0045 msec/pass
+ cET: find_single (--TR T2) 0.0029 msec/pass
- lxe: iter_single (--TR T2) 0.0024 msec/pass
- cET: iter_single (--TR T2) 0.0007 msec/pass
+ lxe: iter_single (--TR T2) 0.0019 msec/pass
+ cET: iter_single (--TR T2) 0.0005 msec/pass
- lxe: xpath_single (--TR T2) 0.0033 msec/pass
+ lxe: xpath_single (--TR T2) 0.0844 msec/pass
When looking for the first two elements out of many, the numbers
explode for XPath, as restricting the result subset requires a
more complex expression::
- lxe: iterfind_two (--TR T2) 0.0184 msec/pass
- cET: iterfind_two (--TR T2) 0.0062 msec/pass
+ lxe: iterfind_two (--TR T2) 0.0050 msec/pass
+ cET: iterfind_two (--TR T2) 0.0031 msec/pass
lxe: iter_two (--TR T2) 0.0029 msec/pass
- cET: iter_two (--TR T2) 0.0017 msec/pass
+ cET: iter_two (--TR T2) 0.0012 msec/pass
- lxe: xpath_two (--TR T2) 0.2768 msec/pass
+ lxe: xpath_two (--TR T2) 0.0706 msec/pass
A longer example
@@ -774,21 +767,21 @@ ObjectPath can be used to speed up the access to elements that are deep in the
tree. It avoids step-by-step Python element instantiations along the path,
which can substantially improve the access time::
- lxe: attribute (--TR T1) 4.1828 msec/pass
- lxe: attribute (--TR T2) 17.3802 msec/pass
- lxe: attribute (--TR T4) 3.8657 msec/pass
+ lxe: attribute (--TR T1) 2.6822 msec/pass
+ lxe: attribute (--TR T2) 16.4094 msec/pass
+ lxe: attribute (--TR T4) 2.4951 msec/pass
- lxe: objectpath (--TR T1) 0.9289 msec/pass
- lxe: objectpath (--TR T2) 13.3109 msec/pass
- lxe: objectpath (--TR T4) 0.9289 msec/pass
+ lxe: objectpath (--TR T1) 1.1985 msec/pass
+ lxe: objectpath (--TR T2) 14.7083 msec/pass
+ lxe: objectpath (--TR T4) 1.2503 msec/pass
- lxe: attributes_deep (--TR T1) 6.2900 msec/pass
- lxe: attributes_deep (--TR T2) 20.4713 msec/pass
- lxe: attributes_deep (--TR T4) 6.1679 msec/pass
+ lxe: attributes_deep (--TR T1) 3.9361 msec/pass
+ lxe: attributes_deep (--TR T2) 17.9017 msec/pass
+ lxe: attributes_deep (--TR T4) 3.7947 msec/pass
- lxe: objectpath_deep (--TR T1) 1.3049 msec/pass
- lxe: objectpath_deep (--TR T2) 14.0815 msec/pass
- lxe: objectpath_deep (--TR T4) 1.3051 msec/pass
+ lxe: objectpath_deep (--TR T1) 1.6170 msec/pass
+ lxe: objectpath_deep (--TR T2) 15.3167 msec/pass
+ lxe: objectpath_deep (--TR T4) 1.5836 msec/pass
Note, however, that parsing ObjectPath expressions is not for free either, so
this is most effective for frequently accessing the same element.
@@ -818,17 +811,17 @@ expressions to be more selective. By choosing the right trees (or even
subtrees and elements) to cache, you can trade memory usage against access
speed::
- lxe: attribute_cached (--TR T1) 3.1357 msec/pass
- lxe: attribute_cached (--TR T2) 15.8911 msec/pass
- lxe: attribute_cached (--TR T4) 2.9194 msec/pass
+ lxe: attribute_cached (--TR T1) 1.9312 msec/pass
+ lxe: attribute_cached (--TR T2) 15.1188 msec/pass
+ lxe: attribute_cached (--TR T4) 1.9250 msec/pass
- lxe: attributes_deep_cached (--TR T1) 3.8984 msec/pass
- lxe: attributes_deep_cached (--TR T2) 16.8300 msec/pass
- lxe: attributes_deep_cached (--TR T4) 3.6936 msec/pass
+ lxe: attributes_deep_cached (--TR T1) 2.6906 msec/pass
+ lxe: attributes_deep_cached (--TR T2) 16.4149 msec/pass
+ lxe: attributes_deep_cached (--TR T4) 2.5618 msec/pass
- lxe: objectpath_deep_cached (--TR T1) 0.7496 msec/pass
- lxe: objectpath_deep_cached (--TR T2) 12.3763 msec/pass
- lxe: objectpath_deep_cached (--TR T4) 0.7427 msec/pass
+ lxe: objectpath_deep_cached (--TR T1) 1.0054 msec/pass
+ lxe: objectpath_deep_cached (--TR T2) 14.3306 msec/pass
+ lxe: objectpath_deep_cached (--TR T4) 0.8924 msec/pass
Things to note: you cannot currently use ``weakref.WeakKeyDictionary`` objects
for this as lxml's element objects do not support weak references (which are