Commit f80c4a66 authored by Crazycolorz5's avatar Crazycolorz5 Committed by Marge Bot

rts: Specialize hashing at call site rather than in struct.

Separate word and string hash tables on the type level, and do not store
the hashing function.  Thus when a different hash function is desire it
is provided upon accessing the table. This is worst case the same as
before the change, and in the majority of cases is better. Also mark the
functions for aggressive inlining to improve performance.  {F1686506}

Reviewers: bgamari, erikd, simonmar

Subscribers: rwbarton, thomie, carter

GHC Trac Issues: #13165

Differential Revision: https://phabricator.haskell.org/D4889
parent 6e47a76a
Pipeline #13671 passed with stages
in 492 minutes and 44 seconds
......@@ -126,7 +126,7 @@ extern void hs_free_stable_ptr_unsafe (HsStablePtr sp);
extern void hs_free_stable_ptr (HsStablePtr sp);
extern void hs_free_fun_ptr (HsFunPtr fp);
extern StgPtr hs_spt_lookup(StgWord64 key1, StgWord64 key2);
extern StgPtr hs_spt_lookup(StgWord64 key[2]);
extern int hs_spt_keys(StgPtr keys[], int szKeys);
extern int hs_spt_key_count (void);
......
......@@ -48,7 +48,7 @@ module GHC.StaticPtr
) where
import Foreign.C.Types (CInt(..))
import Foreign.Marshal (allocaArray, peekArray)
import Foreign.Marshal (allocaArray, peekArray, withArray)
import GHC.Ptr (Ptr(..), nullPtr)
import GHC.Fingerprint (Fingerprint(..))
import GHC.Prim
......@@ -87,13 +87,13 @@ staticKey (StaticPtr w0 w1 _ _) = Fingerprint (W64# w0) (W64# w1)
--
unsafeLookupStaticPtr :: StaticKey -> IO (Maybe (StaticPtr a))
unsafeLookupStaticPtr (Fingerprint w1 w2) = do
ptr@(Ptr addr) <- hs_spt_lookup w1 w2
ptr@(Ptr addr) <- withArray [w1, w2] hs_spt_lookup
if (ptr == nullPtr)
then return Nothing
else case addrToAny# addr of
(# spe #) -> return (Just spe)
foreign import ccall unsafe hs_spt_lookup :: Word64 -> Word64 -> IO (Ptr a)
foreign import ccall unsafe hs_spt_lookup :: Ptr Word64 -> IO (Ptr a)
-- | A class for things buildable from static pointers.
class IsStatic p where
......
......@@ -34,14 +34,14 @@ static HashTable *fd_hash;
static Mutex file_lock_mutex;
#endif
static int cmpLocks(StgWord w1, StgWord w2)
STATIC_INLINE int cmpLocks(StgWord w1, StgWord w2)
{
Lock *l1 = (Lock *)w1;
Lock *l2 = (Lock *)w2;
return (l1->device == l2->device && l1->inode == l2->inode);
}
static int hashLock(const HashTable *table, StgWord w)
STATIC_INLINE int hashLock(const HashTable *table, StgWord w)
{
Lock *l = (Lock *)w;
StgWord key = l->inode ^ (l->inode >> 32) ^ l->device ^ (l->device >> 32);
......@@ -52,7 +52,7 @@ static int hashLock(const HashTable *table, StgWord w)
void
initFileLocking(void)
{
obj_hash = allocHashTable_(hashLock, cmpLocks);
obj_hash = allocHashTable();
fd_hash = allocHashTable(); /* ordinary word-based table */
#if defined(THREADED_RTS)
initMutex(&file_lock_mutex);
......@@ -85,7 +85,7 @@ lockFile(int fd, StgWord64 dev, StgWord64 ino, int for_writing)
key.device = dev;
key.inode = ino;
lock = lookupHashTable(obj_hash, (StgWord)&key);
lock = lookupHashTable_(obj_hash, (StgWord)&key, hashLock, cmpLocks);
if (lock == NULL)
{
......@@ -93,7 +93,7 @@ lockFile(int fd, StgWord64 dev, StgWord64 ino, int for_writing)
lock->device = dev;
lock->inode = ino;
lock->readers = for_writing ? -1 : 1;
insertHashTable(obj_hash, (StgWord)lock, (void *)lock);
insertHashTable_(obj_hash, (StgWord)lock, (void *)lock, hashLock);
insertHashTable(fd_hash, fd, lock);
RELEASE_LOCK(&file_lock_mutex);
return 0;
......@@ -135,7 +135,7 @@ unlockFile(int fd)
}
if (lock->readers == 0) {
removeHashTable(obj_hash, (StgWord)lock, NULL);
removeHashTable_(obj_hash, (StgWord)lock, NULL, hashLock, cmpLocks);
stgFree(lock);
}
removeHashTable(fd_hash, fd, NULL);
......
......@@ -49,22 +49,25 @@ struct hashtable {
HashList **dir[HDIRSIZE]; /* Directory of segments */
HashList *freeList; /* free list of HashLists */
HashListChunk *chunks;
HashFunction *hash; /* hash function */
CompareFunction *compare; /* key comparison function */
};
/* Create an identical structure, but is distinct on a type level,
* for string hash table. Since it's a direct embedding of
* a hashtable and not a reference, there shouldn't be
* any overhead post-compilation. */
struct strhashtable { struct hashtable table; };
/* -----------------------------------------------------------------------------
* Hash first using the smaller table. If the bucket is less than the
* next bucket to be split, re-hash using the larger table.
* -------------------------------------------------------------------------- */
int
hashWord(const HashTable *table, StgWord key)
{
int bucket;
/* Strip the boring zero bits */
key /= sizeof(StgWord);
key >>= sizeof(StgWord);
/* Mod the size of the hash table (a power of 2) */
bucket = key & table->mask1;
......@@ -97,13 +100,13 @@ hashStr(const HashTable *table, StgWord w)
return bucket;
}
static int
STATIC_INLINE int
compareWord(StgWord key1, StgWord key2)
{
return (key1 == key2);
}
static int
STATIC_INLINE int
compareStr(StgWord key1, StgWord key2)
{
return (strcmp((char *)key1, (char *)key2) == 0);
......@@ -114,7 +117,7 @@ compareStr(StgWord key1, StgWord key2)
* Allocate a new segment of the dynamically growing hash table.
* -------------------------------------------------------------------------- */
static void
STATIC_INLINE void
allocSegment(HashTable *table, int segment)
{
table->dir[segment] = stgMallocBytes(HSEGSIZE * sizeof(HashList *),
......@@ -128,8 +131,8 @@ allocSegment(HashTable *table, int segment)
* by @table->split@ is affected by the expansion.
* -------------------------------------------------------------------------- */
static void
expand(HashTable *table)
STATIC_INLINE void
expand(HashTable *table, HashFunction f)
{
int oldsegment;
int oldindex;
......@@ -170,7 +173,7 @@ expand(HashTable *table)
old = new = NULL;
for (hl = table->dir[oldsegment][oldindex]; hl != NULL; hl = next) {
next = hl->next;
if (table->hash(table, hl->key) == newbucket) {
if (f(table, hl->key) == newbucket) {
hl->next = new;
new = hl;
} else {
......@@ -184,19 +187,20 @@ expand(HashTable *table)
return;
}
void *
lookupHashTable(const HashTable *table, StgWord key)
STATIC_INLINE void*
lookupHashTable_inlined(const HashTable *table, StgWord key,
HashFunction f, CompareFunction cmp)
{
int bucket;
int segment;
int index;
HashList *hl;
bucket = table->hash(table, key);
bucket = f(table, key);
segment = bucket / HSEGSIZE;
index = bucket % HSEGSIZE;
CompareFunction *cmp = table->compare;
for (hl = table->dir[segment][index]; hl != NULL; hl = hl->next) {
if (cmp(hl->key, key))
return (void *) hl->data;
......@@ -206,6 +210,26 @@ lookupHashTable(const HashTable *table, StgWord key)
return NULL;
}
void *
lookupHashTable_(const HashTable *table, StgWord key,
HashFunction f, CompareFunction cmp)
{
return lookupHashTable_inlined(table, key, f, cmp);
}
void *
lookupHashTable(const HashTable *table, StgWord key)
{
return lookupHashTable_inlined(table, key, hashWord, compareWord);
}
void *
lookupStrHashTable(const StrHashTable* table, const char* key)
{
return lookupHashTable_inlined(&table->table, (StgWord) key,
hashStr, compareStr);
}
// Puts up to szKeys keys of the hash table into the given array. Returns the
// actual amount of keys that have been retrieved.
//
......@@ -273,8 +297,9 @@ freeHashList (HashTable *table, HashList *hl)
table->freeList = hl;
}
void
insertHashTable(HashTable *table, StgWord key, const void *data)
STATIC_INLINE void
insertHashTable_inlined(HashTable *table, StgWord key,
const void *data, HashFunction f)
{
int bucket;
int segment;
......@@ -287,9 +312,9 @@ insertHashTable(HashTable *table, StgWord key, const void *data)
/* When the average load gets too high, we expand the table */
if (++table->kcount >= HLOAD * table->bcount)
expand(table);
expand(table, f);
bucket = table->hash(table, key);
bucket = f(table, key);
segment = bucket / HSEGSIZE;
index = bucket % HSEGSIZE;
......@@ -299,11 +324,30 @@ insertHashTable(HashTable *table, StgWord key, const void *data)
hl->data = data;
hl->next = table->dir[segment][index];
table->dir[segment][index] = hl;
}
void
insertHashTable_(HashTable *table, StgWord key,
const void *data, HashFunction f)
{
return insertHashTable_inlined(table, key, data, f);
}
void *
removeHashTable(HashTable *table, StgWord key, const void *data)
void
insertHashTable(HashTable *table, StgWord key, const void *data)
{
insertHashTable_inlined(table, key, data, hashWord);
}
void
insertStrHashTable(StrHashTable *table, const char * key, const void *data)
{
insertHashTable_inlined(&table->table, (StgWord) key, data, hashStr);
}
STATIC_INLINE void*
removeHashTable_inlined(HashTable *table, StgWord key, const void *data,
HashFunction f, CompareFunction cmp)
{
int bucket;
int segment;
......@@ -311,12 +355,12 @@ removeHashTable(HashTable *table, StgWord key, const void *data)
HashList *hl;
HashList *prev = NULL;
bucket = table->hash(table, key);
bucket = f(table, key);
segment = bucket / HSEGSIZE;
index = bucket % HSEGSIZE;
for (hl = table->dir[segment][index]; hl != NULL; hl = hl->next) {
if (table->compare(hl->key,key) && (data == NULL || hl->data == data)) {
if (cmp(hl->key, key) && (data == NULL || hl->data == data)) {
if (prev == NULL)
table->dir[segment][index] = hl->next;
else
......@@ -333,6 +377,26 @@ removeHashTable(HashTable *table, StgWord key, const void *data)
return NULL;
}
void*
removeHashTable_(HashTable *table, StgWord key, const void *data,
HashFunction f, CompareFunction cmp)
{
return removeHashTable_inlined(table, key, data, f, cmp);
}
void *
removeHashTable(HashTable *table, StgWord key, const void *data)
{
return removeHashTable_inlined(table, key, data, hashWord, compareWord);
}
void *
removeStrHashTable(StrHashTable *table, const char * key, const void *data)
{
return removeHashTable_inlined(&table->table, (StgWord) key,
data, hashStr, compareStr);
}
/* -----------------------------------------------------------------------------
* When we free a hash table, we are also good enough to free the
* data part of each (key, data) pair, as long as our caller can tell
......@@ -406,7 +470,7 @@ mapHashTable(HashTable *table, void *data, MapHashFn fn)
* -------------------------------------------------------------------------- */
HashTable *
allocHashTable_(HashFunction *hash, CompareFunction *compare)
allocHashTable(void)
{
HashTable *table;
HashList **hb;
......@@ -426,24 +490,10 @@ allocHashTable_(HashFunction *hash, CompareFunction *compare)
table->bcount = HSEGSIZE;
table->freeList = NULL;
table->chunks = NULL;
table->hash = hash;
table->compare = compare;
return table;
}
HashTable *
allocHashTable(void)
{
return allocHashTable_(hashWord, compareWord);
}
HashTable *
allocStrHashTable(void)
{
return allocHashTable_(hashStr, compareStr);
}
void
exitHashTable(void)
{
......
......@@ -11,6 +11,7 @@
#include "BeginPrivate.h"
typedef struct hashtable HashTable; /* abstract */
typedef struct strhashtable StrHashTable;
/* Hash table access where the keys are StgWords.
* Values are passed into the hash table and stored as `const void *` values,
......@@ -40,27 +41,44 @@ void mapHashTable(HashTable *table, void *data, MapHashFn fn);
* assumed to be allocated by the caller, and mustn't be deallocated
* until the corresponding hash table entry has been removed).
*/
HashTable * allocStrHashTable ( void );
#define lookupStrHashTable(table, key) \
(lookupHashTable(table, (StgWord)key))
#define insertStrHashTable(table, key, data) \
(insertHashTable(table, (StgWord)key, data))
#define removeStrHashTable(table, key, data) \
(removeHashTable(table, (StgWord)key, data))
/* Hash tables for arbitrary keys */
INLINE_HEADER StrHashTable * allocStrHashTable ( void )
{
return (StrHashTable*) allocHashTable();
}
void insertStrHashTable ( StrHashTable *table, const char * key,
const void *data );
void * lookupStrHashTable ( const StrHashTable *table, const char * key);
void * removeStrHashTable ( StrHashTable *table, const char * key,
const void *data );
/*
* Hash tables for arbitrary key types.
* Generally, these functions allow for the specification of the
* HashFunction and CompareFunction. It's recommended that those
* are inlinable so there's a chance the compiler can discard
* some parameter-passing, as well as function calls, though note
* it's not guaranteed. Either way, the functions are parameters
* as the types should be statically known and thus
* storing them is unnecessary.
*/
typedef int HashFunction(const HashTable *table, StgWord key);
typedef int CompareFunction(StgWord key1, StgWord key2);
HashTable * allocHashTable_(HashFunction *hash, CompareFunction *compare);
int hashWord(const HashTable *table, StgWord key);
int hashStr(const HashTable *table, StgWord key);
int hashStr(const HashTable *table, StgWord w);
void insertHashTable_ ( HashTable *table, StgWord key,
const void *data, HashFunction f );
void * lookupHashTable_ ( const HashTable *table, StgWord key,
HashFunction f, CompareFunction cmp );
void * removeHashTable_ ( HashTable *table, StgWord key,
const void *data, HashFunction f,
CompareFunction cmp );
/* Freeing hash tables
*/
void freeHashTable ( HashTable *table, void (*freeDataFun)(void *) );
#define freeStrHashTable(table, f) \
(freeHashTable((HashTable*) table, f))
void exitHashTable ( void );
......
......@@ -39,7 +39,7 @@ static pid_t hpc_pid = 0; // pid of this process at hpc-boot time.
static FILE *tixFile; // file being read/written
static int tix_ch; // current char
static HashTable * moduleHash = NULL; // module name -> HpcModuleInfo
static StrHashTable * moduleHash = NULL; // module name -> HpcModuleInfo
HpcModuleInfo *modules = 0;
......@@ -152,11 +152,11 @@ readTix(void) {
expect(']');
ws();
lookup = lookupHashTable(moduleHash, (StgWord)tmpModule->modName);
lookup = lookupStrHashTable(moduleHash, tmpModule->modName);
if (lookup == NULL) {
debugTrace(DEBUG_hpc,"readTix: new HpcModuleInfo for %s",
tmpModule->modName);
insertHashTable(moduleHash, (StgWord)tmpModule->modName, tmpModule);
insertStrHashTable(moduleHash, tmpModule->modName, tmpModule);
} else {
ASSERT(lookup->tixArr != 0);
ASSERT(!strcmp(tmpModule->modName, lookup->modName));
......@@ -265,7 +265,7 @@ hs_hpc_module(char *modName,
moduleHash = allocStrHashTable();
}
tmpModule = lookupHashTable(moduleHash, (StgWord)modName);
tmpModule = lookupStrHashTable(moduleHash, modName);
if (tmpModule == NULL)
{
// Did not find entry so add one on.
......@@ -282,7 +282,7 @@ hs_hpc_module(char *modName,
tmpModule->next = modules;
tmpModule->from_file = false;
modules = tmpModule;
insertHashTable(moduleHash, (StgWord)modName, tmpModule);
insertStrHashTable(moduleHash, modName, tmpModule);
}
else
{
......@@ -392,7 +392,7 @@ exitHpc(void) {
writeTix(f);
}
freeHashTable(moduleHash, (void (*)(void *))freeHpcModuleInfo);
freeStrHashTable(moduleHash, (void (*)(void *))freeHpcModuleInfo);
moduleHash = NULL;
stgFree(tixFilename);
......
......@@ -158,7 +158,7 @@
2) The number of duplicate symbols, since now only symbols that are
true duplicates will display the error.
*/
/*Str*/HashTable *symhash;
StrHashTable *symhash;
/* List of currently loaded objects */
ObjectCode *objects = NULL; /* initially empty */
......@@ -227,7 +227,7 @@ int ocTryLoad( ObjectCode* oc );
static void *mmap_32bit_base = (void *)MMAP_32BIT_BASE_DEFAULT;
static void ghciRemoveSymbolTable(HashTable *table, const SymbolName* key,
static void ghciRemoveSymbolTable(StrHashTable *table, const SymbolName* key,
ObjectCode *owner)
{
RtsSymbolInfo *pinfo = lookupStrHashTable(table, key);
......@@ -262,7 +262,7 @@ static void ghciRemoveSymbolTable(HashTable *table, const SymbolName* key,
*/
int ghciInsertSymbolTable(
pathchar* obj_name,
HashTable *table,
StrHashTable *table,
const SymbolName* key,
SymbolAddr* data,
HsBool weak,
......@@ -373,7 +373,7 @@ int ghciInsertSymbolTable(
* Returns: 0 on failure and result is not set,
* nonzero on success and result set to nonzero pointer
*/
HsBool ghciLookupSymbolInfo(HashTable *table,
HsBool ghciLookupSymbolInfo(StrHashTable *table,
const SymbolName* key, RtsSymbolInfo **result)
{
RtsSymbolInfo *pinfo = lookupStrHashTable(table, key);
......@@ -524,7 +524,7 @@ exitLinker( void ) {
}
#endif
if (linker_init_done == 1) {
freeHashTable(symhash, free);
freeStrHashTable(symhash, free);
}
#if defined(THREADED_RTS)
closeMutex(&linker_mutex);
......@@ -1200,7 +1200,7 @@ void freeObjectCode (ObjectCode *oc)
}
if (oc->extraInfos != NULL) {
freeHashTable(oc->extraInfos, NULL);
freeStrHashTable(oc->extraInfos, NULL);
oc->extraInfos = NULL;
}
......
......@@ -243,7 +243,7 @@ typedef struct _ObjectCode {
/* Holds the list of symbols in the .o file which
require extra information.*/
HashTable *extraInfos;
StrHashTable *extraInfos;
#if RTS_LINKER_USE_MMAP == 1
/* The m32 allocators used for allocating small sections and symbol extras
......@@ -304,12 +304,12 @@ void addSection (Section *s, SectionKind kind, SectionAlloc alloc,
void* start, StgWord size, StgWord mapped_offset,
void* mapped_start, StgWord mapped_size);
HsBool ghciLookupSymbolInfo(HashTable *table,
HsBool ghciLookupSymbolInfo(StrHashTable *table,
const SymbolName* key, RtsSymbolInfo **result);
int ghciInsertSymbolTable(
pathchar* obj_name,
HashTable *table,
StrHashTable *table,
const SymbolName* key,
SymbolAddr* data,
HsBool weak,
......@@ -318,7 +318,7 @@ int ghciInsertSymbolTable(
/* lock-free version of lookupSymbol */
SymbolAddr* lookupSymbol_ (SymbolName* lbl);
extern /*Str*/HashTable *symhash;
extern StrHashTable *symhash;
pathchar*
resolveSymbolAddr (pathchar* buffer, int size,
......
......@@ -93,7 +93,7 @@ static void unmarkImport(SymbolInfo* info)
/* -----------------------------------------------------------------------------
* Marks the symbol at the given address as weak or not.
* If the extra symbol infos table has not been initialized
* yet this will create and allocate a new Hashtable
* yet this will create and allocate a new StrHashtable
*/
void setWeakSymbol(ObjectCode *owner, const void *label)
{
......@@ -103,7 +103,7 @@ void setWeakSymbol(ObjectCode *owner, const void *label)
/* -----------------------------------------------------------------------------
* Marks the symbol at the given address as import or not.
* If the extra symbol infos table has not been initialized
* yet this will create and allocate a new Hashtable
* yet this will create and allocate a new StrHashtable
*/
void setImportSymbol(ObjectCode *owner, const void *label)
{
......@@ -113,7 +113,7 @@ void setImportSymbol(ObjectCode *owner, const void *label)
/* -----------------------------------------------------------------------------
* Clear the import symbol flag.
* If the extra symbol infos table has not been initialized
* yet this will create and allocate a new Hashtable
* yet this will create and allocate a new StrHashtable
*/
void clearImportSymbol(ObjectCode *owner, const void *label)
{
......
......@@ -21,14 +21,14 @@ static Mutex spt_lock;
#endif
/// Hash function for the SPT.
static int hashFingerprint(const HashTable *table, StgWord key) {
STATIC_INLINE int hashFingerprint(const HashTable *table, StgWord key) {
const StgWord64* ptr = (StgWord64*) key;
// Take half of the key to compute the hash.
return hashWord(table, *(ptr + 1));
}
/// Comparison function for the SPT.
static int compareFingerprint(StgWord a, StgWord b) {
STATIC_INLINE int compareFingerprint(StgWord a, StgWord b) {
const StgWord64* ptra = (StgWord64*) a;
const StgWord64* ptrb = (StgWord64*) b;
return *ptra == *ptrb && *(ptra + 1) == *(ptrb + 1);
......@@ -38,14 +38,14 @@ void hs_spt_insert_stableptr(StgWord64 key[2], StgStablePtr *entry) {
// hs_spt_insert is called from constructor functions, so
// the SPT needs to be initialized here.
if (spt == NULL) {
spt = allocHashTable_(hashFingerprint, compareFingerprint);
spt = allocHashTable();
#if defined(THREADED_RTS)
initMutex(&spt_lock);
#endif
}
ACQUIRE_LOCK(&spt_lock);
insertHashTable(spt, (StgWord)key, entry);
insertHashTable_(spt, (StgWord)key, entry, hashFingerprint);
RELEASE_LOCK(&spt_lock);
}
......@@ -68,7 +68,8 @@ static void freeSptEntry(void* entry) {
void hs_spt_remove(StgWord64 key[2]) {
if (spt) {
ACQUIRE_LOCK(&spt_lock);
StgStablePtr* entry = removeHashTable(spt, (StgWord)key, NULL);
StgStablePtr* entry = removeHashTable_(spt, (StgWord)key, NULL,
hashFingerprint, compareFingerprint);
RELEASE_LOCK(&spt_lock);
if (entry)
......@@ -76,11 +77,11 @@ void hs_spt_remove(StgWord64 key[2]) {
}
}
StgPtr hs_spt_lookup(StgWord64 key1, StgWord64 key2) {
StgPtr hs_spt_lookup(StgWord64 key[2]) {
if (spt) {
ACQUIRE_LOCK(&spt_lock);
StgWord64 key[2] = { key1, key2 };
const StgStablePtr * entry = lookupHashTable(spt, (StgWord)key);
const StgStablePtr * entry = lookupHashTable_(spt, (StgWord)key,
hashFingerprint, compareFingerprint);
const StgPtr ret = entry ? deRefStablePtr(*entry) : NULL;
RELEASE_LOCK(&spt_lock);
return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment