[Git][ghc/ghc][wip/T26009] 2 commits: rts/Hash: Factor out hashBuffer

Ben Gamari pushed to branch wip/T26009 at Glasgow Haskell Compiler / GHC Commits: 827961b9 by Ben Gamari at 2025-05-19T16:01:12-04:00 rts/Hash: Factor out hashBuffer This is a useful helper which can be used for non-strings as well. - - - - - deadc2ef by Ben Gamari at 2025-05-19T16:01:24-04:00 rts/linker/PEi386: Don't repeated load DLLs Previously every DLL-imported symbol would result in a call to `LoadLibraryEx`. This ended up constituting over 40% of the runtime of `ghc --interactive -e 42` on Windows. Avoid this by maintaining a hash-set of loaded DLL names, skipping the call if we have already loaded the requested DLL. Addresses #26009. - - - - - 3 changed files: - rts/Hash.c - rts/Hash.h - rts/linker/PEi386.c Changes: ===================================== rts/Hash.c ===================================== @@ -94,13 +94,13 @@ hashWord(const HashTable *table, StgWord key) } int -hashStr(const HashTable *table, StgWord w) +hashBuffer(const HashTable *table, const void *buf, size_t len) { - const char *key = (char*) w; + const char *key = (char*) buf; #if WORD_SIZE_IN_BITS == 64 - StgWord h = XXH3_64bits_withSeed (key, strlen(key), 1048583); + StgWord h = XXH3_64bits_withSeed (key, len, 1048583); #else - StgWord h = XXH32 (key, strlen(key), 1048583); + StgWord h = XXH32 (key, len, 1048583); #endif /* Mod the size of the hash table (a power of 2) */ @@ -114,6 +114,13 @@ hashStr(const HashTable *table, StgWord w) return bucket; } +int +hashStr(const HashTable *table, StgWord w) +{ + const char *key = (char*) w; + return hashBuffer(table, key, strlen(key)); +} + STATIC_INLINE int compareWord(StgWord key1, StgWord key2) { ===================================== rts/Hash.h ===================================== @@ -69,6 +69,10 @@ void * removeStrHashTable ( StrHashTable *table, const char * key, */ typedef int HashFunction(const HashTable *table, StgWord key); typedef int CompareFunction(StgWord key1, StgWord key2); + +// Helper for implementing hash functions +int hashBuffer(const HashTable *table, const void *buf, size_t len); + int hashWord(const HashTable *table, StgWord key); int hashStr(const HashTable *table, StgWord w); void insertHashTable_ ( HashTable *table, StgWord key, @@ -79,6 +83,7 @@ void * removeHashTable_ ( HashTable *table, StgWord key, const void *data, HashFunction f, CompareFunction cmp ); + /* Freeing hash tables */ void freeHashTable ( HashTable *table, void (*freeDataFun)(void *) ); ===================================== rts/linker/PEi386.c ===================================== @@ -427,8 +427,53 @@ const int default_alignment = 8; the pointer as a redirect. Essentially it's a DATA DLL reference. */ const void* __rts_iob_func = (void*)&__acrt_iob_func; +/* + * Note [Avoiding repeated DLL loading] + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * As LoadLibraryEx tends to be expensive and addDLL_PEi386 is called on every + * DLL-imported symbol, we use a hash-set to keep track of which DLLs have + * already been loaded. This hash-set is keyed on the dll_name passed to + * addDLL_PEi386 and serves as a quick check to avoid repeated calls to + * LoadLibraryEx for the identical DLL. See #26009. + */ + +typedef struct { + HashTable *hash; +} LoadedDllSet; + +LoadedDllSet loaded_dll_set; + +void initLoadedDllSet(LoadedDllSet *set) { + set->hash = allocHashTable(); +} + +int hash_path(const HashTable *table, StgWord w) +{ + const pathchar *key = (pathchar*) w; + return hashBuffer(table, key, sizeof(pathchar) * wcslen(key)); +} + +int compare_path(StgWord key1, StgWord key2) +{ + return wscmp((pathchar*) key1, (pathchar*) key2); +} + +void addLoadedDll(LoadedDllSet *set, pathchar *dll_name) +{ + insertHashTable_(set->hash, (StgWord) dll_name, (void*) 1, hash_path); +} + +bool isDllLoaded(LoadedDllSet *set, pathchar *dll_name) +{ + void * result = lookupHashTable_(set->hash, (StgWord) dll_name, hash_path, compare_path); + return result != NULL; +} + + void initLinker_PEi386(void) { + initLoadedDllSet(&loaded_dll_set); + if (!ghciInsertSymbolTable(WSTR("(GHCi/Ld special symbols)"), symhash, "__image_base__", GetModuleHandleW (NULL), HS_BOOL_TRUE, @@ -802,6 +847,10 @@ addDLL_PEi386( pathchar *dll_name, HINSTANCE *loaded ) /* ------------------- Win32 DLL loader ------------------- */ IF_DEBUG(linker, debugBelch("addDLL; dll_name = `%" PATH_FMT "'\n", dll_name)); + if (isDllLoaded(&loaded_dll_set, dll_name)) { + return NULL; + } + /* The file name has no suffix (yet) so that we can try both foo.dll and foo.drv @@ -840,6 +889,7 @@ addDLL_PEi386( pathchar *dll_name, HINSTANCE *loaded ) goto error; } + addLoadedDll(&loaded_dll_set, dll_name); addDLLHandle(buf, instance); if (loaded) { *loaded = instance; View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/5cfcf0f90ab2955401ebbd78f417d88... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/5cfcf0f90ab2955401ebbd78f417d88... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Ben Gamari (@bgamari)