FastMod for EEHashTable (Faster virtual generics) (#65926)
authorEgor Bogatov <egorbo@gmail.com>
Tue, 1 Mar 2022 19:43:37 +0000 (22:43 +0300)
committerGitHub <noreply@github.com>
Tue, 1 Mar 2022 19:43:37 +0000 (22:43 +0300)
src/coreclr/vm/eehash.h
src/coreclr/vm/eehash.inl
src/coreclr/vm/util.hpp

index 5467b1a..1179440 100644 (file)
@@ -150,8 +150,11 @@ protected:
 
     struct BucketTable
     {
-        DPTR(PTR_EEHashEntry_t) m_pBuckets;    // Pointer to first entry for each bucket
-        DWORD            m_dwNumBuckets;
+        DPTR(PTR_EEHashEntry_t) m_pBuckets;       // Pointer to first entry for each bucket
+        DWORD                   m_dwNumBuckets;
+#ifdef TARGET_64BIT
+        UINT64                  m_dwNumBucketsMul; // "Fast Mod" multiplier for "X % m_dwNumBuckets"
+#endif
     } m_BucketTable[2];
     typedef DPTR(BucketTable) PTR_BucketTable;
 
@@ -191,10 +194,15 @@ public:
     EEHashTable()
     {
         LIMITED_METHOD_CONTRACT;
-        this->m_BucketTable[0].m_pBuckets     = NULL;
-        this->m_BucketTable[0].m_dwNumBuckets = 0;
-        this->m_BucketTable[1].m_pBuckets     = NULL;
-        this->m_BucketTable[1].m_dwNumBuckets = 0;
+        this->m_BucketTable[0].m_pBuckets        = NULL;
+        this->m_BucketTable[0].m_dwNumBuckets    = 0;
+        this->m_BucketTable[1].m_pBuckets        = NULL;
+        this->m_BucketTable[1].m_dwNumBuckets    = 0;
+#ifdef TARGET_64BIT
+        this->m_BucketTable[0].m_dwNumBucketsMul = 0;
+        this->m_BucketTable[1].m_dwNumBucketsMul = 0;
+#endif
+
 #ifndef DACCESS_COMPILE
         this->m_pVolatileBucketTable = NULL;
 #endif
index e4c88bb..ba49b19 100644 (file)
@@ -106,6 +106,9 @@ void EEHashTableBase<KeyType, Helper, bDefaultCopyIsDeep>::ClearHashTable()
     }
 
     m_pVolatileBucketTable->m_dwNumBuckets = 0;
+#ifdef TARGET_64BIT
+    m_pVolatileBucketTable->m_dwNumBucketsMul = 0;
+#endif
     m_dwNumEntries = 0;
 }
 
@@ -193,8 +196,10 @@ BOOL EEHashTableBase<KeyType, Helper, bDefaultCopyIsDeep>::Init(DWORD dwNumBucke
 
     // The first slot links to the next list.
     m_pVolatileBucketTable->m_pBuckets++;
-
     m_pVolatileBucketTable->m_dwNumBuckets = dwNumBuckets;
+#ifdef TARGET_64BIT
+    m_pVolatileBucketTable->m_dwNumBucketsMul = GetFastModMultiplier(dwNumBuckets);
+#endif
 
     m_Heap = pHeap;
 
@@ -637,7 +642,13 @@ FORCEINLINE EEHashEntry_t *EEHashTableBase<KeyType, Helper, bDefaultCopyIsDeep>:
 
     _ASSERTE(pBucketTable->m_dwNumBuckets != 0);
 
-    DWORD           dwBucket = dwHash % pBucketTable->m_dwNumBuckets;
+    DWORD dwBucket;
+#ifdef TARGET_64BIT
+    _ASSERTE(pBucketTable->m_dwNumBucketsMul != 0);
+    dwBucket = FastMod(dwHash, pBucketTable->m_dwNumBuckets, pBucketTable->m_dwNumBucketsMul);
+#else
+    dwBucket = dwHash % pBucketTable->m_dwNumBuckets;
+#endif
     EEHashEntry_t * pSearch;
 
     for (pSearch = pBucketTable->m_pBuckets[dwBucket]; pSearch; pSearch = pSearch->pNext)
@@ -774,6 +785,9 @@ BOOL EEHashTableBase<KeyType, Helper, bDefaultCopyIsDeep>::GrowHashTable()
 
     pNewBucketTable->m_pBuckets = pNewBuckets;
     pNewBucketTable->m_dwNumBuckets = dwNewNumBuckets;
+#ifdef TARGET_64BIT
+    pNewBucketTable->m_dwNumBucketsMul = GetFastModMultiplier(dwNewNumBuckets);
+#endif
 
     // Add old table to the to free list. Note that the SyncClean thing will only
     // delete the buckets at a safe point
index 81c7a64..a8aa775 100644 (file)
@@ -1006,4 +1006,19 @@ public:
 HRESULT GetFileVersion(LPCWSTR wszFilePath, ULARGE_INTEGER* pFileVersion);
 #endif // !TARGET_UNIX
 
+#ifdef TARGET_64BIT
+// We use modified Daniel Lemire's fastmod algorithm (https://github.com/dotnet/runtime/pull/406),
+// which allows to avoid the long multiplication if the divisor is less than 2**31.
+// This is a copy of HashHelpers.cs, see that impl (or linked PR) for more details
+inline UINT64 GetFastModMultiplier(UINT32 divisor)
+{
+    return UINT64_MAX / divisor + 1;
+}
+
+inline UINT32 FastMod(UINT32 value, UINT32 divisor, UINT64 multiplier)
+{
+    return (UINT32)(((((multiplier * value) >> 32) + 1) * divisor) >> 32);
+}
+#endif
+
 #endif /* _H_UTIL */