static inline bool first_access_ro(struct kr_cache_top_context *ctx, kru_hash_t hash) {
// struct kr_cache_top_context { uint64_t bloom[4]; }
- static_assert(sizeof(((struct kr_cache_top_context *)0)->bloom[0]) * 8 == 64);
- static_assert(sizeof(((struct kr_cache_top_context *)0)->bloom) * 8 == 64 * 4);
- // expected around 16 unique cache accesses per request context;
- // prob. of collision of the 16th unique access with the preceeding ones: 1/510;
- // 32nd access: 1/45; 64th access: 1/6
+ static_assert(sizeof(((struct kr_cache_top_context *)0)->bloom[0]) * 8 == 32);
+ static_assert(sizeof(((struct kr_cache_top_context *)0)->bloom) * 8 == 32 * 16);
+ // expected around 40 unique cache accesses per request context, up to ~100;
+ // prob. of collision of 40th unique access with the preceeding ones: ~0.5 %;
+ // 60th: ~1.9 %; 80th: 4.5 %; 100th: 8.4 %; 150th: 22 %; 200th; 39 %
+ // -> collision means not counting the cache access in KRU while it should be
uint8_t *h = (uint8_t *)&hash;
- static_assert(sizeof(kru_hash_t) >= 4);
+ static_assert(sizeof(kru_hash_t) >= 8);
- bool accessed = 1ull &
- (ctx->bloom[0] >> (h[0] % 64)) &
- (ctx->bloom[1] >> (h[1] % 64)) &
- (ctx->bloom[2] >> (h[2] % 64)) &
- (ctx->bloom[3] >> (h[3] % 64));
+ bool accessed = 1u &
+ (ctx->bloom[h[0] % 16] >> (h[1] % 32)) &
+ (ctx->bloom[h[2] % 16] >> (h[3] % 32)) &
+ (ctx->bloom[h[4] % 16] >> (h[5] % 32)) &
+ (ctx->bloom[h[6] % 16] >> (h[7] % 32));
return !accessed;
}
if (!first_access_ro(ctx, hash)) return false;
uint8_t *h = (uint8_t *)&hash;
- static_assert(sizeof(kru_hash_t) >= 4);
+ static_assert(sizeof(kru_hash_t) >= 8);
- ctx->bloom[0] |= 1ull << (h[0] % 64);
- ctx->bloom[1] |= 1ull << (h[1] % 64);
- ctx->bloom[2] |= 1ull << (h[2] % 64);
- ctx->bloom[3] |= 1ull << (h[3] % 64);
-
- kr_assert(!first_access_ro(ctx, hash));
+ { // temporal statistics, TODO remove
+ int ones = 0;
+ for (int i = 0; i < 16; i++) {
+ ones += __builtin_popcount(ctx->bloom[i]);
+ }
+ double collision_prob = ones / 512.0; // 1-bit collision
+ collision_prob *= collision_prob; // 2-bit collision
+ collision_prob *= collision_prob; // 4-bit collision
- if (++ctx->cnt > 16) {
- VERBOSE_LOG("BLOOM overfull (%d unique accesses)\n", ctx->cnt);
+ if (collision_prob > 0.1) {
+ VERBOSE_LOG("BLOOM %d unique accesses, collision prob. %5.3f %% (%d/512 ones)\n", ctx->cnt, 100.0 * collision_prob, ones);
+ }
+ ctx->cnt++;
}
+ ctx->bloom[h[0] % 16] |= 1u << (h[1] % 32);
+ ctx->bloom[h[2] % 16] |= 1u << (h[3] % 32);
+ ctx->bloom[h[4] % 16] |= 1u << (h[5] % 32);
+ ctx->bloom[h[6] % 16] |= 1u << (h[7] % 32);
+
+ kr_assert(!first_access_ro(ctx, hash));
+
return true;
}
#if USE_AES
/// 4-8 rounds should be an OK choice, most likely.
#define AES_ROUNDS 4
-#else
+#endif //#else
+// use SipHash also for variable-length keys with otherwise optimized variant
#include "contrib/openbsd/siphash.h"
/// 1,3 should be OK choice, probably.
SIPHASH_RC = 1,
SIPHASH_RF = 3,
};
-#endif
+//#endif
#if USE_AVX2 || USE_SSE41 || USE_AES
#include <immintrin.h>
kru_hash_t hash;
static_assert(sizeof(kru_hash_t) * 8 <= 64);
-#if !USE_AES
- hash = SipHash(&kru->hash_key, SIPHASH_RC, SIPHASH_RF, key, key_size);
-#else
- // TODO
- hash = 3;
- for (size_t i = 0; i < key_size; i++) {
- hash = hash * 257 + key[i];
- }
-#endif
+ // We use SipHash even for otherwise optimized KRU variant, which has diffent type of hash_key.
+ static_assert(sizeof(kru->hash_key) >= sizeof(SIPHASH_KEY));
+ SIPHASH_KEY *hash_key = (void *)&kru->hash_key;
+
+ hash = SipHash(hash_key, SIPHASH_RC, SIPHASH_RF, key, key_size);
return hash;
}