-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Gain a better sense of the sub-operations involved in font compilation and their timing #34
Comments
Here's some data, for building variable fonts: https://gist.github.com/simoncozens/3b841c88d6cb7de813b7530759e25e44 I'm finding it hard to visualise; here's the Noto fonts with the top eight longest build times: library(ggplot2);
library(jsonlite);
library(dplyr);
library(RColorBrewer);
library(stringr);
df <- jsonlite::fromJSON("noto-variable.json");
df <- df %>% unnest(timings) %>% mutate(message=timings[,1], time=as.numeric(timings[,2])) %>% select(-timings)
df2 <- df %>% group_by(name) %>% mutate(total_time=sum(time)) %>% filter(total_time > 40 & total_time < 200)
# Timings as absolute seconds
ggplot(df2, aes(y=time, x=name, label=if_else(time>1,stringr::str_wrap(paste(message, " (", signif(time, digits=3), "s)",sep=""), 30), NULL),color=format)) + geom_bar(stat="identity", fill="transparent",size=0.1, color="black") + geom_text(size = 2.5,position = position_stack(vjust = 0.5)) + facet_wrap(~format, scales="free") + theme(axis.text.x = element_text(angle = 45,vjust=0.5), legend.position="none")
# Timings as percentages
ggplot(df2, aes(y=time, x=name, label=if_else(time>1,stringr::str_wrap(paste(message, " (", signif(time/total_time*100, digits=3), "%)",sep=""), 30), NULL),color=format)) + geom_bar(stat="identity", fill="transparent",size=0.1, color="black") + geom_text(size = 2.5,position = position_stack(vjust = 0.5)) + facet_wrap(~format, scales="free") + theme(axis.text.x = element_text(angle = 45,vjust=0.5), legend.position="none")
|
Here's the equivalent data for static instance generation: https://gist.github.com/simoncozens/9050865f138ae080bee599c9176b61db A big chunk of instance generation is the UFO instantiation. I have a project to do this in Rust (triangulate) which would make that step incredibly quick; the rest of the font generation is embarrassingly parallel per instance, as they're completely independent font builds. This is something we could use Ninja to orchestrate (in gftools-builder-ninja we already do). I can easily see order of magnitude speedups for static instance generation. |
Why do feature writers take a long time? One of the things they do is compile a GSUB table. They use this to trace substitutions and allocate properties to different glyphs. (i.e. we deduce that Once they've done that, they throw away the binary GSUB table, and it gets built again later... |
that's right.. although that temporary GSUB is not serialized (so no overflow resolution triggers), only "built" in the sense that the features.fea is parsed and a GSUB table object is generated by the feaLib.builder. |
Looking at the variable font builds, can we drop the "save UFO sources" step? Currently the conversion from Glyphs to UFO gets a designspace/UFO object, writes out all the files and passes a path to |
MutatorMath had its own parser that wanted to load from disk again, but we can now finally ditch that since fontmake has its own instantiator that works with in-memory designspace/UFO masters. I think the other issue I'm working on right now -- broken include statements in features.fea when exporting .glyphs => .ufo to different directory than input file -- is somehow related to the current saving of UFO masters to disk; glyphsLib returns an in-memory designspace object populated with ufoLib2.Font objects that have no |
No, for once I thought I’d ask first if anyone’s tried it already… also I have run out of brain for today. Will try again tomorrow. |
Get some well deserved rest, you did amazing work! |
Great work! QQ, is the high level reporting stuff integrated into, or on a path to be integrated into, fontmake? |
Noob question, what's the difference between "build OpenType features" and "run feature writes" ? - sounds mildly like the same thing. |
The latter writes .fea files from glyph anchors and other data. The former compiled .fea files I think. |
As Behdad says. One makes binary tables, the other decides what should go in them. We have some (duelling) PRs for integrating the timing reporting into fontmake/ufo2ft and will try to sort them out today. |
I suppose another thing to draw from this is that the "write/compile features once instead of per master" PR will make quite an impact. But I can't shake the feeling that I had a year ago: we can tinker at various optimisations and maybe get 20%, 30%, but font compilation intrinsically means doing a lot of work, and there's no order-of-magnitude gain going to come from tweaking. |
The discussion - and making of tweaks along the way - is helpful to bring some of us (...maybe just me?) up to speed on the wonders of fontmake. I'm very confident that in time we'll have our 1-2 order faster compiler and we can look back on how hilariously slow it used to be over a beer. Zooming out for a moment, if we ignore our current implementation, which parts of font compilation truly are the majority of the work? IIUC our prior is that the long poles should be 1) processing glyphs (embarrasingly parallel) and 2) processing layout (...I'm less clear on how parallel this can be). There is other work but none of it is tremendously expensive. Is that fair? Missing things that are non-trivial amounts of work? |
Well, fonticulus is 60x faster than fontmake. I'll do some profiling and see what its hotspots are; would be interesting to compare. |
Issue #25 gave us an overall picture of the distribution of compilation times, but in order to identify the most profitable spots for optimisation, we need a profile of font compilation based on the distinct operations involved:
We've had attempts in the past to profile fontmake, but this has generally happened on the function level rather than on the macro "operation" level. ("Just throw a profiler at it".) This is relatively difficult to interpret as information gets lost in the weeds. I want a high level report which looks more like this:
There is some timing code in fontmake (but not in ufo2ft), so it's a matter of expanding that, adding semantic information ("What is operation is this code performing?"), and then running it on our Noto test rig and collating the data.
The text was updated successfully, but these errors were encountered: