From: Jim Cromie <jim.cromie@gmail.com>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Lorenzo Stoakes <ljs@kernel.org>, Kees Cook <kees@kernel.org>,
David Laight <david.laight.linux@gmail.com>,
Masahiro Yamada <masahiroy@kernel.org>,
linux-kernel@vger.kernel.org, linux-kbuild@vger.kernel.org,
bpf@vger.kernel.org, Jim Cromie <jim.cromie@gmail.com>
Subject: [PATCH v4 2/4] kallsyms: Match compressed tokens on the fly during binary search
Date: Tue, 22 Sep 2026 14:08:19 -0600 [thread overview]
Message-ID: <20260922-ksyms-tune-v4-2-92acea84b911@gmail.com> (raw)
In-Reply-To: <20260922-ksyms-tune-v4-0-92acea84b911@gmail.com>
kallsyms_lookup_names() runs a binary search across kallsyms_names[],
a packed array of ~130k encoded kernel symbols. For each of the ~17
comparisons in the search, it currently decompresses the candidate
symbol into a temporary buffer on the stack before calling strcmp().
Comparing raw tokens directly in compressed space is impossible. The
BPE token table assigns values by frequency, not alphabetical order
(e.g. token 0x05 might expand to "zebra" while 0x42 expands to "apple"),
so comparing raw token values scrambles lexicographical order.
However, full string expansion is equally wasteful: roughly 16 of the
17 binary search steps fail within the first two characters.
Introduce kallsyms_strcmp_symbol() to compare ASCII queries against
compressed tokens on the fly. It walks kallsyms_token_index and
kallsyms_token_table incrementally, matching characters directly and
bailing out on the first character mismatch without expanding subsequent
tokens.
This optimization:
0. Avoids decompressing non-matching tokens, short-circuiting ~94% of
binary search character expansions without adding any tables in
.rodata.
1. Drops the 512-byte namebuf buffer from the kernel stack in
kallsyms_lookup_names().
2. Cuts unindexed lookup latency by ~530 ns (~14% faster) while leaving
sequential address ordering and kallsyms_expand_symbol() streaming
invariants intact for /proc/kallsyms and table walks.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
Changes in v3:
- Reorder patch ahead of dynamic batch index in series, establishing an
active proof of string matching savings on unindexed baseline
(addresses David Laight review).
- Optimize kallsyms_strcmp_symbol(): drop skipped_first tracking and
test len at loop bottom (addresses David Laight review).
- Guard first token with while (*tptr) to handle 1-byte type tokens.
- Introduce get_symbol_data() helper in this patch for reuse in later
subsystems.
Changes in v2:
- Rebase onto mainline v7.3-rc4, removing external dependencies on
Lorenzo Stoakes' kbuild series.
---
kernel/kallsyms.c | 94 ++++++++++++++++++++++++++++++++++---------------------
1 file changed, 59 insertions(+), 35 deletions(-)
diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index aec2f06858af..d18d78e626db 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -34,6 +34,21 @@
#include "kallsyms_internal.h"
+/*
+ * Get the compressed symbol length and data pointer.
+ */
+static inline const u8 *get_symbol_data(unsigned int off, unsigned int *len)
+{
+ const u8 *p = &kallsyms_names[off];
+ unsigned int l = *p++;
+
+ if (unlikely(l & 0x80))
+ l = (l & 0x7F) | (*p++ << 7);
+ *len = l;
+
+ return p;
+}
+
/*
* Expand a compressed symbol data into the resulting uncompressed string,
* if uncompressed string is too long (>= maxlen), it will be truncated,
@@ -42,28 +57,12 @@
static unsigned int kallsyms_expand_symbol(unsigned int off,
char *result, size_t maxlen)
{
- int len, skipped_first = 0;
+ int skipped_first = 0;
const char *tptr;
- const u8 *data;
+ unsigned int len;
+ const u8 *data = get_symbol_data(off, &len);
- /* Get the compressed symbol length from the first symbol byte. */
- data = &kallsyms_names[off];
- len = *data;
- data++;
- off++;
-
- /* If MSB is 1, it is a "big" symbol, so needs an additional byte. */
- if ((len & 0x80) != 0) {
- len = (len & 0x7F) | (*data << 7);
- data++;
- off++;
- }
-
- /*
- * Update the offset to return the offset for the next symbol on
- * the compressed stream.
- */
- off += len;
+ off = (data - kallsyms_names) + len;
/*
* For every byte on the compressed symbol data, copy the table
@@ -101,14 +100,43 @@ static unsigned int kallsyms_expand_symbol(unsigned int off,
*/
static char kallsyms_get_symbol_type(unsigned int off)
{
- /*
- * Get just the first code, look it up in the token table,
- * and return the first char from this token. If MSB of length
- * is 1, it is a "big" symbol, so needs an additional byte.
- */
- if (kallsyms_names[off] & 0x80)
- off++;
- return kallsyms_token_table[kallsyms_token_index[kallsyms_names[off + 1]]];
+ unsigned int len;
+ const u8 *data = get_symbol_data(off, &len);
+
+ return kallsyms_token_table[kallsyms_token_index[*data]];
+}
+
+/*
+ * Compare an uncompressed ASCII string against a compressed symbol table entry.
+ * Returns negative if name < sym, positive if name > sym, 0 if equal.
+ * Exits immediately on the first mismatched character without decompressing
+ * the rest of the symbol name.
+ */
+static int kallsyms_strcmp_symbol(unsigned int off, const char *name)
+{
+ const char *tptr;
+ unsigned int len;
+ const u8 *data = get_symbol_data(off, &len);
+
+ tptr = &kallsyms_token_table[kallsyms_token_index[*data++]] + 1;
+ while (*tptr) {
+ int diff = (unsigned char)*name++ - (unsigned char)*tptr++;
+
+ if (diff)
+ return diff;
+ }
+
+ while (--len) {
+ tptr = &kallsyms_token_table[kallsyms_token_index[*data++]];
+ do {
+ int diff = (unsigned char)*name++ - (unsigned char)*tptr++;
+
+ if (diff)
+ return diff;
+ } while (*tptr);
+ }
+
+ return (unsigned char)*name;
}
@@ -174,7 +202,6 @@ static int kallsyms_lookup_names(const char *name,
int ret;
int low, mid, high;
unsigned int seq, off;
- char namebuf[KSYM_NAME_LEN];
low = 0;
high = kallsyms_num_syms - 1;
@@ -183,8 +210,7 @@ static int kallsyms_lookup_names(const char *name,
mid = low + (high - low) / 2;
seq = get_symbol_seq(mid);
off = get_symbol_offset(seq);
- kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
- ret = strcmp(name, namebuf);
+ ret = kallsyms_strcmp_symbol(off, name);
if (ret > 0)
low = mid + 1;
else if (ret < 0)
@@ -200,8 +226,7 @@ static int kallsyms_lookup_names(const char *name,
while (low) {
seq = get_symbol_seq(low - 1);
off = get_symbol_offset(seq);
- kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
- if (strcmp(name, namebuf))
+ if (kallsyms_strcmp_symbol(off, name) != 0)
break;
low--;
}
@@ -212,8 +237,7 @@ static int kallsyms_lookup_names(const char *name,
while (high < kallsyms_num_syms - 1) {
seq = get_symbol_seq(high + 1);
off = get_symbol_offset(seq);
- kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
- if (strcmp(name, namebuf))
+ if (kallsyms_strcmp_symbol(off, name) != 0)
break;
high++;
}
--
2.55.0
next prev parent reply other threads:[~2026-09-22 20:08 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-22 20:08 [PATCH v4 0/4] kallsyms: Accelerate symbol name lookups by ~19x Jim Cromie
2026-09-22 20:08 ` [PATCH v4 1/4] kallsyms: Add test_kallsyms_perf module to benchmark lookup latency Jim Cromie
2026-09-22 20:08 ` Jim Cromie [this message]
2026-09-22 20:08 ` [PATCH v4 3/4] kallsyms: Add dynamic lookup index for batch resolution Jim Cromie
2026-09-22 20:08 ` [PATCH v4 4/4] kallsyms: Unroll 24-bit sequence reconstruction in get_symbol_seq() Jim Cromie
2026-09-23 7:12 ` [PATCH v4 0/4] kallsyms: Accelerate symbol name lookups by ~19x Kees Cook
2026-09-23 10:00 ` David Laight
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260922-ksyms-tune-v4-2-92acea84b911@gmail.com \
--to=jim.cromie@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=bpf@vger.kernel.org \
--cc=david.laight.linux@gmail.com \
--cc=kees@kernel.org \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ljs@kernel.org \
--cc=masahiroy@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®