]> OCCT Git - occt.git/commitdiff
Foundation Classes - HashUtils NoExcept optimization #473
authorPasukhin Dmitry <dpasukhi@opencascade.com>
Fri, 4 Apr 2025 15:38:56 +0000 (16:38 +0100)
committerGitHub <noreply@github.com>
Fri, 4 Apr 2025 15:38:56 +0000 (16:38 +0100)
Refactor hash functions in Standard_HashUtils for improved performanceÑŽ
Optimized load_bytes functionality.
Making all hash function noexcept

src/FoundationClasses/TKernel/Standard/Standard_HashUtils.hxx
src/FoundationClasses/TKernel/Standard/Standard_HashUtils.lxx

index 1abc35afd4501ed963030009f2953ea8c22026b8..36f5b05461ecbccdc9b17f1fd6c1c315bdcb5ce2 100644 (file)
@@ -28,28 +28,29 @@ namespace opencascade
 //! The default value for the seed is optimal for general cases at a certain hash size.
 namespace MurmurHash
 {
-uint32_t MurmurHash2A(const void* theKey, int theLen, uint32_t theSeed);
-uint64_t MurmurHash64A(const void* theKey, int theLen, uint64_t theSeed);
+uint32_t MurmurHash2A(const void* theKey, int theLen, uint32_t theSeed) noexcept;
+uint64_t MurmurHash64A(const void* theKey, int theLen, uint64_t theSeed) noexcept;
 
 template <typename T1, typename T = size_t>
 typename std::enable_if<sizeof(T) == 8, uint64_t>::type hash_combine(
   const T1& theValue,
   const int theLen  = sizeof(T1),
-  const T   theSeed = 0xA329F1D3A586ULL)
+  const T   theSeed = 0xA329F1D3A586ULL) noexcept
 {
   return MurmurHash::MurmurHash64A(&theValue, theLen, theSeed);
 }
 
 template <typename T1, typename T = size_t>
-typename std::enable_if<sizeof(T) != 8, T>::type hash_combine(const T1& theValue,
-                                                              const int theLen  = sizeof(T1),
-                                                              const T   theSeed = 0xc70f6907U)
+typename std::enable_if<sizeof(T) != 8, T>::type hash_combine(
+  const T1& theValue,
+  const int theLen  = sizeof(T1),
+  const T   theSeed = 0xc70f6907U) noexcept
 {
   return static_cast<T>(MurmurHash::MurmurHash2A(&theValue, theLen, theSeed));
 }
 
 template <typename T = size_t>
-constexpr T optimalSeed()
+constexpr T optimalSeed() noexcept
 {
   return sizeof(T) == 8 ? static_cast<T>(0xA329F1D3A586ULL) : static_cast<T>(0xc70f6907U);
 }
@@ -63,47 +64,48 @@ constexpr T optimalSeed()
 //! The default value for the seed is optimal for general cases at a certain hash size.
 namespace FNVHash
 {
-uint32_t FNVHash1A(const void* theKey, int theLen, uint32_t theSeed);
-uint64_t FNVHash64A(const void* theKey, int theLen, uint64_t theSeed);
+uint32_t FNVHash1A(const void* theKey, int theLen, uint32_t theSeed) noexcept;
+uint64_t FNVHash64A(const void* theKey, int theLen, uint64_t theSeed) noexcept;
 
 template <typename T1, typename T = size_t>
 static typename std::enable_if<sizeof(T) == 8, uint64_t>::type hash_combine(
   const T1& theValue,
   const int theLen  = sizeof(T1),
-  const T   theSeed = 14695981039346656037ULL)
+  const T   theSeed = 14695981039346656037ULL) noexcept
 {
   return FNVHash::FNVHash64A(&theValue, theLen, theSeed);
 }
 
 template <typename T1, typename T = size_t>
-static typename std::enable_if<sizeof(T) != 8, T>::type hash_combine(const T1& theValue,
-                                                                     const int theLen = sizeof(T1),
-                                                                     const T theSeed  = 2166136261U)
+static typename std::enable_if<sizeof(T) != 8, T>::type hash_combine(
+  const T1& theValue,
+  const int theLen  = sizeof(T1),
+  const T   theSeed = 2166136261U) noexcept
 {
   return static_cast<T>(FNVHash::FNVHash1A(&theValue, theLen, theSeed));
 }
 
 template <typename T = size_t>
-constexpr T optimalSeed()
+constexpr T optimalSeed() noexcept
 {
   return sizeof(T) == 8 ? static_cast<T>(14695981039346656037ULL) : static_cast<T>(2166136261U);
 }
 }; // namespace FNVHash
 
 template <typename T1, typename T = size_t>
-T hash(const T1 theValue) noexcept
+T hash(const T1& theValue) noexcept
 {
   return opencascade::MurmurHash::hash_combine<T1, T>(theValue);
 }
 
 template <typename T1, typename T = size_t>
-T hashBytes(const T1* theKey, int theLen)
+T hashBytes(const T1* theKey, int theLen) noexcept
 {
   return opencascade::MurmurHash::hash_combine<T1, T>(*theKey, theLen);
 }
 
 template <typename T1, typename T = size_t>
