[PATCH] hashtable insert enhancement

François Dumont frs.dumont@gmail.com
Mon Jan 9 20:37:00 GMT 2012


     Same patch proposition as the previous one except that I have 
revisited the _M_rehash method that was still trying to keep nodes 
ordered by their bucket index. The result is 1 loop on nodes rather than 
a loop on nodes and a loop on buckets, simpler and faster code:

41975.cc                        unordered_set<int> without cache: first 
insert     5r    3u    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    4r    4u    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        5r    4u    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       5r    4u    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         44r   43u    1s  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        44r   42u    1s  6400208mem    0pf
41975.cc                        unordered_set<string> without cache: 
erase from key        6r    7u    0s -6400208mem    0pf
41975.cc                        unordered_set<string> with cache: first 
insert    15r   14u    0s  8019472mem    0pf
41975.cc                        unordered_set<string> with cache: erase 
from iterator      3r    3u    0s -6400192mem    0pf
41975.cc                        unordered_set<string> with cache: second 
insert   14r   14u    1s  6400240mem    0pf
41975.cc                        unordered_set<string> with cache: erase 
from key           4r    4u    0s -6400240mem    0pf


François


On 01/07/2012 11:09 PM, François Dumont wrote:
> 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: 36057 bytes
Desc: not available
URL: <http://gcc.gnu.org/pipermail/libstdc++/attachments/20120109/9cfe8291/attachment.bin>


More information about the Libstdc++ mailing list