1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-06-05 11:24:17 +03:00

Foundation Classes - HashUtils NoExcept optimization #473

Refactor hash functions in Standard_HashUtils for improved performanceю
Optimized load_bytes functionality.
Making all hash function noexcept
This commit is contained in:
Pasukhin Dmitry 2025-04-04 16:38:56 +01:00 committed by GitHub
parent 81efe3d3ed
commit 638b0bfc40
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 62 additions and 34 deletions

View File

@ -28,28 +28,29 @@ namespace opencascade
//! The default value for the seed is optimal for general cases at a certain hash size. //! The default value for the seed is optimal for general cases at a certain hash size.
namespace MurmurHash namespace MurmurHash
{ {
uint32_t MurmurHash2A(const void* theKey, int theLen, uint32_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); uint64_t MurmurHash64A(const void* theKey, int theLen, uint64_t theSeed) noexcept;
template <typename T1, typename T = size_t> template <typename T1, typename T = size_t>
typename std::enable_if<sizeof(T) == 8, uint64_t>::type hash_combine( typename std::enable_if<sizeof(T) == 8, uint64_t>::type hash_combine(
const T1& theValue, const T1& theValue,
const int theLen = sizeof(T1), const int theLen = sizeof(T1),
const T theSeed = 0xA329F1D3A586ULL) const T theSeed = 0xA329F1D3A586ULL) noexcept
{ {
return MurmurHash::MurmurHash64A(&theValue, theLen, theSeed); return MurmurHash::MurmurHash64A(&theValue, theLen, theSeed);
} }
template <typename T1, typename T = size_t> template <typename T1, typename T = size_t>
typename std::enable_if<sizeof(T) != 8, T>::type hash_combine(const T1& theValue, typename std::enable_if<sizeof(T) != 8, T>::type hash_combine(
const int theLen = sizeof(T1), const T1& theValue,
const T theSeed = 0xc70f6907U) const int theLen = sizeof(T1),
const T theSeed = 0xc70f6907U) noexcept
{ {
return static_cast<T>(MurmurHash::MurmurHash2A(&theValue, theLen, theSeed)); return static_cast<T>(MurmurHash::MurmurHash2A(&theValue, theLen, theSeed));
} }
template <typename T = size_t> template <typename T = size_t>
constexpr T optimalSeed() constexpr T optimalSeed() noexcept
{ {
return sizeof(T) == 8 ? static_cast<T>(0xA329F1D3A586ULL) : static_cast<T>(0xc70f6907U); 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. //! The default value for the seed is optimal for general cases at a certain hash size.
namespace FNVHash namespace FNVHash
{ {
uint32_t FNVHash1A(const void* theKey, int theLen, uint32_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); uint64_t FNVHash64A(const void* theKey, int theLen, uint64_t theSeed) noexcept;
template <typename T1, typename T = size_t> template <typename T1, typename T = size_t>
static typename std::enable_if<sizeof(T) == 8, uint64_t>::type hash_combine( static typename std::enable_if<sizeof(T) == 8, uint64_t>::type hash_combine(
const T1& theValue, const T1& theValue,
const int theLen = sizeof(T1), const int theLen = sizeof(T1),
const T theSeed = 14695981039346656037ULL) const T theSeed = 14695981039346656037ULL) noexcept
{ {
return FNVHash::FNVHash64A(&theValue, theLen, theSeed); return FNVHash::FNVHash64A(&theValue, theLen, theSeed);
} }
template <typename T1, typename T = size_t> template <typename T1, typename T = size_t>
static typename std::enable_if<sizeof(T) != 8, T>::type hash_combine(const T1& theValue, static typename std::enable_if<sizeof(T) != 8, T>::type hash_combine(
const int theLen = sizeof(T1), const T1& theValue,
const T theSeed = 2166136261U) const int theLen = sizeof(T1),
const T theSeed = 2166136261U) noexcept
{ {
return static_cast<T>(FNVHash::FNVHash1A(&theValue, theLen, theSeed)); return static_cast<T>(FNVHash::FNVHash1A(&theValue, theLen, theSeed));
} }
template <typename T = size_t> template <typename T = size_t>
constexpr T optimalSeed() constexpr T optimalSeed() noexcept
{ {
return sizeof(T) == 8 ? static_cast<T>(14695981039346656037ULL) : static_cast<T>(2166136261U); return sizeof(T) == 8 ? static_cast<T>(14695981039346656037ULL) : static_cast<T>(2166136261U);
} }
}; // namespace FNVHash }; // namespace FNVHash
template <typename T1, typename T = size_t> 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); return opencascade::MurmurHash::hash_combine<T1, T>(theValue);
} }
template <typename T1, typename T = size_t> 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); return opencascade::MurmurHash::hash_combine<T1, T>(*theKey, theLen);
} }
template <typename T1, typename T = size_t> 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); return opencascade::MurmurHash::hash_combine<T1, T>(theValue, theLen, theSeed);
} }

View File

@ -19,24 +19,50 @@ namespace MurmurHash
{ {
namespace MurmurHashUtils namespace MurmurHashUtils
{ {
inline uint64_t shift_mix(uint64_t theV) inline uint64_t shift_mix(uint64_t theV) noexcept
{ {
return theV ^ (theV >> 47); return theV ^ (theV >> 47);
} }
// Loads n bytes, where 1 <= n < 8. // Loads n bytes, where 1 <= n < 8
inline uint64_t load_bytes(const char* thePnt, int theNb) inline uint64_t load_bytes(const char* thePnt, int theNb) noexcept
{ {
uint64_t aRes = 0; // Initialize result value
--theNb; uint64_t aResult = 0;
do
aRes = (aRes << 8) + static_cast<unsigned char>(thePnt[theNb]); // Use switch with fall-through for better performance and branch prediction
while (--theNb >= 0); switch (theNb)
return aRes; {
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> template <typename T>
inline T unaligned_load(const char* thePnt) inline T unaligned_load(const char* thePnt) noexcept
{ {
T aRes; T aRes;
memcpy(&aRes, thePnt, sizeof(aRes)); memcpy(&aRes, thePnt, sizeof(aRes));
@ -48,7 +74,7 @@ inline T unaligned_load(const char* thePnt)
// function : MurmurHash64A // function : MurmurHash64A
// purpose : // 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; static constexpr uint64_t aMul = (((uint64_t)0xc6a4a793UL) << 32UL) + (uint64_t)0x5bd1e995UL;
const char* const aBuf = static_cast<const char*>(theKey); 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 // function : MurmurHash2A
// purpose : // 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; constexpr uint32_t aMul = 0x5bd1e995;
uint32_t aHash = theSeed ^ theLen; uint32_t aHash = theSeed ^ theLen;
const char* aBuf = static_cast<const char*>(theKey); const char* aBuf = static_cast<const char*>(theKey);
// Mix 4 bytes at a time into the hash. // Mix 4 bytes at a time into the hash.
while (theLen >= 4) while (theLen >= 4)
@ -131,7 +157,7 @@ namespace FNVHash
// function : FNVHash1A // function : FNVHash1A
// purpose : // 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); const char* cptr = static_cast<const char*>(theKey);
for (; theLen; --theLen) for (; theLen; --theLen)
@ -146,7 +172,7 @@ inline uint32_t FNVHash1A(const void* theKey, int theLen, uint32_t theSeed)
// function : FNVHash64A // function : FNVHash64A
// purpose : // 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); const char* cptr = static_cast<const char*>(theKey);
for (; theLen; --theLen) for (; theLen; --theLen)