[HarfBuzz] On optimizing HarfBuzz

2012-06-09 Thread Behdad Esfahbod
Hi,

I happened to look into optimization opportunities in HarfBuzz and these are
my findings:

For very short strings, preparing the shape plan seems to take a significant
portion of the shape() call, so transparently caching the plan as Jonathan and
I agreed we should do, would address that.  On the plus side, mutexes (as
would be required for that) seem to have negligible overhead.

For longer text, it's a different story.  Seems like, contrary to my
expectation, all the passes we do in hb-ot-shape.cc (getting Unicode
properties, mirroring, cmap mapping, normalization, default positioning, etc)
have really insignificant overhead.  This is great.

What *is* a big problem is the actual OpenType lookups themselves.  And how
bad that is depends directly on the font.  There are fonts with zero OpenType
lookups (eg. Droid Sans), and there are fonts that have over a 100 sublookups
for a simple shape plan (eg. Arabic Typesetting).  Seems like, currently we
are being very inefficient for those larger fonts.  But even for simpler fonts
with around 20 sublookups (eg. DejaVu Sans) we are doing an awful job.  I
tried to understand this, and here are my findings.  I think I have a good
grasp on what's causing the slowness.  Less so on how to address it, but I
have ideas.  Note that Uniscribe doesn't seem to be nearly as affected by the
number of lookups, so there ought to be ways to address this.

To understand, lets see what the main apply() loops for GSUB and GPOS
currently look like:

1  for each lookup {
2for each glyph in buffer {
3  for each sublookup of the lookup {
4switch on the lookup_type {
5  switch on the sublookup_type {
6if glyph in coverage of the sublookup {
7  do lots of work...
 }
   }
 }
   }
 }
   }

Now, *that*'s expensive!  Note that the passthrough rate at 6 is very very
low.  Ie. most glyphs don't pass the coverage check for most sublookups.  But
we do a hell of a lot of work to get to that conclusion.  In short, I think we
don't have to worry about speeding up the actual lookup processing.  We need
to make to the "first glyph doesn't match" conclusion much much faster.

The actual coverage matching if constrained by the OpenType data-structures
(unless we want to do caching, etc, more on that below).  However, for
example, we can get rid of the switching on lines 4 and 5 for most cases.  The
way OpenType was originally designed, all sublookup types of all lookup types
had the coverage table offset in the same place.  So we can move line 6 to
before line 4.  Now, newer additions to the spec (Context subtype 3,
ChainContext subtype 3, and Extension subtype 1) don't have the coverage in
the same place.  However, by rewriting the loop at line 2 to handle the 3
cases separately we can save lots of churning.  I did an initial take of this
by adding "fast-path" to GSUB/GPOS, but didn't reorder the actual loops.  Will
be doing that soon.  Also, by simply looking up the coverage table before
looping over each glyph, we save a lot.

For fonts that were slow, I found that they have lots of ChainContext subtype
3.  I understand why this is.  Unlike the rest of the lookups, each Context
subtype 3 and ChainContext subtype 3 can only match a single sequence.  So, if
you have 100 different contextual ligatures, you need 100 subtables...  Now,
inspecting Arabic Typesetting, I also found that all the subtables in each
lookup reference the same Coverage table for the first character.  (BTW, I
have a vague memory that OTS does not like this.)  Currently, we ignore that
and check each glyph against each subtable.  We can easily check whether the
Coverage table is the same and skip rechecking.

This much is the easy part.  To significantly get faster, we need a way to
avoid the cartesian product of "all glyphs in the buffer" and "all subtables"
somehow.  That's were caching comes in, and as one wise programmer once said,
"All programming is an exercise in caching."  I have ideas around there too,
but I will harvest the low-hanging fruit before going there.

Hopefully the easy stuff gives us enough boost to let me go back and
concentrate on correctness before we need to optimize again.

Comments?

Cheers,
behdad
___
HarfBuzz mailing list
HarfBuzz@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/harfbuzz


