For the past couple of weeks, Builder from git-master
has come with a new gnome-builder-clang
subprocess. Instead of including libclang
in the UI process, we now proxy all of that work to the subprocess. This should have very positive effect on memory usage within the UI process. It will also simplify the process of using valgrind/ASAN and obtaining useful results. In the future, we’ll teach the subprocess supervisor to recycle subprocesses if they consume too much memory.
That is rather important because compilers wreak havoc on the memory allocator. However, it presents a series of challenges too.
Once you move all of that stuff out of process you need to find an efficient way to communicate between processes. The design that I came up with is plain GVariant messages delivered over a stdin/stdout pipe to the subprocess. This is very similar to the Language Server Protocol but lets us cheat in an important way.
We sometimes need to pass large messages between processes including changes to unsaved buffers or completion results. Clang prefers to do client-side filtering of completion results so the list often includes some 25,000+ results. Not exactly the type of thing you want to parse into lots of tiny json objects and strings. By using GVariant
as our message format we ensure that we only have a single memory allocation for the entire result set (and can use C strings embedded in the variant too!).
By altering some of our APIs to use interfaces like GListModel
we end up more lazy in terms of object creation, use less memory, and fragment significantly less. One area this will be changing in the near future is our new completion engine. It is based on what we learned about other async APIs in Builder along with using GListModel for efficiency.
One thing that is not well known is that g_variant_get_child_value()
is O(1)
due to how arrays are laid out in GVariant
. This couples well with g_list_model_get_item()
.
This has been a big step forward, but there is more to do.