First off, apologies for a bug report with precious few details. I am
hoping this rings a bell for some cairo developer.
The Gnumeric team has received a few reports of severe cpu usage when we
do our "walking ants" animation that marks a copied selection. I do not
see this on my Linux machine; I might see it with our win32 binary under
wine, but that's a whole lot of extra variables.
To see the walking ants animation in action, start Gnumeric, select a
large area, and press ctrl-c. Alternatively, watch
Here's what the animation code does:
1. We set a two-item dashed line style using cairo_set_dash.
2. We set a line width using cairo_set_line_width
3. We set one of the two ant colours.
4. We set a rectangle path using cairo_rectangle
5. We draw a line along that path using cairo_stroke_preserve.
[That draws half the ant pattern, say the black bits.]
6. We set the other ant colour.
7. We set the stipple with an offset of one "ant" length
8. We draw a line along that path using cairo_stroke.
[That draws half the ant pattern, say the wite bits.]
Repeat that every 150ms with the two colours swapped.
The actual code is at
near line 450.
Is there any reason why this should bring a modern machine to its knees?
Further random info:
Both my Linux and win32 cairo are 1.12.16.
One original bug report for this: https://bugzilla.gnome.org/show_bug.cgi?id=381011 starting at comment 12.
I don't really understand the code, but here is what I think I found.
The 150ms timeout is implemented in item_cursor_realize() in item-cursor.c. For items with ->style == GNM_ITEM_CURSOR_ANTED it does this:
ic->animation_timer = g_timeout_add (150, (GSourceFunc) cb_item_cursor_animation, ic);
Looking at the callback function, we see that it just toggles ic->state and calls goc_item_invalidate() on the item. I haven't actually found that function's implementation, but debian code search gave me the following (goc_item_invalidate(item) just calls goc_item_maybe_invalidate(item, FALSE);):
This calculates the bounds of the items and invalidates that rectangle on the canvas.
TL;DR: gnumeric redraws "basically all of its content" about 7 times per second and then wonders that this is slow. (Or did I miss something?)
Re "this used to work fine for over a decade": The implementation a decade ago was more intelligent (I guess instead of redrawing things, it just drew the ant line ones and then used an XOR operator to "toggle the dashes/ants").
And valgrind --tool=callgrind says that 65% of CPU time is spent inside gnm_style_borders_row_draw() (unrelated code?) which itself spends most of its cpu time in cairo_stroke(). So the high cpu usage seems to come from some unrelated code which just happens to be executed more often due to the "ant lines".
I don't see why this would be a cairo problem. Could you provide more information, perhaps some self-contained C code that shows cairo being slow?
Thanks for the analysis and the effort. I am now able to reproduce the
problem myself and work on it.
Apologies for having suspected cairo: this was hitting a random set of machines
suggesting something below gtk+. In fact, I now believe this is something as
simple as different screen resolution. More pixels, more work. And pixels
go up with resolution squared.