[cairo] Adventures in caching

Owen Taylor otaylor at redhat.com
Wed Apr 6 18:07:32 PDT 2005

Today I was working on implementing the central cairo_scaled_font_t
 font matrix        => cairo_scaled_font_t

And wanted the following behavior:

 - All otherwise referenced cairo_scaled_font_t objects are in the
   cache. If Pango is keeping a cairo_scaled_font_t object around,
   and sets the cairo_font_face_t for it via cairo_set_font_face(),
   the *same* cairo_scaled_font_t should be used.

 - Some number of not otherwise referenced cairo_scaled_font_t
   objects should also be kept in the cache.

   If someone is drawing with a couple of fonts via the toy API,
   then we shouldn't continually be creating new cairo_scaled_font_t
   objects each time they switch fonts. (This is an expensive 
   operation since it involves fontconfig lookups)

Now, this behavior isn't easily implementable with cairo_cache_t.
What cairo_cache_t allows is

 A) A size-limited cache. Once the cache goes over the limit, 
    a random element is removed and replaced.

    When the cache is used this way (e.g., the glyph cache), it usually
    keeps strong references to the values.

 B) A infinite-sized cache (hash table). The cache never automatically
    removes elements.

    When the cache is used this way (e.g. the cache of unscaled fonts
    for the FreeType backend) the cache typically does not hold a 
    reference to the fonts, and fonts are removed from the cache when
    their refcount drops to zero.

It would be possible to extend cairo_cache_t to support the desired 
behavior ... the basic scheme would be:

 - Each element in the cache table has a 'referenced' flag
 - We add 'reference' and 'dereference' functions to 
 - Only referenced elements contribute to the "total size" of the 
   cache table.
 - Whenever a unreferenced element is retrieved, it's referenced.
 - If referencing an element makes the size go over the limit,
   we unreference a random other element.

But I didn't really want to make cairo_cache_t more complex, so what
I did instead was to use a two level cache. Both caches have the
same key/value setup

 font matrix        => cairo_scaled_font_t

The outer cache is size limited (to MAX_CACHED_FONTS, currently 24),
and references the values, the inner cache is infinite size and
doesn't reference the values. If the lookup in the outer cache
fails, then we look up in the inner cache and store the result
in the outer cache. If the lookup in the inner cache fails, we
create the scaled font.

This gives the desired behavior ... we keep around MAX_CACHED_FONTS 
even when not otherwise used, but we will always find any currently
loaded cairo_scaled_font_t even if it isn't in that set.

The main extra overhead is the double cache lookup in the case
where the lookup in the outer cache fails ... if an application
is using more than MAX_CACHED_FONTS at once, then this could
be on every font lookup, but that seems unlikely to happen

If it turns out that lookups in the scaled font cache are a 
performance bottleneck, we may want to look at redoing this by
extending cairo_cache_t.


-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: This is a digitally signed message part
Url : http://lists.freedesktop.org/archives/cairo/attachments/20050406/776d95c2/attachment.pgp

More information about the cairo mailing list