bug#81334: 32.0.50; fontifying buffers with many blocks is very slow
Yuan Fu <[email protected]>
| Newsgroups | gmane.emacs.bugs |
|---|---|
| Message-ID | <[email protected]> |
> On Jul 25, 2026, at 10:40 PM, Eli Zaretskii <[email protected]> wrote: > >> From: Yuan Fu <[email protected]> >> Date: Sat, 25 Jul 2026 15:47:33 -0700 >> Cc: [email protected], >> [email protected], >> [email protected], >> [email protected], >> [email protected], >> [email protected] >> >> >> >>> On Jul 18, 2026, at 1:55 AM, Eli Zaretskii <[email protected]> wrote: >>> >>> Ping! Any progress? >> >> The patch makes sense to me. The cache is allocated on the heap rather than a fixed length LRU, which is ok, since the code is simpler that way, and I don’t expect there to be too many languages loaded. Plus, if a language is loaded, it’s likely it’ll be used throughout the session. > > Which patch you think we should install? Rahul posted 2 patches. This one (see attached). I took liberty to edit some of the comments. > >> I do wonder if we want to put an arbitrary upper limit on how many language we can load, like 1000? (And signal an error if the limit is reached; basically, you can’t load more languages than 1000.) Normal usage shouldn’t reach that limit, and if something strange happens, we have an upper bound on how much memory we consume. > > Why isn't the memory limit enough to do this? That way, each user can > use the number of languages his/her system can support. Just a knee-jerk reaction to “unbounded cache, scary” :) Yuan
language-cache.patch
(application/octet-stream, 3.6 KB)
From 996c06379b0553ce42c3a9f2fbc03040da91bf62 Mon Sep 17 00:00:00 2001 From: Rahul Martim Juliato <[email protected]> Date: Sat, 4 Jul 2026 10:03:39 -0300 Subject: [PATCH] Cache resolved tree-sitter languages (bug#81334) * src/treesit.c (treesit_lang_cache_get) (treesit_lang_cache_put): New functions implementing a per-session cache of resolved languages. (treesit_load_language): Consult and populate the cache, to avoid repeating the dlopen/dlsym and ABI-version check on every parser creation. --- src/treesit.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/treesit.c b/src/treesit.c index 73094bfbbdf..09a9006b876 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -632,6 +632,56 @@ treesit_debug_print_parser_list (char *msg, Lisp_Object parser) const char *filename; }; +/* Cache of loaded languages, keyed by language symbols. Resolving a + language does dlopen + dlsym plus an ABI-version check that creates a + throwaway TSParser; repeating that for every parser created is + expensive when a mode creates many embedded parsers (e.g. one + markdown-inline parser per block). We don't support + unloading/reloading languages so we can cache loaded languages for + the entire session; in fact, we _want_ to cache the language object + so it stays stable for the whole session. */ +struct treesit_lang_cache_entry +{ + /* Symbol for the cached langauge, never gc'ed. */ + Lisp_Object symbol; + /* The cached language object. */ + struct treesit_loaded_lang lang; +}; + +/* The cache, a monotonically growing array on the heap. */ +static struct treesit_lang_cache_entry *treesit_lang_cache = NULL; +/* Number of available slots in the allocated space. */ +static ptrdiff_t treesit_lang_cache_size = 0; +/* Number of existing cache entry in the array. */ +static ptrdiff_t treesit_lang_cache_used = 0; + +/* Return the cached language object for LANGUAGE_SYMBOL, or {NULL, + NULL} if there is none. */ +static struct treesit_loaded_lang +treesit_lang_cache_get (Lisp_Object language_symbol) +{ + for (ptrdiff_t i = 0; i < treesit_lang_cache_used; i++) + if (EQ (treesit_lang_cache[i].symbol, language_symbol)) + return treesit_lang_cache[i].lang; + return (struct treesit_loaded_lang) { NULL, NULL }; +} + +/* Cache LOADED_LANG keyed by LANGUAGE_SYMBOL. */ +static void +treesit_lang_cache_put (Lisp_Object language_symbol, + struct treesit_loaded_lang loaded_lang) +{ + if (treesit_lang_cache_used == treesit_lang_cache_size) + { + treesit_lang_cache + = xpalloc (treesit_lang_cache, &treesit_lang_cache_size, 1, -1, + sizeof *treesit_lang_cache); + } + treesit_lang_cache[treesit_lang_cache_used].symbol = language_symbol; + treesit_lang_cache[treesit_lang_cache_used].lang = loaded_lang; + treesit_lang_cache_used++; +} + /* Translate a symbol treesit-<lang> to a C name treesit_<lang>. */ static void treesit_symbol_to_c_name (char *symbol_name) @@ -762,6 +812,11 @@ treesit_language_abi_version (const TSLanguage *ts_lang) treesit_load_language (Lisp_Object language_symbol, Lisp_Object *signal_symbol, Lisp_Object *signal_data) { + struct treesit_loaded_lang cached + = treesit_lang_cache_get (language_symbol); + if (cached.lang != NULL) + return cached; + Lisp_Object symbol_name = Fsymbol_name (language_symbol); CHECK_LIST (Vtreesit_extra_load_path); @@ -886,6 +941,7 @@ treesit_load_language (Lisp_Object language_symbol, dynlib_addr ((void (*)) langfn, &loaded_lang.filename, &sym); loaded_lang.lang = lang; + treesit_lang_cache_put (language_symbol, loaded_lang); return loaded_lang; } -- 2.39.5 (Apple Git-154)