]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/cache: increase bloom size, other minor changes docs-cache-kru-8qejro/deployments/7194
authorLukáš Ondráček <lukas.ondracek@nic.cz>
Tue, 15 Jul 2025 13:19:33 +0000 (15:19 +0200)
committerLukáš Ondráček <lukas.ondracek@nic.cz>
Tue, 15 Jul 2025 13:19:33 +0000 (15:19 +0200)
daemon/lua/kres-gen-33.lua
lib/cache/top.c
lib/cache/top.h
utils/cache_gc/categories.c
utils/cache_gc/db.c
utils/cache_gc/kr_cache_gc.c

index 21f5ecf034ac544504cd3b223166d7b9c77ac97c..a56f09c232bd4ef46dcfef0268acfd5c7d4196e6 100644 (file)
@@ -234,7 +234,7 @@ struct kr_extended_error {
        const char *extra_text;
 };
 struct kr_cache_top_context {
-       uint32_t bloom[16];
+       uint32_t bloom[32];
        uint32_t cnt;
 };
 struct kr_request {
index 081c4e5a5d47bb860833f0820fce9a4625a936f0..37cdd363b8969f630e019b4c1293a6f95d7f4e93 100644 (file)
 
 #define FILE_FORMAT_VERSION 1  // fail if different
 
-#define TICK_SEC     1
-#define NORMAL_SIZE  150 // B; normal size of cache entry
+
+#define KRU_CAPACITY(cache_size) (cache_size / 64)   // KRU size is approx. (8 * capacity) B
+       // average entry size seems to be 70-100 B
+       // -> KRU size is around 12.5 % of cache size, so 112.5 % of the set value is required
+#define TICK_SEC        1
+#define NORMAL_SIZE  (150 + KR_CACHE_SIZE_OVERHEAD)  // B; normal size of cache entry
+       // used as baseline for the following
 #define BASE_PRICE   (((kru_price_t)5) << (KRU_PRICE_BITS - 16))
        // for cache entries of NORMAL_SIZE
        // -> normal increment: 5 (16-bit)
@@ -37,25 +42,25 @@ static inline uint32_t ticks_now(void)
 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 == 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 %
+       static_assert(sizeof(((struct kr_cache_top_context *)0)->bloom)    * 8 == 32 * 32);
+               // expected around 40 unique cache accesses per request context, possibly up to ~200;
+               // prob. of collision of 50th unique access with the preceeding ones: ~0.1 %;
+               // 75th: ~0.4 %; 100th: ~1.1 %; 150th: ~3.9 %; 200th: ~8.7 %; 300th: ~23 %; 400th: ~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) >= 8);
 
        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));
+               (ctx->bloom[h[0] % 32] >> (h[1] % 32)) &
+               (ctx->bloom[h[2] % 32] >> (h[3] % 32)) &
+               (ctx->bloom[h[4] % 32] >> (h[5] % 32)) &
+               (ctx->bloom[h[6] % 32] >> (h[7] % 32));
 
        return !accessed;
 }
 
