C-Eldoc Speedups

I’ve been using c-eldoc-mode, the C plugin for eldoc, on and off for some time now. On one hand, the functionality of eldoc is amazing; it is very nice to have function signatures displayed in the echo area when you are in the argument list. When making function calls, you can easily ensure that you are putting arguments in the right order; when reading code, you can tell what the arguments correspond to. However, c-eldoc.el was quite slow on larger codebases such as the emacs source code; each movement command could halt processing for up to a second, which is completely unacceptable.

I decided to see what I could do to speed it up. On reading the source code, I found the main problem; for each lookup, the C preprocessor was called on the file, and grep was called on the results. While the ability to use grep, speeding up regex lookups, was the original point of doing this, in a a large project running the C preprocessor so much would completely kill performance. The easiest way to fix this was just to cache the C preprocessor output for some amount of time; in my case, I decided regenerating the buffer every 120 seconds was good enough. After implementing this, performance was greatly enhanced, and I haven’t had any performance problems so far; if you still are, please let me know, preferably with some details of how large the preprocessor output is.

Mostly, the improvements of c-eldoc involved caching the results of running the c preprocessor, instead of running it on each symbol change. For this, I wanted a cached hash table; one where the key-value pairs would expire after some amount of time. Since Emacs does not have one of these built in, and it is a general enough concept that I may need it outside of c-eldoc, I wrote the cache in it’s own library. c-eldoc still contains the cache code, as I didn’t want to annoy users of it, but the cache is also available standalone.

cache.el, which you can download here, currently provides only three functions; cache-make-cache, cache-gethash, and cache-puthash. Three arguments are provided to cache-make-cache, as well as the key arguments that are also given to make-hash-table; an initialization function, a test function, and a cleanup function. The initialization function is called with no arguments when a value is first put into the cache; the value it returns is stored along with the value put into the cache. The test function is called when a value is retrieved; It is passed the previously computed value of the initialization function, and returns t if the value has expired. If so, the key-value pair is removed from the hash table, and the cleanup function is called with the value of the key-value pair.

The cache structure is designed to emulate the hash tables of emacs as much as possible: there is no difference in interface or semantics of return value between cache-gethash and gethash, or cache-puthash and puthash. Additionally, all key arguments to make-hash-table can also be passed to cache-make-cache, and are used in the hash table backing the cache.

The cache data structure is just a list of a hash table and the three functions passed to cache-make-cache; this allows all the normal hash functions to be called on the car of the result returned by cache-make-cache. I believe that this will suffice without much irritation; however, if the lack of some utility does make the cache more difficult to use, please tell me and I will implement them.

2 Responses to “C-Eldoc Speedups”

  1. beketa says:

    Hi, Nathaniel. Thank you for performance improvement of c-eldoc mode. That makes c-eldoc mode quite useful. I think I found a bug in c-eldoc.el on EmacsWiki.

    (defun c-eldoc-time-difference (old-time)
    (> (c-eldoc-time-diff (current-time) old-time) c-eldoc-buffer-regenerate-time)
    “Returns whether or not old-time is less than c-eldoc-buffer-regenerate-time seconds ago.”)

    should be

    (defun c-eldoc-time-difference (old-time)
    “Returns whether or not old-time is less than c-eldoc-buffer-regenerate-time seconds ago.”
    (> (c-eldoc-time-diff (current-time) old-time) c-eldoc-buffer-regenerate-time))

  2. nflath says:

    Hi,
    Yep, this is a bug! I’ve fixed it in the emacs-wiki.

Leave a Reply