PERF: Let FreeType read font data from memory instead of through Python#32064
PERF: Let FreeType read font data from memory instead of through Python#32064scottshambaugh wants to merge 2 commits into
Conversation
|
How does this intersect with a (hot) disk cache? What is the memory hit (discussed this in person with @ksunden ). |
|
My understanding of how this works is that the disk cache is on the kernel side, and doesn't help with the userspace calls to fetch that data (much less the python overhead). I believe the reason the WSL bridge/network drives are so much slower here is because they can't have a disk cache. mmap forces that cache to happen, and skips the kernel for direct memory reads, so there's no additional memory usage (for local filesystems) or syscall/python overhead (for either). If the file changes underneath while we're running then the values we've already loaded to ram will be stale, but I don't think that's behavior we need to protect for. Measuring it, DejaVuSans is ~738kB on disk, and for the ascii character set mmap lazily loads only ~292kB of this into ram (mostly headers). But again, I think this memory usage is just the disk cache made explicit and not actually additional. It's a free win AFAICT. |
…h python More targeted error handling
bdf46d9 to
3b5fe33
Compare
| // loads read from it directly rather than routing through the Python | ||
| // file object. | ||
| py::object data; | ||
| try { |
There was a problem hiding this comment.
This is not available on WASI. Is the fallback sufficient here or do we need to keep the old code-path around for that case?
There was a problem hiding this comment.
The fallback is still solves the issue relative to the old method because it loads the whole contents of the file into memory (rather than mmap's selective loading).
But, I didn't realize mmap wasn't available on some platforms. Updated with an import check, and a test for that fallback path.
|
@QuLogic How do we get the WASM tests to run? |
PR summary
FT2Fontcurrently re-reads data from the font file on every glyph load, costing 10 lseek and 4 read syscalls per glyph on everyFT2Font.set_textcall. This is still relatively fast on a local filesystem, but imposes huge IO strain on remote filesystems (WSL's /mnt/c bridge to windows in my particular case, but also network mounts).This PR mmaps the font file and hands the buffer to FreeType, so glyph loads skip the Python layer and only touch the filesystem once. Python still handles opening the file for unicode path handling, with a test added for that.
Note that we build freetype with meson's
auto_features=disabled, so itsmmap=autoline was a noop and removed in this PR. It doesn't matter here since we're handing freetype the data instead of a path.Measuring syscalls directly on the font file for 30 glyphs across 10
set_textcalls, we drop from ~2900 lseeks and ~1200 reads to 1 lseek and 1 mmap. When using the local linux filesystem on my machine,set_textis ~15x faster and a savefig on an empty plot is ~1.5x faster. When I cross the WSL filesystem boundary, the speedups are ~1000x forset_textand ~10x for savefig.Before (Across WSL filesystem boundary):
After:
AI Disclosure
Discovered myself, investigated and prototyped with claude code, manually reviewed / edited.
PR checklist