From 638b0bfc4082f48382cfa95b749a0fc7e07f6352 Mon Sep 17 00:00:00 2001 From: Pasukhin Dmitry Date: Fri, 4 Apr 2025 16:38:56 +0100 Subject: [PATCH] Foundation Classes - HashUtils NoExcept optimization #473 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor hash functions in Standard_HashUtils for improved performanceÑŽ Optimized load_bytes functionality. Making all hash function noexcept --- .../TKernel/Standard/Standard_HashUtils.hxx | 36 +++++------ .../TKernel/Standard/Standard_HashUtils.lxx | 60 +++++++++++++------ 2 files changed, 62 insertions(+), 34 deletions(-) diff --git a/src/FoundationClasses/TKernel/Standard/Standard_HashUtils.hxx b/src/FoundationClasses/TKernel/Standard/Standard_HashUtils.hxx index 1abc35afd4..36f5b05461 100644 --- a/src/FoundationClasses/TKernel/Standard/Standard_HashUtils.hxx +++ b/src/FoundationClasses/TKernel/Standard/Standard_HashUtils.hxx @@ -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 std::enable_if::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 std::enable_if::type hash_combine(const T1& theValue, - const int theLen = sizeof(T1), - const T theSeed = 0xc70f6907U) +typename std::enable_if::type hash_combine( + const T1& theValue, + const int theLen = sizeof(T1), + const T theSeed = 0xc70f6907U) noexcept { return static_cast(MurmurHash::MurmurHash2A(&theValue, theLen, theSeed)); } template -constexpr T optimalSeed() +constexpr T optimalSeed() noexcept { return sizeof(T) == 8 ? static_cast(0xA329F1D3A586ULL) : static_cast(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 static typename std::enable_if::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 -static typename std::enable_if::type hash_combine(const T1& theValue, - const int theLen = sizeof(T1), - const T theSeed = 2166136261U) +static typename std::enable_if::type hash_combine( + const T1& theValue, + const int theLen = sizeof(T1), + const T theSeed = 2166136261U) noexcept { return static_cast(FNVHash::FNVHash1A(&theValue, theLen, theSeed)); } template -constexpr T optimalSeed() +constexpr T optimalSeed() noexcept { return sizeof(T) == 8 ? static_cast(14695981039346656037ULL) : static_cast(2166136261U); } }; // namespace FNVHash template -T hash(const T1 theValue) noexcept +T hash(const T1& theValue) noexcept { return opencascade::MurmurHash::hash_combine(theValue); } template -T hashBytes(const T1* theKey, int theLen) +T hashBytes(const T1* theKey, int theLen) noexcept { return opencascade::MurmurHash::hash_combine(*theKey, theLen); } template -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(theValue, theLen, theSeed); } diff --git a/src/FoundationClasses/TKernel/Standard/Standard_HashUtils.lxx b/src/FoundationClasses/TKernel/Standard/Standard_HashUtils.lxx index 170bfdcd62..240d20dfa7 100644 --- a/src/FoundationClasses/TKernel/Standard/Standard_HashUtils.lxx +++ b/src/FoundationClasses/TKernel/Standard/Standard_HashUtils.lxx @@ -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(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(static_cast(thePnt[6])) << 48) | aResult; + Standard_FALLTHROUGH + case 6: + aResult = (static_cast(static_cast(thePnt[5])) << 40) | aResult; + Standard_FALLTHROUGH + case 5: + aResult = (static_cast(static_cast(thePnt[4])) << 32) | aResult; + Standard_FALLTHROUGH + case 4: + aResult = (static_cast(static_cast(thePnt[3])) << 24) | aResult; + Standard_FALLTHROUGH + case 3: + aResult = (static_cast(static_cast(thePnt[2])) << 16) | aResult; + Standard_FALLTHROUGH + case 2: + aResult = (static_cast(static_cast(thePnt[1])) << 8) | aResult; + Standard_FALLTHROUGH + case 1: + aResult = static_cast(static_cast(thePnt[0])) | aResult; + Standard_FALLTHROUGH + default: + break; + } + + return aResult; } template -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(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(theKey); + constexpr uint32_t aMul = 0x5bd1e995; + uint32_t aHash = theSeed ^ theLen; + const char* aBuf = static_cast(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(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(theKey); for (; theLen; --theLen)