[HarfBuzz] an optimization for complex fonts

Jonathan Kew jfkthame at googlemail.com
Mon Oct 28 06:47:00 PDT 2013


Hey Behdad,

Turns out that for fonts such as Noto Devanagari and Gujarati, we're 
spending an inordinate amount of time under lookup.is_inplace 
(&inplace_c) in apply_string().

We can get a big win for these fonts if we cache the is_inplace result 
in the lookup-accelerator, instead of recursing down through the 
sub-lookups every time.

With the attached patch, I see the total run time for a test such as

   hb-shape NotoSansDevanagari-Regular.ttf --text-file hi.txt > /dev/null

drop from 22 seconds to 8 sec. And that includes the time to read the 
file, populate and serialize the buffer, etc., so once those constant 
factors are discounted, the actual shaping itself is over 3x faster. :)

NotoSansGujarati shows a similar gain; and Amiri with ar.txt runs in 45 
sec instead of 1 min+, for a speedup of over 25%.

(For fonts that don't have lots of complex contextual lookups, of 
course, there's no significant difference.)

See what you think. An alternative implementation might be to initialize 
the is_inplace flag during hb_ot_layout_lookup_accelerator_t::init() 
(then no need for the _initialized flag, hence taking an if() out of the 
hot path, and the hb_ot_layout_lookup_accelerator_t references could 
remain const), but it looked like this would involve rather more 
rearrangement of code, so I took the simplest approach for now.

JK
-------------- next part --------------
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index 139e33f..ae5bb99 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -86,7 +86,7 @@ namespace OT {
 HB_INTERNAL void
 hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c,
 				const OT::SubstLookup &lookup,
-				const hb_ot_layout_lookup_accelerator_t &accel);
+				hb_ot_layout_lookup_accelerator_t &accel);
 
 
 /* Should be called after all the substitute_lookup's are done */
@@ -124,6 +124,7 @@ struct hb_ot_layout_lookup_accelerator_t
   {
     digest.init ();
     lookup.add_coverage (&digest);
+    is_inplace_initialized = false;
   }
 
   template <typename TLookup>
@@ -132,6 +133,8 @@ struct hb_ot_layout_lookup_accelerator_t
   }
 
   hb_set_digest_t digest;
+  bool is_inplace;
+  bool is_inplace_initialized;
 };
 
 struct hb_ot_layout_t
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index bd8ef08..be17701 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -790,7 +790,7 @@ struct GSUBProxy
     accels (hb_ot_layout_from_face (face)->gsub_accels) {}
 
   const OT::GSUB &table;
-  const hb_ot_layout_lookup_accelerator_t *accels;
+  hb_ot_layout_lookup_accelerator_t *accels;
 };
 
 struct GPOSProxy
@@ -803,7 +803,7 @@ struct GPOSProxy
     accels (hb_ot_layout_from_face (face)->gpos_accels) {}
 
   const OT::GPOS &table;
-  const hb_ot_layout_lookup_accelerator_t *accels;
+  hb_ot_layout_lookup_accelerator_t *accels;
 };
 
 
@@ -820,11 +820,9 @@ template <typename Proxy>
 static inline bool
 apply_string (OT::hb_apply_context_t *c,
 	      const typename Proxy::Lookup &lookup,
-	      const hb_ot_layout_lookup_accelerator_t &accel)
+	      hb_ot_layout_lookup_accelerator_t &accel)
 {
   bool ret = false;
-  OT::hb_is_inplace_context_t inplace_c (c->face);
-  bool inplace = lookup.is_inplace (&inplace_c);
   hb_buffer_t *buffer = c->buffer;
 
   if (unlikely (!buffer->len || !c->lookup_mask))
@@ -850,6 +848,18 @@ apply_string (OT::hb_apply_context_t *c,
     }
     if (ret)
     {
+      bool inplace;
+      if (accel.is_inplace_initialized)
+      {
+	inplace = accel.is_inplace;
+      }
+      else
+      {
+	accel.is_inplace_initialized = true;
+	OT::hb_is_inplace_context_t inplace_c (c->face);
+	inplace = accel.is_inplace = lookup.is_inplace (&inplace_c);
+      }
+
       if (!inplace)
 	buffer->swap_buffers ();
       else
@@ -924,7 +934,7 @@ void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_
 HB_INTERNAL void
 hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c,
 				const OT::SubstLookup &lookup,
-				const hb_ot_layout_lookup_accelerator_t &accel)
+				hb_ot_layout_lookup_accelerator_t &accel)
 {
   apply_string<GSUBProxy> (c, lookup, accel);
 }


More information about the HarfBuzz mailing list