[cairo] Where's the bottleneck in using glitz/cairo/librsvg?

David Turner dturner at nds.com
Mon Jun 12 09:34:15 PDT 2006


Jamey Sharp a écrit :
> My computational geometry background is ... non-existent, but assuming
> that your stroke and fill extent computations are correct, then avoiding
> the tesselator for those seems like a fine plan. I wonder how much
> difference it will make when the tesselator is implemented using the new
> algorithm though?
>
>   
Assuming the computations are correct, probably the difference between 
O(n) and O(n.logn)
and 'n' is generally large when decomposing beziers, more so when 
stroking paths.
> Specific code comments follow:
>
>   
>> diff -urNb cairo-1.1.6-org/pixman/src/icimage.c cairo-1.1.6-new/pixman/src/icimage.c
>> --- cairo-1.1.6-org/pixman/src/icimage.c	2006-06-09 08:25:48.000000000 +0200
>> +++ cairo-1.1.6-new/pixman/src/icimage.c	2006-06-09 08:25:33.000000000 +0200
>> @@ -179,6 +238,100 @@
>>  
>>      gradient->stopRange = tableSize;
>>  
>> +#ifdef oldDAVID
>> +    /* The position where the gradient begins and ends */
>> +    begin_pos = (stops[0].x * gradient->colorTableSize) >> 16;
>> +    end_pos = (stops[nstops - 1].x * gradient->colorTableSize) >> 16;
>> ...
>>     
>
> Did you mean to include this? The symbol oldDAVID is not being defined.
>
>   
It's something that should have been removed from the patch. I made a 
stupid mistake of optimizing some
code after reading the profile incorrectly; as such, I optimized a 
function that was never, or nearly never,
called. There is no point in keeping it then, hence the "oldDAVID".
>   
>> diff -urNb cairo-1.1.6-org/src/cairo-polygon.c cairo-1.1.6-new/src/cairo-polygon.c
>> --- cairo-1.1.6-org/src/cairo-polygon.c	2006-06-09 08:25:47.000000000 +0200
>> +++ cairo-1.1.6-new/src/cairo-polygon.c	2006-06-09 08:25:32.000000000 +0200
>> @@ -123,9 +135,7 @@
>>      polygon->num_edges++;
>>  
>>    DONE:
>> -    _cairo_polygon_move_to (polygon, p2);
>> -
>> -    return CAIRO_STATUS_SUCCESS;
>> +    return _cairo_polygon_move_to (polygon, p2);
>>  }
>>  
>>  cairo_status_t 
>>     
>
> This looks like a bug-fix, perhaps? Some explanation of this change
> might be nice.
>
>   
it looked like a bug-fix when I wrote it, but reading the code for 
_cairo_polygon_move_to
after that shows that the function cannot fail anyway, so the original 
code is OK too. Sorry
for that.

