[cairo] [PATCH] scaled-font: optimize cairo_scaled_font_text_to_glyphs()

Dmitri Vorobiev dmitri.vorobiev at movial.com
Wed Jun 9 09:20:27 PDT 2010


This patch serves two purposes. First, it factors out the heavy part
of the cairo_scaled_font_text_to_glyphs() routine thus allowing GCC
to better optimize the cache cleanup loop. Keeping the look-up table
indices in a separate array speeds up array initialization even further.

Second, this patch introduces a shortcut for the case when the string
to be rendered consists of a single character. In this case, caching is
not necessary at all.

We have a benchmark that uses Cairo to render a large amount of random
strings of consisting of printable ASCII characters. Below are Oprofile
results collected while running this benchmark. It is easy to see that
the heavy part becomes noticeably lighter.

Before:

Profiling through timer interrupt
samples  %        app name                 symbol name
198755   13.5580  libcairo.so.2.10907.0    cairo_scaled_font_text_to_glyphs
88580     6.0424  libcairo.so.2.10907.0    _cairo_scaled_glyph_lookup
81127     5.5340  libcairo.so.2.10907.0    _cairo_hash_table_lookup
68186     4.6513  libcairo.so.2.10907.0    cairo_scaled_font_glyph_extents
47145     3.2160  libcairo.so.2.10907.0    _composite_glyphs_via_mask
46327     3.1602  libcairo.so.2.10907.0    _cairo_scaled_font_glyph_device_extents
44817     3.0572  libcairo.so.2.10907.0    _composite_glyphs
40431     2.7580  libcairo.so.2.10907.0    .plt

After (note that cairo_scaled_font_text_to_glyphs_internal_single() was inlined):

Profiling through timer interrupt
samples  %        app name                 symbol name
107264    7.6406  libcairo.so.2.10907.0    cairo_scaled_font_text_to_glyphs_internal_multiple
87888     6.2604  libcairo.so.2.10907.0    _cairo_scaled_glyph_lookup
79011     5.6281  libcairo.so.2.10907.0    _cairo_hash_table_lookup
71723     5.1090  libcairo.so.2.10907.0    cairo_scaled_font_glyph_extents
48084     3.4251  libcairo.so.2.10907.0    _composite_glyphs_via_mask
46636     3.3220  libcairo.so.2.10907.0    _cairo_scaled_font_glyph_device_extents
44740     3.1869  libcairo.so.2.10907.0    _composite_glyphs
42472     3.0254  libc-2.8.so              _int_malloc
39194     2.7919  libcairo.so.2.10907.0    _cairo_gstate_transform_glyphs_to_backend
38614     2.7506  libcairo.so.2.10907.0    .plt
37063     2.6401  libcairo.so.2.10907.0    _cairo_ft_ucs4_to_index
36856     2.6253  libc-2.8.so              random
36376     2.5911  libcairo.so.2.10907.0    _cairo_scaled_glyphs_equal
34545     2.4607  libcairo.so.2.10907.0    cairo_matrix_transform_point
31690     2.2573  libc-2.8.so              malloc
29395     2.0939  libcairo.so.2.10907.0    _cairo_matrix_is_identity
26142     1.8621  libcairo.so.2.10907.0    _cairo_utf8_to_ucs4
24406     1.7385  libc-2.8.so              free
24059     1.7138  libcairo.so.2.10907.0    cairo_scaled_font_text_to_glyphs
---
 src/cairo-scaled-font.c |  171 ++++++++++++++++++++++++++++++++---------------
 1 files changed, 117 insertions(+), 54 deletions(-)

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index f928c08..ab3e2d1 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -1570,6 +1570,105 @@ ZERO_EXTENTS:
 }
 slim_hidden_def (cairo_scaled_font_glyph_extents);
 
