Some hash support improvments

This commit is contained in:
Andrea Mazzoleni 2026-01-08 18:47:53 +01:00
parent 00dc3e6a48
commit cc2bdad335
5 changed files with 184 additions and 25 deletions

View file

@ -584,6 +584,12 @@ void selftest(void)
/* LCOV_EXCL_STOP */
}
if (util_selftest() != 0) {
/* LCOV_EXCL_START */
log_fatal("Failed UTIL test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
test_hash();
test_crc32c();
test_tommy();

View file

@ -219,17 +219,7 @@ void speed(int period, int nd, int size)
printf("\n");
printf("%8s", "hash");
#ifdef CONFIG_X86
if (sizeof(void *) == 4 && !raid_cpu_has_slowmult())
printf("%8s", "murmur3");
else
printf("%8s", "spooky2");
#else
if (sizeof(void *) == 4)
printf("%8s", "murmur3");
else
printf("%8s", "spooky2");
#endif
printf("%8s", memhashname(membesthash()));
fflush(stdout);
SPEED_START {

View file

@ -1346,17 +1346,7 @@ void state_config(struct snapraid_state* state, const char* path, const char* co
} else if (state->opt.force_spooky2) {
state->besthash = HASH_SPOOKY2;
} else {
#ifdef CONFIG_X86
if (sizeof(void*) == 4 && !raid_cpu_has_slowmult())
state->besthash = HASH_MURMUR3;
else
state->besthash = HASH_SPOOKY2;
#else
if (sizeof(void*) == 4)
state->besthash = HASH_MURMUR3;
else
state->besthash = HASH_SPOOKY2;
#endif
state->besthash = membesthash();
}
/* by default use the best hash */

View file

@ -474,18 +474,16 @@ static inline uint64_t util_rotl64(uint64_t x, int8_t r)
* Rotate right.
* In x86/x64 they are optimized with a single assembler instruction.
*/
#if 0 /* unused */
static inline uint32_t util_rotr32(uint32_t x, int8_t r)
{
return (x >> r) | (x << (32 - r));
}
#endif
static inline uint64_t util_rotr64(uint64_t x, int8_t r)
{
return (x >> r) | (x << (64 - r));
}
/**
* Swap endianness.
* They are needed only if BigEndian.
@ -566,9 +564,169 @@ static inline void util_write64(void* ptr, uint64_t v)
memcpy(ptr, &v, sizeof(v));
}
/*
* 64*64 -> 128bit multiply function.
*
* @param A Address of 64-bit number.
* @param B Address of 64-bit number.
*
* Calculates 128-bit C = *A * *B.
*
* Overwrites A contents with C's low 64 bits.
* Overwrites B contents with C's high 64 bits.
*/
#if defined(__x86_64__) || defined(_M_X64)
static inline void util_mum(uint64_t *A, uint64_t *B)
{
uint64_t a = *A;
uint64_t b = *B;
uint64_t low, high;
__asm__ (
"mulq %3"
: "=a" (low), "=d" (high)
: "a" (a), "rm" (b)
: "cc"
);
*A = low;
*B = high;
}
#elif defined(__aarch64__) || defined(_M_ARM64)
static inline void util_mum(uint64_t *A, uint64_t *B)
{
uint64_t a = *A;
uint64_t b = *B;
uint64_t low = a * b;
uint64_t high;
__asm__ (
"umulh %0, %1, %2"
: "=r" (high)
: "r" (a), "r" (b)
);
*A = low;
*B = high;
}
#elif defined(__SIZEOF_INT128__)
static inline void util_mum(uint64_t *A, uint64_t *B)
{
__uint128_t product = ((__uint128_t)*A) * ((__uint128_t)*B);
*A = (uint64_t)product;
*B = (uint64_t)(product >> 64);
}
#else
static inline void util_mum(uint64_t *A, uint64_t *B)
{
uint64_t a = *A;
uint64_t b = *B;
uint64_t a_lo = (uint32_t)a;
uint64_t a_hi = a >> 32;
uint64_t b_lo = (uint32_t)b;
uint64_t b_hi = b >> 32;
uint64_t p0 = a_lo * b_lo;
uint64_t p1 = a_lo * b_hi;
uint64_t p2 = a_hi * b_lo;
uint64_t p3 = a_hi * b_hi;
uint64_t middle = p1 + (p0 >> 32) + (uint32_t)p2;
uint64_t low = (middle << 32) | (uint32_t)p0;
uint64_t high = p3 + (middle >> 32) + (p2 >> 32);
*A = low;
*B = high;
}
#endif
int util_selftest(void)
{
/* rotate left 32-bit */
if (util_rotl32(0x00000001U, 1) != 0x00000002U) return -1;
if (util_rotl32(0x80000000U, 1) != 0x00000001U) return -1;
if (util_rotl32(0x00000001U, 31) != 0x80000000U) return -1;
if (util_rotl32(0x12345678U, 4) != 0x23456781U) return -1;
if (util_rotl32(0x12345678U, 0) != 0x12345678U) return -1;
if (util_rotl32(0x12345678U, 32) != 0x12345678U) return -1; /* r % 32 == 0 */
/* rotate right 32-bit */
if (util_rotr32(0x00000001U, 1) != 0x80000000U) return -1;
if (util_rotr32(0x80000000U, 1) != 0x40000000U) return -1;
if (util_rotr32(0x00000002U, 1) != 0x00000001U) return -1;
if (util_rotr32(0x12345678U, 4) != 0x81234567U) return -1;
/* rotate left 64-bit */
if (util_rotl64(1ULL, 1) != 2ULL) return -1;
if (util_rotl64(1ULL << 63, 1) != 1ULL) return -1;
if (util_rotl64(0x123456789ABCDEF0ULL, 8) != 0x3456789ABCDEF012ULL) return -1;
if (util_rotl64(0x123456789ABCDEF0ULL, 0) != 0x123456789ABCDEF0ULL) return -1;
/* rotate right 64-bit */
if (util_rotr64(1ULL, 1) != (1ULL << 63)) return -1;
if (util_rotr64(2ULL, 1) != 1ULL) return -1;
if (util_rotr64(0x123456789ABCDEF0ULL, 8) != 0xF0123456789ABCDEULL) return -1;
/* util_mum tests */
uint64_t a, b;
/* small values */
a = 3; b = 4;
util_mum(&a, &b);
if (a != 12 || b != 0) return -1;
/* 1 * anything */
a = 0xFFFFFFFFFFFFFFFFULL; b = 1;
util_mum(&a, &b);
if (a != 0xFFFFFFFFFFFFFFFFULL || b != 0) return -1;
/* (2^32) * (2^32) = 2^64 */
a = 1ULL << 32; b = 1ULL << 32;
util_mum(&a, &b);
if (a != 0 || b != 1) return -1;
/* max uint64 * max uint64 */
a = 0xFFFFFFFFFFFFFFFFULL; b = 0xFFFFFFFFFFFFFFFFULL;
util_mum(&a, &b);
if (a != 1 || b != 0xFFFFFFFFFFFFFFFEULL) return -1;
/* larger random-ish test */
a = 0x0123456789ABCDEFULL;
b = 0xFEDCBA9876543210ULL;
util_mum(&a, &b);
if (a != 0x2236D88FE5618CF0ULL || b != 0x121FA00AD77D742ULL) return -1;
return 0;
}
/****************************************************************************/
/* hash */
const char* memhashname(unsigned kind)
{
switch (kind) {
case HASH_MURMUR3 : return "murmur3";
case HASH_SPOOKY2 : return "spooky2";
case HASH_METRO : return "metro";
}
return 0;
}
unsigned membesthash(void)
{
#ifdef CONFIG_X86
if (sizeof(void*) == 4 && !raid_cpu_has_slowmult())
return HASH_MURMUR3;
else
return HASH_SPOOKY2;
#else
if (sizeof(void*) == 4)
return HASH_MURMUR3;
else
return HASH_SPOOKY2;
#endif
}
#include "murmur3.c"
#include "spooky2.c"
#include "metro.c"

View file

@ -191,6 +191,16 @@ void crc32c_init(void);
#define HASH_SPOOKY2 2
#define HASH_METRO 3
/**
* Return the hash that is expected to be the fastest in this architecture
*/
unsigned membesthash(void);
/**
* Return the name of the hash
*/
const char* memhashname(unsigned kind);
/**
* Compute the HASH of a memory block.
* Seed is a 128 bit vector.
@ -207,6 +217,11 @@ const char* hash_config_name(unsigned kind);
*/
unsigned memdiff(const unsigned char* data1, const unsigned char* data2, size_t size);
/**
* Unit test
*/
int util_selftest(void);
/****************************************************************************/
/* lock */