Avoid excessively-big hash tables in empty-add cycles
authorRichard Sandiford <richard.sandiford@arm.com>
Fri, 13 Jan 2017 16:00:26 +0000 (16:00 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Fri, 13 Jan 2017 16:00:26 +0000 (16:00 +0000)
commitd3da63e543ba6e01af109f0b9827cdc03ddf116e
tree117299cf30ca43fe6d79a5a0be58b2a56caa8d68
parent3d8e492088897fadb3434917d73f5728f1bbbc19
Avoid excessively-big hash tables in empty-add cycles

A big source of cache misses when compiling a recent version of
gimple-match.ii was the call to cv_cache.empty () in clear_cv_cache.
The problem was that at one early point the hash table had grown
to 8191 entries (128k on LP64 hosts).  It then stayed at that size
for the rest of the compilation, even though subsequent uses needed
only a small number of entries (usually fewer than ten).  We would
still clear the whole 128k each time clear_cv_cache was called.

empty() already looks for cases where the hash table is very big
and cuts it down.  At the moment it fires when the table is 1M
in size and reduces it to the next selected prime above 1K (so
almost 2K in practice).  One fix would have been to lower the
threshold, but that didn't feel like the right approach.  Reducing
the current limit of 1M by a factor of 8 would be pretty significant
on its own, but I think this cv_cache behaviour would have been a
problem even with 64k or 32k tables.

I think the existing check is really for cases in which even a
well-populated table would need to be shrunk rather than cleared.
Here the problem isn't that the table is excessively big in
absolute terms, more that one outlier has made the table much
too big for the general case.

traverse() already shrinks the table if it's "too empty",
which is taken to be if:

      no. elements * 8 < capacity && capacity > 32

So an alternative would be to apply the same test (and the same choice
of shrunken size) to empty_slow too.  The patch below does this.
It gives a 2.5% improvement in gimple-match.ii compile time at -O0 -g
and doesn't seem to adversely affect any other tests I've tried.

Of course, there's a theoretical risk of a table alternating between
one large element count and one small element count.  If there was a
factor of eight difference between the two, we could shrink the table
on seeing each small element count, then grow it again when adding the
large number of elements.  That seems pretty unlikely in practice
though.

Also, empty_slow() does involve a traversal if some form of manual
gc is needed on active elements, so trying to recover from an outlier
should have even more benefit there.  (cv_cache uses automatic gc and so
the traversal gets optimised away.)

The calculation of the existing 1M threshold was assuming that each
entry was pointer-sized.  This patch makes it use the actual size of the
entry instead.

Tested on aarch64-linux-gnu and x86_64-linux-gnu.

gcc/
* hash-table.h (hash_table::too_empty_p): New function.
(hash_table::expand): Use it.
(hash_table::traverse): Likewise.
(hash_table::empty_slot): Use sizeof (value_type) instead of
sizeof (PTR) to convert bytes to elements.  Shrink the table
if the current size is excessive for the current number of
elements.

From-SVN: r244447
gcc/ChangeLog
gcc/hash-table.h