In modern Vim usage with plugins and colorschemes, syn_name2id() is one of the most called functions during startup. In my environment, it is called roughly 4,800 times before Vim finishes starting up.
However, the implementation of syn_name2id() uses a linear scan over a growarray, making each lookup O(n) in the number of highlight groups. This results in over 3.4 million STRCMP() calls just during startup.
This change replaces the linear search with a hashtab_T-based lookup, reducing the cost to O(1). The hashtable is maintained alongside the existing growarray using the same offsetof pattern as signgroup_T / HI2SG.
Benchmark (User time, vim -c quit):
The more highlight groups are defined, the greater the benefit of this change.
https://github.com/vim/vim/pull/19788
(1 file)
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
For reference, here is the benchmark result when opening a file (mbyte.c) with syntax highlighting:
Benchmark 1: ./vim_old mbyte.c -c quit
Time (mean ± σ): 2.357 s ± 0.020 s [User: 0.248 s, System: 0.067 s]
Benchmark 2: ./vim_new mbyte.c -c quit
Time (mean ± σ): 2.329 s ± 0.018 s [User: 0.220 s, System: 0.066 s]
User time improved by 11.3% (0.248s → 0.220s). The benefit grows when syntax highlighting is involved, since syn_name2id() is called more frequently.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Here are realistic benchmark results excluding startup time.
The scenario simulates colorscheme re-application + highlight link + hlID() lookups with 541 highlight groups (82 base + 200 treesitter-style + 256 plugin-style), performing 17,640 name lookups.
master (linear scan): 0.084 sec (~210,000 lookups/sec)
hashtable branch: 0.062 sec (~284,000 lookups/sec)
speedup: ~1.35x
Since the benchmark includes the overhead of execute 'highlight ...' command parsing, the actual improvement in syn_name2id() itself is likely higher. The O(n) → O(1) difference also grows with group count — environments with 800–1000+ groups (e.g., treesitter plugins) will see a larger speedup.
For reference, a microbenchmark with 2000 groups using only hlID() showed ~2.5x speedup.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@mattn pushed 2 commits.
—
View it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
@chrisbra commented on this pull request.
In src/highlight.c:
> @@ -207,6 +207,21 @@ static int hl_flags[HLF_COUNT] = HL_FLAGS;
static garray_T highlight_ga;
#define HL_TABLE() ((hl_group_T *)((highlight_ga.ga_data)))
+// Wrapper struct for hashtable entries: stores the highlight group ID alongside
+// the uppercase name used as a hash key. Uses the same offsetof pattern as
+// signgroup_T / HI2SG.
+typedef struct {
+ int hn_id; // highlight group ID (1-based)
+ char_u hn_key[1]; // uppercase name (stretchable array)
+} hlname_T;
+
+#define HLNAME_KEY_OFF offsetof(hlname_T, hn_key)
+#define HI2HLNAME(hi) ((hlname_T *)((hi)->hi_key - HLNAME_KEY_OFF))
+
+// hashtable for quick highlight group name lookup
+static hashtab_T highlight_ht;
+static int highlight_ht_inited = FALSE;
bool?
In src/highlight.c:
> }
ga_clear(&highlight_ga);
+ hash_clear(&highlight_ht);
this frees all memory, but highlight_ht_inited isn't reset to false
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@mattn pushed 1 commit.
—
View it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
thanks
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()