on the other hand, the call itself can be replaced by a simple 
"polygon->current_point = *p2",
saving a function call. ok, that's nit-picking :-)
>> diff -urNb cairo-1.1.6-org/src/cairo-spline.c cairo-1.1.6-new/src/cairo-spline.c
>> --- cairo-1.1.6-org/src/cairo-spline.c	2006-06-09 08:25:47.000000000 +0200
>> +++ cairo-1.1.6-new/src/cairo-spline.c	2006-06-09 08:25:32.000000000 +0200
>> @@ -225,6 +227,107 @@
>>      return _PointDistanceSquaredToPoint (p, &px);
>>  }
>>  
>> +#ifdef DAVID
>> ...
>> +cairo_status_t
>> +_cairo_spline_decompose_direct (cairo_spline_t              *spline,
>> +                                double                       tolerance,
>> +                                cairo_spline_point_func_t   *topoint,
>> +                                void                        *closure,
>> +                                cairo_bool_t                 keep_first)
>> +{
>> +    cairo_point_t   stack[64];   /* should be largely enough */
>>     
>
> As far as I can tell, you end the iteration early if your explicit stack
> overflows. To me that seems inconsistent with what I perceive as Cairo's
> philosophy of always giving correct results.
A 64-point stack is capable of managing (64-1)/3=21 bezier 
sub-divisions, which means that if
we consider a spline as large as 65536 pixels (the maximum path 
dimensions supported by Cairo),
we could have sub-arcs as small as 1/32 of a pixel (which can trivially 
be approximated by a line
segment without any perceivable loss of quality, isn't it ?)

Also, Bezier sub-divisions tend to approximate line segments pretty 
quickly, which means the back-off
code will stop the recursion well before you reach the bottom of your stack.

>> diff -urNb cairo-1.1.6-org/src/cairo-traps.c cairo-1.1.6-new/src/cairo-traps.c
>> --- cairo-1.1.6-org/src/cairo-traps.c	2006-06-09 08:25:47.000000000 +0200
>> +++ cairo-1.1.6-new/src/cairo-traps.c	2006-06-09 08:25:32.000000000 +0200
>> @@ -60,8 +60,36 @@
>>  static int
>>  _compare_cairo_edge_by_slope (const void *av, const void *bv);
>>  
>> -static cairo_fixed_16_16_t
>> -_compute_x (cairo_line_t *line, cairo_fixed_t y);
>> +#ifdef DAVID
>> +#  if defined(__GNUC__) && defined(i386)
>> +static __inline__ cairo_fixed_16_16_t
>> +_compute_x (cairo_line_t *line, cairo_fixed_t y)
>> +{
>> +    cairo_fixed_16_16_t dx = line->p2.x - line->p1.x;
>> +    cairo_fixed_16_16_t ey = y - line->p1.y;
>> +    cairo_fixed_16_16_t dy = line->p2.y - line->p1.y;
>> +    cairo_fixed_16_16_t ex;
>> +
>> +    __asm__ __volatile__ (
>> +        "imul  %%edx\n"
>> +        "idiv  %%ecx\n"
>> +        : "=a" (ex)
>> +        : "a"(dx), "d"(ey), "c"(dy)
>> +    );
>> +    return line->p1.x + ex;
>> +}
>> +#  else /* other platforms */
>> +static __inline__ cairo_fixed_16_16_t
>> +_compute_x (cairo_line_t *line, cairo_fixed_t y)
>> +{
>> +    cairo_fixed_16_16_t dx = line->p2.x - line->p1.x;
>> +    cairo_fixed_32_32_t ex = (cairo_fixed_48_16_t) (y - line->p1.y) * (cairo_fixed_48_16_t) dx;
>> +    cairo_fixed_16_16_t dy = line->p2.y - line->p1.y;
>> +
>> +    return line->p1.x + (ex / dy);
>> +}
>> +#  endif
>> +#endif
>>  
>>  static int
>>  _line_segs_intersect_ceil (cairo_line_t *left, cairo_line_t *right, cairo_fixed_t *y_ret);
>>     
>
> As long as the definition of _compute_x precedes the call, GCC will
> normally inline it since it's static and called only once.
Nope, it's called multiple times by the code, and hasn't been inlined by 
my version of GCC.
Moreover, it always calls a hidden function called __divi3, used to 
divide two 64-bits numbers
(which is slower than a 64-bits-by-32 division of the inlined assembly).

By the way, I've discovered that my inlined assembly is buggy, since it 
may result in a division
error in case of overflow (it seems it's possible to trigger this 
condition with the cairo-dock
benchmark), I've written an alternative version but will provide it 
through git.

> As for the inline assembly, did you check what code GCC was actually
> generating first? I'd be surprised if it was losing so badly that your
> inline assembly made a real difference.
>
>   
Yes, I did check, and it was losing badly. Mainly because __divi3 is a 
slow function for what's
needed here. Also, the overhead of a function call is not to be 
underestimated when your function
is in the top CPU eaters shown by the profiler. Ditto for 
_cairo_fixed_to_double()

Regards,

- David Turner
- The FreeType Project  (www.freetype.org)


***********************************************************************************
Information contained in this email message is confidential and may be privileged, and is intended only for use of the individual or entity named above. If the reader of this message is not the intended recipient, or the employee or agent responsible to deliver it to the intended recipient, you are hereby notified that any dissemination, distribution or copying of this communication is strictly prohibited. If you have received this communication in error, please immediately notify the postmaster at nds.com and destroy the original message.
*********************************************************************************** 



More information about the cairo mailing list