-static inline bool first_access(struct kr_cache_top_context *ctx, kru_hash_t hash) {
+static inline bool first_access(struct kr_cache_top_context *ctx, kru_hash_t hash, bool *overfull) {
        if (!first_access_ro(ctx, hash)) return false;
 
        uint8_t *h = (uint8_t *)&hash;
@@ -63,23 +68,24 @@ static inline bool first_access(struct kr_cache_top_context *ctx, kru_hash_t has
 
        { // temporal statistics, TODO remove
                int ones = 0;
-               for (int i = 0; i < 16; i++) {
+               for (int i = 0; i < 32; 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
+               double collision_prob = ones / 1024.0; // 1-bit collision
+               collision_prob *= collision_prob;      // 2-bit collision
+               collision_prob *= collision_prob;      // 4-bit collision
 
                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);
+                       VERBOSE_LOG("BLOOM %d unique accesses, collision prob. %5.3f %% (%d/1024 ones)\n", ctx->cnt, 100.0 * collision_prob, ones);
+                       *overfull = true;
                }
                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);
+       ctx->bloom[h[0] % 32] |= 1u << (h[1] % 32);
+       ctx->bloom[h[2] % 32] |= 1u << (h[3] % 32);
+       ctx->bloom[h[4] % 32] |= 1u << (h[5] % 32);
+       ctx->bloom[h[6] % 32] |= 1u << (h[7] % 32);
 
        kr_assert(!first_access_ro(ctx, hash));
 
@@ -89,15 +95,16 @@ static inline bool first_access(struct kr_cache_top_context *ctx, kru_hash_t has
 
 int kr_cache_top_init(struct kr_cache_top *top, char *mmap_file, size_t cache_size) {
        size_t size = 0, capacity_log = 0;
-       VERBOSE_LOG("INIT, cache size %d\n", cache_size);
-
        if (cache_size > 0) {
-               const size_t capacity = 2<<19;  // TODO calculate from cache_size
+               const size_t capacity = KRU_CAPACITY(cache_size);
                for (size_t c = capacity - 1; c > 0; c >>= 1) capacity_log++;
 
                size = offsetof(struct top_data, kru) + KRU.get_size(capacity_log);
        } // else use existing file settings
 
+       VERBOSE_LOG("INIT, cache size %d, KRU capacity_log %d\n", cache_size, capacity_log);
+
+
        struct top_data header = {
                .version          = (FILE_FORMAT_VERSION << 1) | kru_using_avx2(),
                .base_price_norm  = BASE_PRICE * NORMAL_SIZE,
@@ -239,16 +246,19 @@ char *kr_cache_top_strkey(void *key, size_t len) {
 
 void kr_cache_top_access(struct kr_cache_top *top, void *key, size_t key_len, size_t data_size, char *debug_label)
 {
+       bool bloom_overfull = false; // XXX tmp
        kru_hash_t hash = KRU.hash_bytes((struct kru *)&top->data->kru, (uint8_t *)key, key_len);
-       const bool unique = top->ctx ? first_access(top->ctx, hash) : true;
+       const bool unique = top->ctx ? first_access(top->ctx, hash, &bloom_overfull) : true;
        const size_t size = kr_cache_top_entry_size(key_len, data_size);
        if (unique) {
                const kru_price_t price = kr_cache_top_entry_price(top, size);
                KRU.load_hash((struct kru *)&top->data->kru, ticks_now(), hash, price);
        }
-       VERBOSE_LOG("ACCESS %-19s%4d B %-5s  %s\n", debug_label, size,
-                       !top->ctx ? "NO_CTX" : unique ? "" : "SKIP",
-                       kr_cache_top_strkey(key, key_len));
+       if (bloom_overfull) {
+               VERBOSE_LOG("ACCESS %-19s%4d B %-5s  %s\n", debug_label, size,
+                               !top->ctx ? "NO_CTX" : unique ? "" : "SKIP",
+                               kr_cache_top_strkey(key, key_len));
+       }
 }
 
 // temporal logging one level under _access
index c0b2b7bd2e584bc0dc018e6756eb276a6326292f..8cdc17e91ecdc8164717c8a9aa9c9edd05561f94 100644 (file)
@@ -13,7 +13,7 @@ struct kr_cache_top {
 };
 
 struct kr_cache_top_context {
-       uint32_t bloom[16]; // size of just one cache-line, but probably not aligned (neither kr_request is)
+       uint32_t bloom[32]; // size of just one cache-line, but probably not aligned (neither kr_request is)
        uint32_t cnt;  // TODO remove this (and propagate to kres-gen)
 };
 
@@ -24,8 +24,10 @@ struct top_data {
        _Alignas(64) uint8_t kru[];
 };
 
+#define KR_CACHE_SIZE_OVERHEAD  16 // B, just guess, probably more; size = key + data + DB overhead
+
 static inline size_t kr_cache_top_entry_size(size_t key_len, size_t data_size) {
-       return key_len + data_size;  // TODO increase by a constant as DB overhead?
+       return key_len + data_size + KR_CACHE_SIZE_OVERHEAD;
 }
 static inline kru_price_t kr_cache_top_entry_price(struct kr_cache_top *top, size_t size) {
        return top->data->base_price_norm / size;
index 96b58702a7781d9456340b7b77521e26fe208ee3..ec1aa773178dbaa0958c1877008b24ea5981c4e4 100644 (file)
@@ -59,7 +59,7 @@ category_t kr_gc_categorize(struct kr_cache_top *top, gc_record_info_t * info, v
 
        const kru_price_t price = kr_cache_top_entry_price(top, info->entry_size);
        const double accesses = (double)((kru_price_t)load << (KRU_PRICE_BITS - 16)) / price;
-       printf("cat %02d %6d l %8.1f acc %6ld B %8ld s  %s\n",
+       kr_log_debug(CACHE, "cat %02d %6d l %8.1f acc %6ld B %8ld s  %s\n",
                res, load, accesses, info->entry_size, info->expires_in,
                kr_cache_top_strkey(key, key_len)
        );
index 40c2340c8de0239223637cfd7f19d3071a3e0bab..620f283101af53a87ebf182df1107c97ea1664ec 100644 (file)
@@ -81,10 +81,9 @@ int kr_gc_key_consistent(knot_db_val_t key)
                i = 1;
        } else {
                /* find the first double zero in the key */
-               for (i = 2; kd[i - 1] || kd[i - 2]; ++i) {
-                       if (kr_fails_assert(i < key.len))
-                               return kr_error(EINVAL);
-               }
+               for (i = 2; (i < key.len) && (kd[i - 1] || kd[i - 2]); ++i);
+               if (kr_fails_assert(i < key.len))
+                       return kr_error(EINVAL);
        }
        // the next character can be used for classification
        switch (kd[i]) {
index 46914a17969479a43954f59cf3208ebcc691b1d0..932d57dd39b17b8deaae015bd49f381bcb789c71 100644 (file)
@@ -213,8 +213,8 @@ int kr_cache_gc(kr_cache_gc_cfg_t *cfg, kr_cache_gc_state_t **state)
                amount_tofree -= cat_size;
        }
 
-       printf("Cache analyzed in %.0lf msecs, %zu records, limit category is %d.\n",
-              kr_timer_elapsed(&timer_analyze) * 1000, cats.records, limit_category);
+       printf("Cache analyzed in %.0lf msecs, %zu records, %.2f B avg., limit category is %d.\n",
+              kr_timer_elapsed(&timer_analyze) * 1000, cats.records, (double)cats_sumsize / cats.records, limit_category);
 
        if (cfg->dry_run) {
                return KNOT_EOK;