Maybe I’m being misled by my profiler (Netbeans), but I’m seeing some odd behavior, hoping maybe someone here can help me understand it.
I am working on an application, which makes heavy use of rather large hash tables (keys are longs, values are objects). The performance with the built in java hash table (HashMap specifically) was very poor, and after trying some alternatives — Trove, Fastutils, Colt, Carrot — I started working on my own.
The code is very basic using a double hashing strategy. This works fine and good and shows the best performance of all the other options I’ve tried thus far.
The catch is, according to the profiler, lookups into the hash table are the single most expensive method in the entire application — despite the fact that other methods are called many more times, and/or do a lot more logic.
What really confuses me is the lookups are called only by one class; the calling method does the lookup and processes the results. Both are called nearly the same number of times, and the method that calls the lookup has a lot of logic in it to handle the result of the lookup, but is about 100x faster.
Below is the code for the hash lookup. It’s basically just two accesses into an array (the functions that compute the hash codes, according to profiling, are virtually free). I don’t understand how this bit of code can be so slow since it is just array access, and I don’t see any way of making it faster.
Note that the code simply returns the bucket matching the key, the caller is expected to process the bucket. ‘size’ is the hash.length/2, hash1 does lookups in the first half of the hash table, hash2 does lookups in the second half. key_index is a final int field on the hash table passed into the constructor, and the values array on the Entry objects is a small array of longs usually of length 10 or less.
Any thoughts people have on this are much appreciated.
Thanks.
public final Entry get(final long theKey) {
Entry aEntry = hash[hash1(theKey, size)];
if (aEntry != null && aEntry.values[key_index] != theKey) {
aEntry = hash[hash2(theKey, size)];
if (aEntry != null && aEntry.values[key_index] != theKey) {
return null;
}
}
return aEntry;
}
Edit, the code for hash1 & hash2
private static int hash1(final long key, final int hashTableSize) {
return (int)(key&(hashTableSize-1));
}
private static int hash2(final long key, final int hashTableSize) {
return (int)(hashTableSize+((key^(key>>3))&(hashTableSize-1)));
}
Nothing in your implementation strikes me as particularly inefficient. I’ll admit I don’t really follow your hashing/lookup strategy, but if you say it’s performant in your circumstances, I’ll believe you.
The only thing that I would expect might make some difference is to move the key out of the values array of
Entry.Instead of having this:
Try this:
Instead of incurring the cost of accessing a member, plus doing bounds checking, then getting a value of the array, you should just incur the cost of accessing the member.
Is there a random-access data type faster than an array?
I was interested in the answer to this question, so I set up a test environment. This is my Array interface:
This “Array” has undefined behaviour when indices are out of bounds. I threw together the obvious implementation:
And then a control:
Finally, I designed an “array” where the first 10 indices are hardcoded members. The members are set/selected through a switch:
I tested it with this harness:
The results were somewhat surprising. The TenArray performs non-trivially faster than a NormalArray does (for sizes <= 10). Subtracting the overhead (using the NoOpArray average) you get TenArray as taking ~65% of the time of the normal array. So if you know the likely max size of your array, I suppose it is possible to exceed the speed of an array. I would imagine switch uses either less bounds checking or more efficient bounds checking than does an array.
Now whether you can in practice get speeds faster than an array I’m not sure; obviously this way you incur any overhead associated with the interface/class/methods.