[PATCH] hashtable insert enhancement
François Dumont
frs.dumont@gmail.com
Sat Jan 7 22:09:00 GMT 2012
Hello
Here is a patchfor the hashtable implementation, the last modifying
hashtable data model I swear :-). Here are the important points of this
patch:
- _M_begin_bucket_index that used to be the index of the first non-empty
bucket has been replaced by _M_before_begin which a node before the
hashtable begin node. Thanks to this modification the hashtable
implementation is much more consistent because now all non-empty buckets
are containing their before-begin node; previously the first non-empty
bucket was containing its begin node rather than the before-begin. It is
a rather logical modification because, as documented, the hashtable is
the aggregation of a std::forward_list and a
std::vector<std::forward_list::iterator>. A std::forward_list contains
its before begin node, so does the hashtable now.
- I used to consider that hashtable nodes had to be ordered according to
their bucket index. This is in fact useless and worst a performance
issue because previously, when I had to add a node in an empty bucket, I
need to look for the previous not-empty bucket to find the node to link
after. With this patch, when inserting in an empty bucket, the new node
is simply inserted at the beginning of the forward_list and the bucket
then contain the forward_list before-begin. This is a constant time
operation while previously it was O(N) with N the number of empty
buckets before the targeted bucket.
- I have introduced a _M_find_before_node method, called by
_M_find_node, that returns the node before an equivalent one. This way I
can insert node after it without taking care of computing the next node
bucket to potentially update it, see what was done in _M_insert_after
for additional details.
Here is the result on the performance test
performance/23_containers/insert_erase/41975.cc:
Before patch:
41975.cc unordered_set<int> without cache: first
insert 9r 7u 0s 8022176mem 0pf
41975.cc unordered_set<int> without cache: erase
from iterator 1r 1u 0s -6400160mem 0pf
41975.cc unordered_set<int> without cache: second
insert 95r 94u 0s 6397600mem 0pf
41975.cc unordered_set<int> without cache: erase
from key 1r 1u 0s -6400320mem 0pf
41975.cc unordered_set<int> with cache: first
insert 7r 7u 0s 8019552mem 0pf
41975.cc unordered_set<int> with cache: erase from
iterator 1r 1u 0s -6400256mem 0pf
41975.cc unordered_set<int> with cache: second
insert 95r 95u 0s 6400304mem 0pf
41975.cc unordered_set<int> with cache: erase from
key 1r 0u 0s -6400304mem 0pf
41975.cc unordered_set<string> without cache: first
insert 55r 54u 0s 8019536mem 0pf
41975.cc unordered_set<string> without cache: erase
from iterator 7r 7u 0s -6400240mem 0pf
41975.cc unordered_set<string> without cache: second
insert 56r 55u 1s 6400272mem 0pf
41975.cc unordered_set<string> without cache: erase
from key 6r 7u 0s -6400272mem 0pf
41975.cc unordered_set<string> with cache: first
insert 22r 20u 0s 8019536mem 0pf
41975.cc unordered_set<string> with cache: erase
from iterator 4r 4u 0s -6400240mem 0pf
41975.cc unordered_set<string> with cache: second
insert 22r 21u 1s 6400288mem 0pf
41975.cc unordered_set<string> with cache: erase
from key 3r 4u 0s -6400288mem 0pf
After patch:
41975.cc unordered_set<int> without cache: first
insert 9r 6u 1s 8022176mem 0pf
41975.cc unordered_set<int> without cache: erase
from iterator 1r 1u 0s -6400160mem 0pf
41975.cc unordered_set<int> without cache: second
insert 7r 6u 0s 6397472mem 0pf
41975.cc unordered_set<int> without cache: erase
from key 1r 1u 0s -6400208mem 0pf
41975.cc unordered_set<int> with cache: first
insert 8r 6u 1s 8019488mem 0pf
41975.cc unordered_set<int> with cache: erase
from iterator 1r 2u 0s -6400208mem 0pf
41975.cc unordered_set<int> with cache: second
insert 8r 7u 0s 6400256mem 0pf
41975.cc unordered_set<int> with cache: erase
from key 1r 1u 0s -6400256mem 0pf
41975.cc unordered_set<string> without cache:
first insert 51r 51u 0s 8019472mem 0pf
41975.cc unordered_set<string> without cache:
erase from iterator 7r 7u 0s -6400192mem 0pf
41975.cc unordered_set<string> without cache:
second insert 50r 49u 0s 6400208mem 0pf
41975.cc unordered_set<string> without cache:
erase from key 7r 7u 0s -6400208mem 0pf
41975.cc unordered_set<string> with cache: first
insert 20r 20u 1s 8019472mem 0pf
41975.cc unordered_set<string> with cache: erase
from iterator 4r 4u 0s -6400192mem 0pf
41975.cc unordered_set<string> with cache: second
insert 20r 20u 0s 6400240mem 0pf
41975.cc unordered_set<string> with cache: erase
from key 4r 4u 0s -6400240mem 0pf
You can see the very good impact on the 'second insert' test case which
is a worst case insertion for the current implementation. It could even
be worst if the hash policy was not reducing the number of buckets when
too high. I don't understand however why the difference is so small on
string type...
2012-01-07 François Dumont <fdumont@gcc.gnu.org>
* include/bits/hashtable_policy.h (_Hash_node_base): New, use it as
base class of ...
(_Hash_node<Value, true>, _Hash_node<Value, false>): ... those.
* include/bits/hashtable.h (_Hashtable): Replace
_M_begin_bucket_index
by _M_before_begin. Review implementation so that we do not need to
look for previous non-empty bucket when inserting nodes.
Tested under linux x86_64 normal, debug, profile modes.
Ok to commit ?
François
-------------- next part --------------
A non-text attachment was scrubbed...
Name: hashtable.patch
Type: text/x-patch
Size: 35837 bytes
Desc: not available
URL: <http://gcc.gnu.org/pipermail/libstdc++/attachments/20120107/ef8542c6/attachment.bin>
More information about the Libstdc++
mailing list