- Joined
- Nov 7, 2014
- Messages
- 571
Hashtables/dictionaries/associative arrays are used to map keys to values. They are also supposed to be fast, i.e regardless of how many keys are inserted/added to the hashtable, the amount of time it takes to do lookups stays "constant". This does not appear to be the case for Jass'
The lookup times for the hashtables in Jass seem to increase the more keys get added. While it's possible that the implementation uses an array of linked lists, and these lists get longer and longer, at some point the hashtable should grow and rehash (as far as I know), thus reducing the length of these linked lists, reducing the lookup times.
But it seems to me that lookups take too much time, some of it is because Jass is bytecode interpreted and the rest... taken by iterating linked lists?
hashtable
type.The lookup times for the hashtables in Jass seem to increase the more keys get added. While it's possible that the implementation uses an array of linked lists, and these lists get longer and longer, at some point the hashtable should grow and rehash (as far as I know), thus reducing the length of these linked lists, reducing the lookup times.
But it seems to me that lookups take too much time, some of it is because Jass is bytecode interpreted and the rest... taken by iterating linked lists?
JASS:
library slowinghashtable initializer init
function writeln takes string s returns nothing
call BJDebugMsg(s)
endfunction
globals
force g_fn_call_force
timer g_fn_call_timer
endglobals
function fn_call_init takes nothing returns nothing
set g_fn_call_force = CreateForce()
call ForceAddPlayer(g_fn_call_force, Player(0))
set g_fn_call_timer = CreateTimer()
endfunction
function fn_call takes code f returns nothing
call ForForce(g_fn_call_force, f)
endfunction
globals
// the more keys we add, the more time it takes for each 'LoadInteger'
constant integer Max_Keys = 1000 * 1000
integer g_key = 0
hashtable g_ht = InitHashtable()
endglobals
function hashtable_init_helper takes nothing returns nothing
local integer n = 5000
loop
exitwhen n == 0
call SaveInteger(g_ht, 0, g_key, 1)
set g_key = g_key + 1
set n = n - 1
endloop
endfunction
function hashtable_init takes nothing returns nothing
loop
exitwhen g_key >= Max_Keys
call fn_call(function hashtable_init_helper)
endloop
endfunction
function do_nothing takes hashtable ht, integer k1, integer k2 returns integer
return 0
endfunction
function do_hashtable_lookups_helper takes nothing returns nothing
local integer a
local integer x
static if false then
// baseline, i.e how much time our setup takes, although calling natives should be slightly faster
set a = 0
loop
exitwhen a == 5000
set x = do_nothing(g_ht, 0, 0)
set a = a + 1
endloop
endif
static if true then
// lookup the last added key
set a = 0
loop
exitwhen a == 5000
set x = LoadInteger(g_ht, 0, 999999) // Max_Keys - 1
set a = a + 1
endloop
endif
static if false then
// it seems that looking up the first added key is slower than looking up the last added key,
// perhaps because the last added key becomes the head of the linked list from which the lookups start?
//
// lookup the first added key
set a = 0
loop
exitwhen a == 5000
set x = LoadInteger(g_ht, 0, 0)
set a = a + 1
endloop
endif
static if false then
// lookup the middle key
set a = 0
loop
exitwhen a == 5000
set x = LoadInteger(g_ht, 0, 499999)
set a = a + 1
endloop
endif
endfunction
function do_hashtable_lookups takes nothing returns nothing
local integer a = 0
loop
exitwhen a == 1000
call fn_call(function do_hashtable_lookups_helper)
set a = a + 1
endloop
endfunction
globals
integer g_countdown
timer g_countdown_timr = CreateTimer()
code g_countdown_fn
endglobals
function countdown takes nothing returns nothing
set g_countdown = g_countdown - 1
if g_countdown != 0 then
call writeln(I2S(g_countdown))
call TimerStart(g_countdown_timr, 1.0, false, function countdown)
else
call fn_call(g_countdown_fn)
call writeln("DONE")
endif
endfunction
function begin_countdown takes code f returns nothing
set g_countdown_fn = f
set g_countdown = 5
call writeln(I2S(g_countdown))
call TimerStart(g_countdown_timr, 1.0, false, function countdown)
endfunction
function on_start takes nothing returns nothing
call writeln("hashtable initialized with " + I2S(g_key) + " keys")
call begin_countdown(function do_hashtable_lookups)
endfunction
function init takes nothing returns nothing
call fn_call_init()
call hashtable_init()
call TimerStart(CreateTimer(), 0.0, false, function on_start)
endfunction
endlibrary // slowinghashtable