+#define GLYPH_LUT_SIZE 256
+static void
+cairo_scaled_font_text_to_glyphs_internal_multiple (cairo_scaled_font_t   *scaled_font,
+				  double		 x,
+				  double		 y,
+				  const char	        *utf8,
+				  cairo_glyph_t	       **glyphs,
+				  cairo_text_cluster_t **clusters,
+				  int num_chars,
+				  cairo_status_t	*status
+				  )
+{
+
+    int i;
+    const char *p;
+    struct glyph_lut_elt {
+	unsigned long index;
+	double x_advance;
+	double y_advance;
+    } glyph_lut[GLYPH_LUT_SIZE];
+
+    uint32_t glyph_lut_unicode[GLYPH_LUT_SIZE];
+
+    for (i = 0; i < ARRAY_LENGTH (glyph_lut_unicode); i++)
+	glyph_lut_unicode[i] = ~0U;
+
+    p = utf8;
+    for (i = 0; i < num_chars; i++) {
+	int idx, num_bytes;
+	uint32_t unicode;
+	cairo_scaled_glyph_t *scaled_glyph;
+	struct glyph_lut_elt *glyph_slot;
+
+	num_bytes = _cairo_utf8_get_char_validated (p, &unicode);
+	p += num_bytes;
+
+	(*glyphs)[i].x = x;
+	(*glyphs)[i].y = y;
+
+	idx = unicode % ARRAY_LENGTH (glyph_lut);
+	glyph_slot = &glyph_lut[idx];
+	if (glyph_lut_unicode[idx] == unicode) {
+	    (*glyphs)[i].index = glyph_slot->index;
+	    x += glyph_slot->x_advance;
+	    y += glyph_slot->y_advance;
+	} else {
+	    (*glyphs)[i].index =
+		(*scaled_font->backend->ucs4_to_index) (scaled_font, unicode);
+
+	    *status = _cairo_scaled_glyph_lookup (scaled_font,
+						  (*glyphs)[i].index,
+						  CAIRO_SCALED_GLYPH_INFO_METRICS,
+						  &scaled_glyph);
+	    if (unlikely (*status))
+		return;
+
+	    x += scaled_glyph->metrics.x_advance;
+	    y += scaled_glyph->metrics.y_advance;
+
+	    glyph_lut_unicode[idx] = unicode;
+	    glyph_slot->index = (*glyphs)[i].index;
+	    glyph_slot->x_advance = scaled_glyph->metrics.x_advance;
+	    glyph_slot->y_advance = scaled_glyph->metrics.y_advance;
+	}
+
+	if (clusters) {
+	    (*clusters)[i].num_bytes  = num_bytes;
+	    (*clusters)[i].num_glyphs = 1;
+	}
+    }
+}
+
+static void
+cairo_scaled_font_text_to_glyphs_internal_single (cairo_scaled_font_t   *scaled_font,
+						    double		 x,
+						    double		 y,
+						    const char	        *utf8,
+						    cairo_glyph_t	       **glyphs,
+						    cairo_text_cluster_t **clusters,
+						    int num_chars,
+						    cairo_status_t	*status
+						    )
+{
+    uint32_t unicode;
+    int num_bytes;
+
+    (*glyphs)[0].x = x;
+    (*glyphs)[0].y = y;
+
+    num_bytes = _cairo_utf8_get_char_validated (utf8, &unicode);
+    (*glyphs)[0].index =
+	    (*scaled_font->backend->ucs4_to_index) (scaled_font, unicode);
+
+    if (clusters) {
+	(*clusters)[0].num_bytes  = num_bytes;
+	(*clusters)[0].num_glyphs = 1;
+    }
+}
+
 /**
  * cairo_scaled_font_text_to_glyphs:
  * @x: X position to place first glyph
@@ -1717,18 +1816,10 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t   *scaled_font,
 				  int		        *num_clusters,
 				  cairo_text_cluster_flags_t *cluster_flags)
 {
-    int i;
     int num_chars = 0;
-    const char *p;
     cairo_status_t status;
     cairo_glyph_t *orig_glyphs;
     cairo_text_cluster_t *orig_clusters;
-    struct glyph_lut_elt {
-	uint32_t unicode;
-	unsigned long index;
-	double x_advance;
-	double y_advance;
-    } glyph_lut[256];
 
     status = scaled_font->status;
     if (unlikely (status))
@@ -1866,52 +1957,24 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t   *scaled_font,
 	*num_clusters = num_chars;
     }
 
-    for (i = 0; i < ARRAY_LENGTH (glyph_lut); i++)
-	glyph_lut[i].unicode = ~0U;
-
-    p = utf8;
-    for (i = 0; i < num_chars; i++) {
-	int num_bytes;
-	uint32_t unicode;
-	cairo_scaled_glyph_t *scaled_glyph;
-	struct glyph_lut_elt *glyph_slot;
-
-	num_bytes = _cairo_utf8_get_char_validated (p, &unicode);
-	p += num_bytes;
-
-	(*glyphs)[i].x = x;
-	(*glyphs)[i].y = y;
-
-	glyph_slot = &glyph_lut[unicode % ARRAY_LENGTH (glyph_lut)];
-	if (glyph_slot->unicode == unicode) {
-	    (*glyphs)[i].index = glyph_slot->index;
-	    x += glyph_slot->x_advance;
-	    y += glyph_slot->y_advance;
-	} else {
-	    (*glyphs)[i].index =
-		(*scaled_font->backend->ucs4_to_index) (scaled_font, unicode);
-
-	    status = _cairo_scaled_glyph_lookup (scaled_font,
-						 (*glyphs)[i].index,
-						 CAIRO_SCALED_GLYPH_INFO_METRICS,
-						 &scaled_glyph);
-	    if (unlikely (status))
-		goto DONE;
-
-	    x += scaled_glyph->metrics.x_advance;
-	    y += scaled_glyph->metrics.y_advance;
-
-	    glyph_slot->unicode = unicode;
-	    glyph_slot->index = (*glyphs)[i].index;
-	    glyph_slot->x_advance = scaled_glyph->metrics.x_advance;
-	    glyph_slot->y_advance = scaled_glyph->metrics.y_advance;
-	}
-
-	if (clusters) {
-	    (*clusters)[i].num_bytes  = num_bytes;
-	    (*clusters)[i].num_glyphs = 1;
-	}
-    }
+    if (num_chars > 1)
+	cairo_scaled_font_text_to_glyphs_internal_multiple (scaled_font,
+					       x,
+					       y,
+					       utf8,
+					       glyphs,
+					       clusters,
+					       num_chars,
+					       &status);
+    else
+	cairo_scaled_font_text_to_glyphs_internal_single (scaled_font,
+					       x,
+					       y,
+					       utf8,
+					       glyphs,
+					       clusters,
+					       num_chars,
+					       &status);
 
  DONE: /* error that should be logged on scaled_font happened */
     _cairo_scaled_font_thaw_cache (scaled_font);
-- 
1.6.3.3



More information about the cairo mailing list