-T hash_combine(const T1 theValue, const int theLen, const T theSeed)
+T hash_combine(const T1& theValue, const int theLen, const T theSeed) noexcept
 {
   return opencascade::MurmurHash::hash_combine<T1, T>(theValue, theLen, theSeed);
 }
index 170bfdcd626d1d797359c2541c7d3532b226e14f..240d20dfa7e14c53b036e011a12923ccb74ff466 100644 (file)
@@ -19,24 +19,50 @@ namespace MurmurHash
 {
 namespace MurmurHashUtils
 {
-inline uint64_t shift_mix(uint64_t theV)
+inline uint64_t shift_mix(uint64_t theV) noexcept
 {
   return theV ^ (theV >> 47);
 }
 
-// Loads n bytes, where 1 <= n < 8.
-inline uint64_t load_bytes(const char* thePnt, int theNb)
+// Loads n bytes, where 1 <= n < 8
+inline uint64_t load_bytes(const char* thePnt, int theNb) noexcept
 {
-  uint64_t aRes = 0;
-  --theNb;
-  do
-    aRes = (aRes << 8) + static_cast<unsigned char>(thePnt[theNb]);
-  while (--theNb >= 0);
-  return aRes;
+  // Initialize result value
+  uint64_t aResult = 0;
+
+  // Use switch with fall-through for better performance and branch prediction
+  switch (theNb)
+  {
+    case 7:
+      aResult = (static_cast<uint64_t>(static_cast<unsigned char>(thePnt[6])) << 48) | aResult;
+      Standard_FALLTHROUGH
+    case 6:
+      aResult = (static_cast<uint64_t>(static_cast<unsigned char>(thePnt[5])) << 40) | aResult;
+      Standard_FALLTHROUGH
+    case 5:
+      aResult = (static_cast<uint64_t>(static_cast<unsigned char>(thePnt[4])) << 32) | aResult;
+      Standard_FALLTHROUGH
+    case 4:
+      aResult = (static_cast<uint64_t>(static_cast<unsigned char>(thePnt[3])) << 24) | aResult;
+      Standard_FALLTHROUGH
+    case 3:
+      aResult = (static_cast<uint64_t>(static_cast<unsigned char>(thePnt[2])) << 16) | aResult;
+      Standard_FALLTHROUGH
+    case 2:
+      aResult = (static_cast<uint64_t>(static_cast<unsigned char>(thePnt[1])) << 8) | aResult;
+      Standard_FALLTHROUGH
+    case 1:
+      aResult = static_cast<uint64_t>(static_cast<unsigned char>(thePnt[0])) | aResult;
+      Standard_FALLTHROUGH
+    default:
+      break;
+  }
+
+  return aResult;
 }
 
 template <typename T>
-inline T unaligned_load(const char* thePnt)
+inline T unaligned_load(const char* thePnt) noexcept
 {
   T aRes;
   memcpy(&aRes, thePnt, sizeof(aRes));
@@ -48,7 +74,7 @@ inline T unaligned_load(const char* thePnt)
 // function : MurmurHash64A
 // purpose  :
 //=======================================================================
-inline uint64_t MurmurHash64A(const void* theKey, int theLen, uint64_t theSeed)
+inline uint64_t MurmurHash64A(const void* theKey, int theLen, uint64_t theSeed) noexcept
 {
   static constexpr uint64_t aMul = (((uint64_t)0xc6a4a793UL) << 32UL) + (uint64_t)0x5bd1e995UL;
   const char* const         aBuf = static_cast<const char*>(theKey);
@@ -80,11 +106,11 @@ inline uint64_t MurmurHash64A(const void* theKey, int theLen, uint64_t theSeed)
 // function : MurmurHash2A
 // purpose  :
 //=======================================================================
-inline uint32_t MurmurHash2A(const void* theKey, int theLen, uint32_t theSeed)
+inline uint32_t MurmurHash2A(const void* theKey, int theLen, uint32_t theSeed) noexcept
 {
-  const uint32_t aMul  = 0x5bd1e995;
-  uint32_t       aHash = theSeed ^ theLen;
-  const char*    aBuf  = static_cast<const char*>(theKey);
+  constexpr uint32_t aMul  = 0x5bd1e995;
+  uint32_t           aHash = theSeed ^ theLen;
+  const char*        aBuf  = static_cast<const char*>(theKey);
 
   // Mix 4 bytes at a time into the hash.
   while (theLen >= 4)
@@ -131,7 +157,7 @@ namespace FNVHash
 // function : FNVHash1A
 // purpose  :
 //=======================================================================
-inline uint32_t FNVHash1A(const void* theKey, int theLen, uint32_t theSeed)
+inline uint32_t FNVHash1A(const void* theKey, int theLen, uint32_t theSeed) noexcept
 {
   const char* cptr = static_cast<const char*>(theKey);
   for (; theLen; --theLen)
@@ -146,7 +172,7 @@ inline uint32_t FNVHash1A(const void* theKey, int theLen, uint32_t theSeed)
 // function : FNVHash64A
 // purpose  :
 //=======================================================================
-inline uint64_t FNVHash64A(const void* theKey, int theLen, uint64_t theSeed)
+inline uint64_t FNVHash64A(const void* theKey, int theLen, uint64_t theSeed) noexcept
 {
   const char* cptr = static_cast<const char*>(theKey);
   for (; theLen; --theLen)