Easy hashtable for HotSpot
Ioi Lam
ioi.lam at oracle.com
Tue Jan 31 20:01:29 UTC 2017
I've always been frustrated by the HotSpot hashtable code. It looks like
every time you need to create a new table, you have to write a whole
bunch of boilerplate code like get() and add(). This is especially bad
if you just need a very simple mapping.
So I've created an "Easy" hashtable, to reduce as much boilerplate code
as possible:
template <class T, class Entry, MEMFLAGS F> class EasyHashtable
: public Hashtable<T, F>
{
static unsigned int compute_hash(T key) {
return Entry::compute_hash(key);
}
int hash_to_index(unsigned int hash) {
return Hashtable<T, F>::hash_to_index(hash);
}
public:
EasyHashtable(int table_size)
: Hashtable<T, F>(table_size, sizeof(Entry)) {}
// For iterating all the entries in the table
Entry* bucket(int index) {
return (Entry*)Hashtable<T, F>::bucket(index);
}
Entry* get(T literal) {
unsigned int hash = compute_hash(literal);
int index = hash_to_index(hash);
for (Entry* e = bucket(index); e != NULL; e = (Entry*)e->next()) {
if (e->literal() == literal) {
return e;
}
}
return NULL;
}
Entry* add(T literal) {
unsigned int hash = compute_hash(literal);
int index = hash_to_index(hash);
Entry* e = (Entry*)Hashtable<T, F>::new_entry(hash, literal);
Hashtable<T, F>::add_entry(index, e);
return e;
}
};
Here's an example (map Symbol* -> Klass*). You just need to provide your
own hash function.
class SymToKlassEntry : public HashtableEntry<Symbol*, mtSymbol> {
Klass* _klass;
public:
static unsigned int compute_hash(Symbol* sym) {
return (unsigned int) sym->identity_hash();
}
void init(Klass* k) {
_klass = k;
}
Klass* klass() {
return _klass;
}
};
void test() {
typedef EasyHashtable<Symbol*, SymToKlassEntry, mtSymbol> MyTable;
MyTable* table = new MyTable(1234);
Symbol* sym = SymbolTable::new_permanent_symbol("some symbol", CHECK);
table->add(sym)->init((Klass*)0xbaadf00d);
Klass* klass = table->get(sym)->klass();
// Iterate all symbols
for (int i=0; i<table->table_size(); i++) {
for (SymToKlassEntry* entry = table->bucket(i);
entry;
entry = (SymToKlassEntry*)entry->next()) {
tty->print_cr("sym: %p => klass: %p", entry->literal(),
entry->klass());
}
}
}
}
Another common case would be to map an address to arbitrary data type.
(I needed this for fixing JDK-8072061)
template <MEMFLAGS F> class AddressHashtableEntry
: public HashtableEntry<address, F> {
public:
static unsigned int compute_hash(address obj) {
return (unsigned int)(uintptr_t(obj) >> BitsPerWord);
}
};
template <class Entry, MEMFLAGS F> class AddressHashtable
: public EasyHashtable<address, Entry, F> {
public:
AddressHashtable(int table_size)
: EasyHashtable<address, Entry, F>(table_size) {}
Entry* get(address obj) {
assert(obj != NULL, "Caller should not pass in NULL");
return (Entry*)EasyHashtable<address, Entry, mtInternal>::get(obj);
}
Entry* add(address obj) {
assert(obj != NULL, "Caller should not pass in NULL");
return (Entry*)EasyHashtable<address, Entry, mtInternal>::add(obj);
}
};
Examples:
class AddrToNumEntry : public AddressHashtableEntry<mtInternal> {
int _val;
public:
void init(int v) {
_val = v;
}
int val() {
return _val;
}
};
void test() {
// A table of known addresses
{
typedef AddressHashtableEntry<mtInternal> MyEntry;
typedef AddressHashtable<MyEntry, mtInternal> MyTable;
MyTable* table = new MyTable(1234);
table->add((address)0xdeadbeef);
tty->print_cr("exists? %d", (table->get((address)0xbaadf00d) != NULL));
tty->print_cr("exists? %d", (table->get((address)0xdeadbeef) != NULL));
}
// Map addresses to numbers
{
typedef AddressHashtable<AddrToNumEntry, mtInternal> MyTable;
MyTable* table = new MyTable(1234);
table->add((address)0xdeadbeef)->init(1);
table->add((address)0xbaadf00d)->init(2);
tty->print_cr("%d", table->get((address)0xdeadbeef)->val());
}
}
Any thoughts about this coding style?
Thanks
- Ioi
More information about the hotspot-dev
mailing list