]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/generic/array: avoid quadratic work for long arrays
authorVladimír Čunát <vladimir.cunat@nic.cz>
Mon, 8 Aug 2022 16:40:15 +0000 (18:40 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Mon, 8 Aug 2022 17:08:11 +0000 (19:08 +0200)
For long arrays we really want to increase their length by a fraction.
Otherwise it will cost lots of CPU.  Doubling seems customary,
though I could imagine e.g. keeping the +50% growth on longest arrays.

I finally got sufficiently angry with this piece of code when debugging
https://forum.turris.cz/t/how-to-debug-a-custom-hosts-file-for-kresd/17449
though in that case it wasn't the main source of inefficiency.

CI: two of the mysterious/bogus warnings around arrays disappeared.

.gitlab-ci.yml
lib/generic/array.h
lib/utils.c

index 11f360e0ffc50344d512e1bc01954b43901dd1ba..baa09aca7874aa38cf755d91516b0e52b7095fdf 100644 (file)
@@ -243,7 +243,7 @@ lint:scan-build:
   script:
     - export SCANBUILD="$(realpath ./scripts/run-scanbuild-with-args.sh)"
     - ninja -C build_ci* scan-build || true
-    - test "$(ls build_ci*/meson-logs/scanbuild/*/report-*.html | wc -l)" = 24 # we have this many errors ATM :-)
+    - test "$(ls build_ci*/meson-logs/scanbuild/*/report-*.html | wc -l)" = 22 # we have this many errors ATM :-)
 
 lint:tidy:
   <<: *after_build
index b20a802a6e7a2f91bbb078e00dcd9b06b9110ee3..6f969a2228dbff719f3a58e647b8be4ecee1fcb0 100644 (file)
 #pragma once
 #include <stdlib.h>
 
-/** Simplified Qt containers growth strategy. */
-static inline size_t array_next_count(size_t want)
+/** Choose array length when it overflows. */
+static inline size_t array_next_count(size_t elm_size, size_t want, size_t have)
 {
-       if (want < 2048) {
-               return (want < 20) ? want + 4 : want * 2;
-       } else {
-               return want + 2048;
-       }
+       if (want >= have * 2) // We amortized enough and maybe more won't be needed.
+               return want;
+       const size_t want_b = want * elm_size;
+       if (want_b < 64) // Short arrays are cheap to copy; get just one extra.
+               return want + 1;
+       if (want_b < 1024) // 50% growth amortizes to roughly 3 copies per element.
+               return want + want / 2;
+       return want * 2; // Doubling growth amortizes to roughly 2 copies per element.
 }
 
 /** @internal Incremental memory reservation */
@@ -70,7 +73,7 @@ static inline int array_std_reserve(void *baton, void **mem, size_t elm_size, si
                return 0;
        }
        /* Simplified Qt containers growth strategy */
-       size_t next_size = array_next_count(want);
+       size_t next_size = array_next_count(elm_size, want, *have);
        void *mem_new = realloc(*mem, next_size * elm_size);
        if (mem_new != NULL) {
                *mem = mem_new;
index 46d3fb65af0445652cad779b98a64f06edb19ef4..da2b8236c8d1c91236ba7080f4bf4b28dea7696f 100644 (file)
@@ -185,7 +185,7 @@ int kr_memreserve(void *baton, void **mem, size_t elm_size, size_t want, size_t
         return 0;
     } else {
         knot_mm_t *pool = baton;
-        size_t next_size = array_next_count(want);
+        size_t next_size = array_next_count(elm_size, want, *have);
         void *mem_new = mm_alloc(pool, next_size * elm_size);
         if (mem_new != NULL) {
            if (*mem) { /* 0-length memcpy from NULL isn't technically OK */