Skip to content

Instantly share code, notes, and snippets.

@adrientetar
Last active December 26, 2019 14:14
Show Gist options
  • Save adrientetar/936be611f7b805590c80ebe0e273c2dc to your computer and use it in GitHub Desktop.
Save adrientetar/936be611f7b805590c80ebe0e273c2dc to your computer and use it in GitHub Desktop.

Here are my findings after profiling the loading of Roboto-Regular.ufo.

Setting Lib objects into the Glyph-s is expensive (10%)

  • the Lib.update(...) call is expensive (4.9%), mainly because of postNotification and _set_dirty
  • creating Lib is expensive, mainly because of beginSelfNotificationObservation (3.4%), _get_dispatcher is a large contributor of the time spent (2.5%) as it does a chain of calls from getfont to getlayerset to getlayer
  • _set_dirty (0.5%)

Solution

  • let Lib parent BaseDictObject constructor take an initial mapping arg (defaults to None) that it will forward to the dict ctor, this way we can set an initial value without dealing with the notifications system
  • write a fast path for when _isLoading is set
  • glyph.instantiateLib could pass the font object to the Lib, that would avoid the chain of calls to find a dispatcher

Setting Anchor objects into the Glyph-s is expensive (11%)

  • Again, we need a way to set anchors without paying the cost of notifications

Solution

  • write a fast path for when _isLoading is set

Setting Component objects into the Glyph-s is expensive (5%)

  • Same

Solution

  • don't enable notifications but in appendComponent, this way the glyphObjectPointPen can do:
component = self._glyph.instantiateComponent()
component.baseGlyph = baseGlyphName
component.transformation = transformation
component.identifier = identifier
self._glyph.appendComponent(component)

without sending three useless notifications.

ATTN: beginSelfNotificationObservation must be the first subscriber

ATTN: this would break calling addObserver on a "lone" component (but who cares?)

A lot of time (4%) is spent making temporary GlyphLoadingPointPen data structures

It seems the point of this is mostly to avoid creating Contour (?) yet it is still putting contours and points in temporary data structures..

Solution

  • remove this machinery and optimize Contour to have low-cost constructors and data-setting capatibility that circumvent notifications

Instanciating Glyph objects is expensive (3%)

  • we need to do less in the constructor

Solution

  • don't set all the contourClass, pointClass etc. over-and-over for every glyph object. make those into class attributes

ATTN: those class args are cascading from Font -> LayerSet -> Layer -> Glyph with some subtleties e.g. libClass and guidelineClass should ideally match between Font and LayerSet.

NOTE: we can start by freezing those attrs only in Glyph

Reading plist is expensive (13% for the lib part)

Not sure how much optimization room there is here, but I'm pretty sure it's non-trivial.

Solution

  • rewrite plistlib to a more efficient version, with cython etc.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment