Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Hash-tables
#1
As I'm working with some memoization-macro in LISP, I turned on a performance-issue I've not really worked much with:

To make a general macro for memoization, I'm using hash-tables. If the value's already calculated before, it would be found in the hash-table. If it's not found, it will calculate the value and add this into the hash-table afterwards. Great stuff for recursive functions.

So, here's the thing: For multiple values in a memoization-function, would it be more handy to make hash-tables like this:

function (a, b, c):

 Hash-Table

Or like this?
 Hash-Table

I would assume the first one would be the fastest. However, the thing here is that this is no 3d-array, this is a hash-tree. If I e.g. test out a new value here, say:
function (4, 1, 2);

To gather the information if existing, it will look if there's a key in the hash-table for A = 4. If this returns a hash-table, it will look if there's a hash-table for B = 1. If there is a hash-table for this value, it will then look for a result for C = 2. If there is, returns that value. If not, do the following:

Create a new hash-input in the first hash-table. Now, it has to create a new hash-table, and then a new hash-input in this new hash-table. Finally, it has to create a new hash-table and a new hash-input in this table, which will hold the answer when computed.

The other way would be to check if there's a hash-key 4 1 2 in the hash-table. If not, makes this hash-key and computes value for the function.


Now, what would be the fastest? Creating the hash-tables would take some time, but then again, looking up a value in a bigger hash-table would also take some time. What would take the most time?
Reply
#2
errr...I would *think* the second would be faster since you're doing 1 lookup instead of 3. In a properly implemented hash table with a good hash function, lookup is O(1) with the size of the table.

If you're not sure, measure. Measure before optimizing to see if you even need to optimize, and measure after to make sure it actually makes it faster.

Donald Knuth Wrote:Premature optimization is the root of all evil.
Reply
#3
Spaz Wrote:errr...I would *think* the second would be faster since you're doing 1 lookup instead of 3. In a properly implemented hash table with a good hash function, lookup is O(1) with the size of the table.

If you're not sure, measure. Measure before optimizing to see if you even need to optimize, and measure after to make sure it actually makes it faster.

Heck, I tested it out. Adding up 1 random value not used in the fibonacci-function (fib (n x)), tested out the efficiency by looping from 1 to 100,000, with a constant x.

It seems like the second idea is 1.5 times faster than the first function, using these macros in LISP:

 Spoiler

Then using them on the function:

 Spoiler

Just replacing defun with defmemoiz1 and defmemoize to make the functions memoizing. (Guess what, I used another macro to simplify redefining of the fibonacci-function. Sigh.)

For several values, time increased exponentially. For 8 different values, it took almost 4 times longer to calculate compared to the second idea. For 3, time barely changed. For one value, time was the same. (But then again, for one value, I make another kind of hash-table, which increases the speed noticeably.)
Reply
#4
Since your question largely summarizes the competition between breadth first and depth first branching, I highly recommend you do some research on each form of branching to determine which will be the best method for your particular situation. Each type of branching has its unique advantages and disadvantages.
Reply
#5
Spaz Wrote:errr...I would *think* the second would be faster since you're doing 1 lookup instead of 3. In a properly implemented hash table with a good hash function, lookup is O(1) with the size of the table.

Main costs of hash table are overfull (lots of collisions -> slower lookup) or resizing (may have to recalculate all keys)

If you know how big it'll be, as in the given example, it won't be a problem. I'm not sure splitting into smaller tables helps though, as efficient hashing avoids clumping it'll tend to have to resize all the tables anyway if they become overfull.


In terms of seek time, I think having to follow more pointers would be slower than just having a big table (key 1->value 1, value 1->table 2, key 2->value 2, value 2->table 3, key 3->value 3) or (longer key -> final value)

The 1 big table does require some concatenation to make the key, but I don't think that takes long.



otoh if you can generalize some areas to less depth (eg. all lookups with a=b=0 return 0 instead of a c hash table) then being able to cut off at an earlier depth might be better than having to look up the full string every time.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)