Kurz gesagt: Firefox’s integrierter PDF-Viewer wird mit der Veröffentlichung von Firefox 33 Mitte Oktober voraussichtlich weitere erhebliche Speicherverbrauchssenkungen erfahren.
Anfang des Jahres habe ich einige bedeutende Verbesserungen an der Speichernutzung und Geschwindigkeit von pdf.js, dem integrierten PDF-Viewer von Firefox, vorgenommen. Damals war ich ziemlich zufrieden mit mir selbst. Ich hatte die “Low-Hanging Fruits” gepflückt und einen Punkt abnehmender Erträge erreicht.
Unerwartete Messungen auf dem Mac
Kurz darauf versuchte ich, während eines Fluges, pdf.js auf meinem Mac-Laptop zu nutzen. Ich war entsetzt, als ich feststellte, dass die Speichernutzung auf meinem Mac-Laptop weitaus höher war als auf meiner Linux-Desktop-Maschine, auf der ich meine Optimierungen durchgeführt hatte. Das erste Dokument, das ich maß, ließ den RSS (Resident Set Size, d. h. physischen Speicherverbrauch) von Firefox auf über 1.000 MiB ansteigen. Die entsprechende Zahl auf meinem Linux-Desktop betrug nur 400 MiB! Was war hier los?
Die kurze Antwort lautet “Canvases”. pdf.js verwendet HTML-Canvas-Elemente, um jede PDF-Seite darzustellen. Diese können Millionen von Pixeln groß sein, und jeder Canvas-Pixel benötigt 32 Bit RGBA-Daten. Unter Linux werden diese Daten jedoch nicht innerhalb des Firefox-Prozesses gespeichert. Stattdessen werden sie an den X-Server übergeben. Die Seite about:memory von Firefox meldet zwar die Canvas-Nutzung unter den Bezeichnungen “canvas-2d-pixels” und “gfx-xlib”, aber diese hatte ich nicht beachtet. Während meiner Optimierungsarbeiten hatte ich nur den RSS des Firefox-Prozesses gemessen. RSS ist normalerweise die beste einzelne Kennzahl, auf die man sich konzentrieren sollte, aber in diesem Fall war sie äußerst irreführend.
Im Gegensatz dazu werden unter macOS die Canvas-Daten innerhalb des Prozesses gespeichert, weshalb der RSS von Firefox unter macOS so viel höher ist. Hinzu kommt, dass mein MacBook einen Retina-Bildschirm hat, was bedeutet, dass die verwendeten Canvases ungefähr viermal so viele Pixel haben wie die auf meiner Linux-Maschine verwendeten.
Das Problem beheben
Es stellte sich heraus, dass pdf.js absichtlich eine übermäßig großzügige Anzahl von Canvases (20) zwischenspeicherte und diese dann unbeabsichtigt nicht rechtzeitig freigab. Dies konnte dazu führen, dass Hunderte von Canvases unnötigerweise vorgehalten wurden, wenn man schnell durch ein großes Dokument scrollte. Auf meinem MacBook kostet jeder Canvas über 20 MiB, was zu einem enormen Speicheranstieg führen konnte.
Glücklicherweise konnte ich dieses Verhalten mit vier winzigen Patches, hier, nochmal und zuletzt beheben. pdf.js speichert jetzt nur noch 10 Canvases zwischen und gibt überschüssige sofort frei.
Messungen im Detail
Ich habe die Auswirkungen beider Optimierungsrunden auf die folgenden drei Dokumente auf meinem Mac gemessen:
- TraceMonkey: Dies ist eine wissenschaftliche Arbeit über den TraceMonkey JIT-Compiler. Es handelt sich um ein 14-seitiges LaTeX-Dokument mit mehreren Bildern und Grafiken.
- Telefónica: Dies ist der Jahresbericht 2009 von Telefónica. Es ist ein 122-seitiges, farbiges Dokument mit vielen Grafiken, Tabellen und Bildern.
- Decontamination: Dies ist ein alter Bericht über die Dekontamination einer Industrieanlage in New Jersey. Es handelt sich um einen Scan eines 226-seitigen Dokuments, das hauptsächlich aus schwarz-weißen Texten und groben Grafiken besteht, wobei nur wenige Seiten Farbe aufweisen.
Für jedes Dokument habe ich den maximalen RSS gemessen, während ich schnell von der ersten zur letzten Seite gescrollt bin, indem ich gleichzeitig die “fn”- und die Abwärtstaste gedrückt gehalten habe. Obwohl dies ein Stresstest ist, ist er nicht unrealistisch; man kann sich leicht vorstellen, dass Benutzer schnell durch Dutzende oder sogar Hunderte von Seiten einer PDF-Datei scrollen, um eine bestimmte Seite zu finden.
Ich habe dies dreimal für jedes Dokument durchgeführt und den höchsten Wert ausgewählt. Die Ergebnisse sind in der folgenden Grafik dargestellt. Die “Fx28”-Balken zeigen die ursprünglichen Spitzen-RSS-Werte; die “Fx29”-Balken zeigen die Spitzen-RSS-Werte nach meiner ersten Optimierungsrunde; und die “Fx33”-Balken zeigen die Spitzen-RSS-Werte nach meiner zweiten Optimierungsrunde.
Screenshot der Speicherverbrauchs-Optimierung
Vergleicht man Firefox 28 und Firefox 33, so betragen die Reduzierungen des Spitzen-RSS für die drei Dokumente das 1,2-fache, 4,4-fache und 7,8-fache. Es ist sinnvoll, dass die relativen Verbesserungen mit der Länge der Dokumente zunehmen.
Trotz dieser erheblichen Verbesserungen verbraucht pdf.js immer noch deutlich mehr Speicher als native PDF-Viewer. Wir müssen also weiter daran arbeiten… aber es ist auch erwähnenswert, wie weit wir gekommen sind.
Status der Optimierungen
Diese Patches wurden in das Master-Repository von pdf.js integriert. Sie wurden noch nicht in den Code von Firefox übernommen, aber dies wird irgendwann während des Entwicklungszyklus für Firefox 33 geschehen. Firefox 33 wird voraussichtlich Mitte Oktober veröffentlicht. Die stetige Verbesserung der Browsertechnologie, wie hier beim PDF-Viewer von Firefox, zeigt das Engagement von Mozilla für eine optimierte Nutzererfahrung. Nutzer, die Wert auf Leistung und Effizienz legen, werden die Vorteile dieser neuen Version von Firefox zu schätzen wissen.