[HarfBuzz] [PATCH] Enhancements for gobject introspection

2012-06-09 Thread أحمد المحمودي
From: "أحمد المحم�?= =?UTF-8?q?�دي (Ahmed El-Mahmoudy)" 

* Check for HB_GOBJECT_COMPILATION before failing on private headers
* Use introspection autoconf/make stuff.
---
 configure.ac|2 ++
 src/Makefile.am |   24 ++--
 src/hb-blob.h   |2 +-
 src/hb-buffer.h |2 +-
 src/hb-common.h |2 +-
 src/hb-font.h   |2 +-
 src/hb-ot-layout.h  |2 +-
 src/hb-ot-tag.h |2 +-
 src/hb-set.h|2 +-
 src/hb-shape.h  |2 +-
 src/hb-unicode.h|2 +-
 src/hb-version.h.in |2 +-
 12 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/configure.ac b/configure.ac
index 2fb058f..784c8fe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -194,6 +194,8 @@ fi
 
 dnl ===
 
+GOBJECT_INTROSPECTION_CHECK([0.6.7])
+
 AC_CONFIG_FILES([
 Makefile
 harfbuzz.pc
diff --git a/src/Makefile.am b/src/Makefile.am
index 344cc57..3abd786 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,6 +6,7 @@ EXTRA_DIST =
 CLEANFILES =
 DISTCLEANFILES =
 MAINTAINERCLEANFILES =
+DISTCHECK_CONFIGURE_FLAGS = --enable-introspection
 
 # The following warning options are useful for debugging: -Wpadded -Wcast-align
 #AM_CXXFLAGS =
@@ -207,8 +208,27 @@ TESTS_ENVIRONMENT = \
HBHEADERS="$(HBHEADERS)" \
$(NULL)
 
-scan:
-   g-ir-scanner $(HBCFLAGS) $(HBHEADERS) -n hb --strip-prefix=hb --library 
libharfbuzz.la
+-include $(INTROSPECTION_MAKEFILE)
+INTROSPECTION_GIRS = hb-1.0.gir
+INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir)
+INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
 
+if HAVE_INTROSPECTION
+introspection_sources = $(HBHEADERS)
+
+hb-1.0.gir: libharfbuzz.la
+hb_1_0_gir_INCLUDES = GObject-2.0
+hb_1_0_gir_CFLAGS = $(INCLUDES) $(HBCFLAGS) -DHB_GOBJECT_COMPILATION
+hb_1_0_gir_LIBS = libharfbuzz.la
+hb_1_0_gir_FILES = $(introspection_sources)
+
+girdir = $(datadir)/gir-1.0
+gir_DATA = $(INTROSPECTION_GIRS)
+
+typelibdir = $(libdir)/girepository-1.0
+typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
+
+CLEANFILES += $(gir_DATA) $(typelib_DATA)
+endif
 
 -include $(top_srcdir)/git.mk
diff --git a/src/hb-blob.h b/src/hb-blob.h
index 360310b..c64e09b 100644
--- a/src/hb-blob.h
+++ b/src/hb-blob.h
@@ -24,7 +24,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_GOBJECT_COMPILATION)
 #error "Include  instead."
 #endif
 
diff --git a/src/hb-buffer.h b/src/hb-buffer.h
index fe53197..6a5b110 100644
--- a/src/hb-buffer.h
+++ b/src/hb-buffer.h
@@ -27,7 +27,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_GOBJECT_COMPILATION)
 #error "Include  instead."
 #endif
 
diff --git a/src/hb-common.h b/src/hb-common.h
index 562b04c..7140396 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -26,7 +26,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_GOBJECT_COMPILATION)
 #error "Include  instead."
 #endif
 
diff --git a/src/hb-font.h b/src/hb-font.h
index b98759b..27239c4 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -24,7 +24,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_GOBJECT_COMPILATION)
 #error "Include  instead."
 #endif
 
diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h
index b8b5baf..42bd7ef 100644
--- a/src/hb-ot-layout.h
+++ b/src/hb-ot-layout.h
@@ -24,7 +24,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_H_IN
+#if !defined(HB_OT_H_IN) && !defined(HB_GOBJECT_COMPILATION)
 #error "Include  instead."
 #endif
 
diff --git a/src/hb-ot-tag.h b/src/hb-ot-tag.h
index 1bf12ab..94f7c59 100644
--- a/src/hb-ot-tag.h
+++ b/src/hb-ot-tag.h
@@ -24,7 +24,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_H_IN
+#if !defined(HB_OT_H_IN) && !defined(HB_GOBJECT_COMPILATION)
 #error "Include  instead."
 #endif
 
diff --git a/src/hb-set.h b/src/hb-set.h
index 9f849cf..69367bf 100644
--- a/src/hb-set.h
+++ b/src/hb-set.h
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_GOBJECT_COMPILATION)
 #error "Include  instead."
 #endif
 
diff --git a/src/hb-shape.h b/src/hb-shape.h
index 1a0d6cf..89b4922 100644
--- a/src/hb-shape.h
+++ b/src/hb-shape.h
@@ -24,7 +24,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_GOBJECT_COMPILATION)
 #error "Include  instead."
 #endif
 
diff --git a/src/hb-unicode.h b/src/hb-unicode.h
index 205e4c7..e8893ac 100644
--- a/src/hb-unicode.h
+++ b/src/hb-unicode.h
@@ -28,7 +28,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_GOBJECT_COMPILATION)
 #error "Include  instead."
 #endif
 
diff --git a/src/hb-version.h.in b/src/hb-version.h.in
index 43634f9..38185b2 100644
--- a/src/hb-version.h.in
+++ b/src/hb-version.h.in

[HarfBuzz] harfbuzz-ng: Branch 'master'

2012-06-09 Thread Behdad Esfahbod
 util/shape-consumer.hh |2 --
 1 file changed, 2 deletions(-)

New commits:
commit 6a5661f1e69c937083e8d976cb12429b99180d54
Author: Behdad Esfahbod 
Date:   Sat Jun 9 03:26:16 2012 -0400

Ugh

diff --git a/util/shape-consumer.hh b/util/shape-consumer.hh
index 11e4cc3..220daa4 100644
--- a/util/shape-consumer.hh
+++ b/util/shape-consumer.hh
@@ -49,7 +49,6 @@ struct shape_consumer_t
   {
 output.new_line ();
 
-for (unsigned int i = 0; i < 1; i++) {
 shaper.populate_buffer (buffer, text, text_len);
 output.consume_text (buffer, text, text_len, shaper.utf8_clusters);
 
@@ -59,7 +58,6 @@ struct shape_consumer_t
   output.shape_failed (buffer, text, text_len, shaper.utf8_clusters);
   return;
 }
-}
 
 output.consume_glyphs (buffer, text, text_len, shaper.utf8_clusters);
   }
___
HarfBuzz mailing list
HarfBuzz@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/harfbuzz


[HarfBuzz] harfbuzz-ng: Branch 'master' - 17 commits

2012-06-09 Thread Behdad Esfahbod
 src/hb-cache-private.hh  |2 +
 src/hb-open-type-private.hh  |   21 +-
 src/hb-ot-layout-common-private.hh   |   26 +-
 src/hb-ot-layout-gpos-table.hh   |   28 +--
 src/hb-ot-layout-gsub-table.hh   |   28 ++-
 src/hb-ot-layout-gsubgpos-private.hh |   15 +-
 src/hb-ot-layout-private.hh  |4 --
 src/hb-ot-layout.cc  |   50 +--
 src/hb-set-private.hh|5 +++
 util/shape-consumer.hh   |2 +
 10 files changed, 137 insertions(+), 44 deletions(-)

New commits:
commit f211d5c291b4c947cfd732e873627567173057e4
Author: Behdad Esfahbod 
Date:   Sat Jun 9 03:11:22 2012 -0400

More Oops!  Fix fast-path with sub-type==0

diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 48f0a9d..c8020d8 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -1311,7 +1311,7 @@ struct PosLookupSubTable
   inline bool can_use_fast_path (unsigned int lookup_type) const
   {
 /* Fast path, for those that have coverage in the same place. */
-return likely (lookup_type < Context) ||
+return likely (lookup_type && lookup_type < Context) ||
   (hb_in_range (lookup_type, Context, ChainContext) &&
hb_in_range (u.header.sub_format, 1, 2));
   }
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index e62be98..f5f38cc 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -970,7 +970,7 @@ struct SubstLookupSubTable
 /* Fast path, for those that have coverage in the same place.
  * Note that ReverseChainSingle can also go through this but
  * it's not worth the effort. */
-return likely (lookup_type < Context) ||
+return likely (lookup_type && lookup_type < Context) ||
   (hb_in_range (lookup_type, Context, ChainContext) &&
hb_in_range (u.header.sub_format, 1, 2));
   }
diff --git a/util/shape-consumer.hh b/util/shape-consumer.hh
index 220daa4..11e4cc3 100644
--- a/util/shape-consumer.hh
+++ b/util/shape-consumer.hh
@@ -49,6 +49,7 @@ struct shape_consumer_t
   {
 output.new_line ();
 
+for (unsigned int i = 0; i < 1; i++) {
 shaper.populate_buffer (buffer, text, text_len);
 output.consume_text (buffer, text, text_len, shaper.utf8_clusters);
 
@@ -58,6 +59,7 @@ struct shape_consumer_t
   output.shape_failed (buffer, text, text_len, shaper.utf8_clusters);
   return;
 }
+}
 
 output.consume_glyphs (buffer, text, text_len, shaper.utf8_clusters);
   }
commit b1de6aa1f33b228afe231c8209aef90a5fa1ee5d
Author: Behdad Esfahbod 
Date:   Sat Jun 9 03:07:59 2012 -0400

Oops!

diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index ad403d5..48f0a9d 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -1343,7 +1343,7 @@ struct PosLookupSubTable
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
 TRACE_SANITIZE ();
 if (!u.header.sub_format.sanitize (c) ||
-   (can_use_fast_path (lookup_type)) && !u.header.coverage.sanitize (c, 
this))
+   (can_use_fast_path (lookup_type) && !u.header.coverage.sanitize (c, 
this)))
   return TRACE_RETURN (false);
 switch (lookup_type) {
 case Single:   return TRACE_RETURN (u.single.sanitize (c));
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 75e38b9..e62be98 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -1000,7 +1000,7 @@ struct SubstLookupSubTable
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
 TRACE_SANITIZE ();
 if (!u.header.sub_format.sanitize (c) ||
-   (can_use_fast_path (lookup_type)) && !u.header.coverage.sanitize (c, 
this))
+   (can_use_fast_path (lookup_type) && !u.header.coverage.sanitize (c, 
this)))
   return TRACE_RETURN (false);
 switch (lookup_type) {
 case Single:   return TRACE_RETURN (u.single.sanitize (c));
commit b12e2549cbcd4f1ef46e66c75533686ee560f59b
Author: Behdad Esfahbod 
Date:   Sat Jun 9 03:05:20 2012 -0400

Minor

diff --git a/src/hb-ot-layout-gsubgpos-private.hh 
b/src/hb-ot-layout-gsubgpos-private.hh
index 4ac724c..98d4e0a 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -144,7 +144,7 @@ struct hb_apply_context_t
 {
   return unlikely (num_items && idx + num_items >= end);
 }
-inline bool reject (void)
+inline void reject (void)
 {
   num_items++;
 }
@@ -193,7 +193,7 @@ struct hb_apply_context_t
 {
   return unlikely (idx < num_items);
 }
-inline bool reject (void)
+inline void reject (void)
 {
   num_items++;
 }
commit faf0f20253d954cc4cfa4c967ece7573a5ddae3b
Author: Behdad Esfahbod 
Date:   Sat Ju