Nicht Markdown machte es schwerfällig, sondern die Web-Engine
Wenn der Lüfter läuft, obwohl nur ein Markdown-Editor geöffnet ist, liegt das nicht daran, dass Markdown rechenintensiv wäre. Misst man dasselbe Dokument unter denselben Bedingungen, zeigt sich, dass die Kosten von der Rendering-Engine und einer an die Uhr gekoppelten Schleife verursacht werden.

Ich hatte auf meinem Laptop einen Markdown-Editor geöffnet. Obwohl ich nichts eingab, begann der Lüfter zu laufen.
Zuerst dachte ich, es läge an Markdown. Ich dachte, er sei langsam, weil das Dokument ständig neu gezeichnet werde.
War das wirklich so?
Ich habe unter gleichen Bedingungen verglichen
Ich habe Bildschirmgröße, Rendering-Methode und Dokument vollständig angeglichen. Ich öffnete ein 7,5 KB großes Dokument mit 113 Zeilen und tat 25 Sekunden lang nichts. Auch beim Tippen gab ich dieselben Zeichen mit derselben Geschwindigkeit ein.
Die Leerlauf-CPU-Auslastung von apostrophe, das die Vorschau mit einer Web-Engine rendert, betrug 34,6 %, der Speicherverbrauch 378 MB. Dabei liefen fünf Prozesse.
Der Prototyp, der dasselbe Dokument ohne Web-Engine inline darstellte, verbrauchte 0,7 % CPU und 64 MB Speicher. Dabei lief nur ein Prozess.
Beide renderten Markdown, doch die Ergebnisse unterschieden sich erheblich.
Als ich in apostrophe nur die Vorschau deaktivierte, sank die CPU-Auslastung auf 7,0 % und der Speicherverbrauch auf 133 MB. Sowohl die App als auch das Toolkit blieben unverändert. Geändert hatte sich nur die Art, wie die Vorschau gerendert wurde.
Ich maß auch einen Texteditor, der überhaupt kein Markdown rendert. Seine CPU-Auslastung betrug 2,2 %, sein Speicherverbrauch 79 MB.
Der Prototyp, der Markdown renderte, war sogar ressourcenschonender.
Da wurde es eindeutig. Das Problem war nicht Markdown.
Selbst ohne irgendeine Eingabe arbeitete er 60-mal pro Sekunde
Die Ursache lag im Code, der den Bildlauf der Vorschau mit dem Haupttext synchronisierte.
Dieser Code wurde nicht nur beim Scrollen ausgeführt. Alle 16 ms wachte ein Timer auf und führte im WebKit-Prozess JavaScript aus. Sobald die Antwort eintraf, wurde der Timer erneut gesetzt.
Selbst wenn niemand das Fenster berührte, wurde also etwa 60-mal pro Sekunde Arbeit verrichtet.
Bei der Verwendung einer Web-Engine werden der Bereich zum Bearbeiten des Dokuments und der Bereich zum Rendern der Vorschau zu zwei getrennten Welten. Die einfachste Möglichkeit, beide Zustände abzugleichen, besteht darin, sie fortlaufend zu überprüfen. Wenn man die CPU-Auslastung im Leerlauf nicht gesondert misst, fällt dieser Aufwand kaum auf.
Beim Tippen wurde der Unterschied noch größer. apostrophe verbrauchte 180 % CPU, der Prototyp 41 %.
Bei der einen Anwendung führte jeder Tastendruck zu einem Austausch mit dem Browserprozess. Bei der anderen wurden nur die geänderten Zeilen neu mit Tags versehen.
Ich machte aus den Messergebnissen Gestaltungsprinzipien
Der Prototyp enthielt keine besonderen Optimierungen.
Die Formatierung wurde mithilfe von Tags auf den Text angewendet. Mehrere Bearbeitungsvorgänge wurden in einem einmaligen Idle-Handler gebündelt, der sich selbst wieder entfernte. Das erneute Setzen der Tags wurde auf die geänderten Zeilen beschränkt. Außerdem wurde nur ein einziger Prozess verwendet.
Es wurde nicht etwas hinzugefügt, sondern auf Unnötiges verzichtet.
Anschließend legte ich zwei Dinge als Gestaltungsprinzipien fest.
Erstens nehme ich keine Web-Engine in die Abhängigkeiten auf.
Zweitens plane ich keine Frames, Timer oder Ticks ein, wenn sich das Dokument nicht ändert und keine Animation läuft.
Diese Prinzipien lassen sich später nur schwer ergänzen. Eine Web-Engine ist keine einfache Bibliothek, sondern verändert die Struktur der App. Sobald sie einmal eingebaut ist, kommt ihre Entfernung einem Neubau gleich. Und ohne klare Regeln entsteht für jede Funktion ein weiterer Timer.
Dafür musste ich auf den einfachen Weg verzichten. Dateiänderungen ließen sich nicht per Polling überwachen, und Formeln konnten nicht einer WebView überlassen werden. Die benötigten Layouts musste ich selbst erstellen.
Damit diese Prinzipien nicht verwässern, messe ich bei jedem Release erneut auf einem realen Gerät.

Beim Betrachten der Zahlen gibt es ebenfalls einiges zu beachten
Das Experiment wurde mit Software-Rendering durchgeführt. Dadurch fällt die CPU-Auslastung höher aus als in einer Umgebung mit einer echten GPU. Deshalb sollte man eher auf die Verhältnisse als auf die absoluten Werte achten.
Auch die Leerlauf-CPU-Auslastung des Prototyps von 0,7 % entstand größtenteils durch das Blinken des Cursors. Wenn ich das Blinken deaktivierte, war der Wert kaum noch von 0 zu unterscheiden.
Das bedeutet nicht, dass alle auf Web-Engines basierenden Apps dasselbe Problem haben. Diese Ergebnisse gelten nur für apostrophe. Es gibt auch Engines wie die Chromium-Familie, die ihre Aktivität im Leerlauf reduzieren.
Der Prototyp verfügte außerdem nicht über viele Funktionen. Tabellen, Formeln, Export und Suche fehlten. Vergleichbar sind die Ergebnisse nur bis hin zu den Rendering-Kosten.
Trotzdem war eines eindeutig.
Nicht Markdown verursachte die Kosten. Es waren die Rendering-Engine und die ununterbrochen laufende Schleife.
Wenn man die Ursache genau findet, wird Ressourcenschonung nicht zu einer neuen Funktion, sondern zu einer Frage des Weglassens.