From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C620C4F93AF; Tue, 29 Sep 2026 18:07:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790705251; cv=none; b=denldlP5urJNl2qJihkSKg10x5C0gOdVObQsNY+3ptTF71XiJN5FChMJHVnJQLQzVyVQO3yR2r9Q0IVHhodMD5P/xnU6wsL6h3qohpLdiVpPQpv0Q7PUH1ppWmw6WjNAs/jhbGa0QiMevnTIOh/cKXFT8cxTACgBRW/YgSIUMfU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790705251; c=relaxed/simple; bh=Ggz1UUM43554Nty4rDDk9gjTjgoBO/hZuuIpxY4kNrY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=fY8FEy9w5S8erskh2tcEQb6Fc3a+B8967ZntKzuX7nkkUB7w5gVlK78DngmEbeUiBTDLAt/m0TJsoeyTWbBTlOp6UIfnLWBQ3gnpm12pzYG4Ni7TVp7sDFQve8E1USRp4EuqOi3ifsJiMuUBCdxXB8RkKVRR+TsXUah/BoxUhLw= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=X9f2qUyA; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="X9f2qUyA" Received: by smtp.kernel.org (Postfix) with ESMTPS id 6FFF5C4AF12; Tue, 29 Sep 2026 18:07:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1790705251; bh=Ggz1UUM43554Nty4rDDk9gjTjgoBO/hZuuIpxY4kNrY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=X9f2qUyAGI5nJfO96qv1KE6A9NLaQfGi1sbaFNaMgz05SBaKLYoafKwRSuS/SxhOx XfzIhXlI/EPO+m7ECzW/m8tEu7TUBki19jPWjREJ5EXvUneTd+eQ7Fxsnc+QyxWsQ/ av7Ag76WVKDFRotTpQ6+ec3BqyLK6QLrrtHye1IUdxQtaCostoGbiJSkJ6fo/Oyar9 eDH+9scrQX7KMH57qeMp/4LPNkBwhq5MicJCtbaeaoHPlNEgtflaedg3lKD29C6AO+ 1uEBQrJ8lTPQiDx9wBPLfYldW/pePYt+KWJlWw0HU8bRGdIad6fUPUNqeuaEP3SS8t xkdjcWuK1R2yQ== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4FC7DCA5FA5; Tue, 29 Sep 2026 18:07:31 +0000 (UTC) From: Jim Cromie via B4 Relay Date: Tue, 29 Sep 2026 12:07:30 -0600 Subject: [PATCH v7 1/3] kallsyms: Match compressed tokens on the fly during binary search Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260929-ksyms-tune-v7-1-be568ceef41e@gmail.com> References: <20260929-ksyms-tune-v7-0-be568ceef41e@gmail.com> In-Reply-To: <20260929-ksyms-tune-v7-0-be568ceef41e@gmail.com> To: Andrew Morton Cc: Petr Mladek , Zhen Lei , Luis Chamberlain , Andrey Grodzovsky , Steven Rostedt , Lorenzo Stoakes , Kees Cook , David Laight , Masahiro Yamada , Jiri Olsa , linux-kernel@vger.kernel.org, linux-kbuild@vger.kernel.org, bpf@vger.kernel.org, Jim Cromie X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1790705250; l=7334; i=jim.cromie@gmail.com; s=20260203; h=from:subject:message-id; bh=x418C0Z26mQamkJXbHqPXO271fxdJW9S7nrWDjCs7VE=; b=2WgV4XHsleAnDMiLsyPdykI+Qruslpzu80vLrmHPGG9Hw/4hdjCItDegefygMibHIddsT57f+ ncGlUwzBGtXA4LjwBnOz8cmAj66u1jy9B393AMwogdB0jU25cwQ1tyC X-Developer-Key: i=jim.cromie@gmail.com; a=ed25519; pk=C6E5ODlPQo7ZBynATXH9wg7K6HxP0pIXyf4s38Qw0XE= X-Endpoint-Received: by B4 Relay for jim.cromie@gmail.com/20260203 with auth_id=958 X-Original-From: Jim Cromie Reply-To: jim.cromie@gmail.com From: Jim Cromie kallsyms_lookup_names() runs a binary search across ~184k tokenized (compressed) 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 tokenized symbols 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. Even sorting the token table alphabetically wouldn't help; "bpf_" and "bpf_foo_" do not *have* a determinative sorting order, because the suffixes following those tokens would matter. However, full string expansion at every step is equally wasteful: of the ~17 strcmps in the binary search, only the last needs to check all N chars in both strings, earlier steps will know +/- outcome at char 0,1,2..N-1. However, full string expansion at every step is equally wasteful: of the ~17 strcmp()s in the binary search, only the final matching step needs to test all characters. Earlier non-matching steps diverge at the first differing character (0..N-1), but the baseline expands every candidate symbol to the stack unconditionally, before comparing. So we introduce kallsyms_strcmp_symbol() to compare ASCII search_name against tokenized symbols on the fly. Like strcmp, it tests the strings char by char, but when it hits a token in the symbol-string, it continues the char-test against that token-string, which is in kallsyms_token_table[]. It returns +- on 1st mismatch. Measured across all ~184k symbols via CONFIG_KALLSYMS_SELFTEST, this shaves ~530 ns (~14%) off average kallsyms_lookup_name() latency (from ~3810 ns to ~3280 ns on the default 256:1 baseline) and drops the 512-byte namebuf buffer stack-alloc in kallsyms_lookup_names(). Signed-off-by: Jim Cromie --- Changes in v6: - Clarify character match mechanics in commit body (no stack expansion, short-circuit at first divergent char 0..N-1). - Cite CONFIG_KALLSYMS_SELFTEST across all ~184k symbols for performance measurements. - Drop stale "unindexed" phrasing and update symbol count to ~184k. 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 later 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