mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* Speed up KConfig - fix hashtable confusion
@ 2026-11-01 20:08 Linus Torvalds
  2026-11-01 23:44 ` Julian Braha
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Linus Torvalds @ 2026-11-01 20:08 UTC (permalink / raw)
  To: Nathan Chancellor, Nicolas Schier, Julian Braha, Lorenzo Stoakes
  Cc: Linux Kbuild mailing list, Linux Kernel Mailing List

[-- Attachment #1: Type: text/plain, Size: 2367 bytes --]

So the Kconfig 'expr_lookup()' function is criminally slow and takes
over 10% of the time of "make oldconfig".

The reason is that it's called just a lot, but it *really* doesn't
help that the scripting versions of the hashtable functions has not
understood the subtler issues of the hashing code that it took from
the kernel side, and in the process got things very very wrong.

The "hash_32()" function is a perfectly fine hashing function - there
are better ones around, but it's fast and "good enough". But when you
use it, you need to understand that it puts the significant bits in
the *high* bits of the result.

The kernel uses a model where you give it the number of bits you want,
and it will pick out those high bits, which makes it all work right.

But the simplified hashtable code for the host code didn't use those
high bits of the key. It just did

    (key) % HASH_SIZE(table)

to pick the hash table entry, and that's basically pessimal. When
hashing an address that typically has the low bits clear, the low bits
of "hash_32()" will *also* be clear. So the hash table ended up being
mostly empty! So even though the hash table was sized appropriately,
it wasn't being very efficient. And while making the hash table larger
mostly hides the problem, it's the wrong solution, because the cache
footprint gets worse.

Teach "hash_head()" to use the high bits of the key. Because the users
don't pass in the number of bits they want, this is pretty hacky:

    #define HASH_KEY(key,size) \
        (unsigned int)((unsigned int)(key)/(0x100000000ull/(size)))

but at least this improves on things.

I also stopped the expr_lookup() key generation from using XOR of two
pointers, which I suspect just ended up clearing most of the shared
bits. I doubt that makes a difference, but XOR of thigns that may have
shared bits really isn't great.

The cost of 'expr_lookup()' drops by a about factor of four on my
machine, and it's no longer the hottest function in all of 'make
oldconfig'.

No, "make oldconfig" still isn't the fastest thing around, but at
least this removes on particularly stupid reason for it being slower
than it needs to be.

Somebody needs to double-check this. I don't love my HASH_KEY() macro,
but it should work for power-of-2 sizes. And as far as I can tell, all
the hash tables are powers of two.

Comments?

                Linus

[-- Attachment #2: patch.diff --]
[-- Type: text/x-patch, Size: 2008 bytes --]

 scripts/include/hashtable.h | 4 +++-
 scripts/kconfig/expr.c      | 5 +++--
 scripts/kconfig/internal.h  | 4 ----
 3 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/scripts/include/hashtable.h b/scripts/include/hashtable.h
index 45abcb12bfce..a69332ccffcd 100644
--- a/scripts/include/hashtable.h
+++ b/scripts/include/hashtable.h
@@ -13,7 +13,9 @@
 	HASHTABLE_DECLARE(name, size) =						\
 			{ [0 ... ((size) - 1)] = HLIST_HEAD_INIT }
 
-#define hash_head(table, key)		(&(table)[(key) % HASH_SIZE(table)])
+#define HASH_KEY(key,size) (unsigned int)((unsigned int)(key)/(0x100000000ull/(size)))
+
+#define hash_head(table, key)		(&(table)[HASH_KEY(key,HASH_SIZE(table))])
 
 static inline void __hash_init(struct hlist_head *ht, unsigned int sz)
 {
diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c
index 2b91d16bf14f..19ca2ad10425 100644
--- a/scripts/kconfig/expr.c
+++ b/scripts/kconfig/expr.c
@@ -16,7 +16,8 @@
 
 #define DEBUG_EXPR	0
 
-HASHTABLE_DEFINE(expr_hashtable, EXPR_HASHSIZE);
+#define EXPR_HASHSIZE		(1U << 14)
+static HASHTABLE_DEFINE(expr_hashtable, EXPR_HASHSIZE);
 
 static struct expr *expr_eliminate_yn(struct expr *e);
 
@@ -35,7 +36,7 @@ static struct expr *expr_lookup(enum expr_type type, void *l, void *r)
 	struct expr *e;
 	int hash;
 
-	hash = hash_32((unsigned int)type ^ hash_ptr(l) ^ hash_ptr(r));
+	hash = hash_32((unsigned int)type + hash_ptr(l) + hash_ptr(r));
 
 	hash_for_each_possible(expr_hashtable, e, node, hash) {
 		if (e->type == type && e->left._initdata == l &&
diff --git a/scripts/kconfig/internal.h b/scripts/kconfig/internal.h
index d0ffce2dfbba..f244952496eb 100644
--- a/scripts/kconfig/internal.h
+++ b/scripts/kconfig/internal.h
@@ -11,10 +11,6 @@ extern HASHTABLE_DECLARE(sym_hashtable, SYMBOL_HASHSIZE);
 #define for_all_symbols(sym) \
 	hash_for_each(sym_hashtable, sym, node)
 
-#define EXPR_HASHSIZE		(1U << 14)
-
-extern HASHTABLE_DECLARE(expr_hashtable, EXPR_HASHSIZE);
-
 void expr_invalidate_all(void);
 
 struct menu;

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-11-02 19:51 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-01 20:08 Speed up KConfig - fix hashtable confusion Linus Torvalds
2026-11-01 23:44 ` Julian Braha
2026-11-02  6:53   ` Nicolas Schier
2026-11-02  9:47 ` David Laight
2026-11-02 18:20   ` Linus Torvalds
2026-11-02 19:51     ` David Laight
2026-11-02 10:26 ` Sasha Levin
2026-11-02 12:50   ` Nicolas Schier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®