To nie Markdown był źródłem ociężałości, lecz silnik przeglądarkowy
Jeśli wentylator zaczyna pracować, choć edytor Markdown jest tylko otwarty, nie oznacza to, że Markdown jest kosztowny. Gdy zmierzymy ten sam dokument w tych samych warunkach, okaże się, że koszt wynika z silnika renderującego i pętli powiązanej z zegarem.

Na laptopie zostawiłem otwarty edytor Markdown. Niczego nie wpisywałem, a wentylator zaczął pracować.
Na początku myślałem, że to przez Markdown. Sądziłem, że działa wolno, ponieważ bez przerwy ponownie renderuje dokument.
Czy rzeczywiście tak było?
Porównałem je w takich samych warunkach
Ustawiłem taki sam rozmiar ekranu, sposób renderowania i dokument. Otworzyłem dokument o rozmiarze 7,5 KB, zawierający 113 wierszy, i przez 25 sekund niczego nie robiłem. Podczas pisania również wprowadzałem te same znaki z taką samą szybkością.
W apostrophe, który renderuje podgląd za pomocą silnika webowego, użycie CPU w stanie bezczynności wynosiło 34,6%, a zużycie pamięci 378 MB. Aplikacja korzystała z pięciu procesów.
Prototyp, który renderował ten sam dokument bez silnika webowego, bezpośrednio w tekście, zużywał 0,7% CPU i 64 MB pamięci. Korzystał z jednego procesu.
Oba programy renderowały Markdown, ale rezultaty znacznie się różniły.
Gdy wyłączyłem w apostrophe jedynie podgląd, użycie CPU spadło do 7,0%, a zużycie pamięci do 133 MB. Aplikacja i zestaw narzędzi pozostały bez zmian. Zmienił się tylko sposób renderowania podglądu.
Zmierzyłem również edytor tekstu, który w ogóle nie renderuje Markdown. Zużywał 2,2% CPU i 79 MB pamięci.
Prototyp renderujący Markdown był wręcz lżejszy.
Dopiero wtedy wszystko stało się jasne. Problemem nie był Markdown.
Nawet gdy nic się nie działo, wykonywał pracę 60 razy na sekundę
Przyczyna tkwiła w kodzie synchronizującym przewijanie podglądu z głównym tekstem.
Ten kod nie uruchamiał się wyłącznie podczas przewijania. Co 16 ms budził się licznik czasu i wykonywał JavaScript w procesie WebKit. Po nadejściu odpowiedzi ponownie ustawiał licznik czasu.
Nawet gdy nikt nie dotykał okna, aplikacja wykonywała pracę około 60 razy na sekundę.
Gdy korzysta się z silnika webowego, obszar edycji dokumentu i obszar renderowania podglądu stają się dwoma odrębnymi światami. Najłatwiejszym sposobem synchronizowania ich stanów jest ciągłe ich sprawdzanie. Jeśli osobno nie zmierzy się użycia CPU w stanie bezczynności, ten koszt trudno zauważyć.
Podczas pisania różnica była jeszcze większa. apostrophe zużywał 180% CPU, a prototyp 41%.
W jednym przypadku każde naciśnięcie klawisza wymagało komunikacji z procesem przeglądarki. W drugim znaczniki były nakładane ponownie tylko na zmieniony wiersz.
Wyniki pomiarów przekształciłem w zasady projektowe
Prototyp nie zawierał żadnych szczególnych optymalizacji.
Style były nakładane na tekst za pomocą znaczników. Wiele zmian grupowałem w jedno jednorazowe zadanie wykonywane w stanie bezczynności, które samo znikało. Zakres ponownego nakładania znaczników ograniczyłem do zmienionych wierszy. Korzystałem też tylko z jednego procesu.
Nie dodałem niczego nowego — po prostu nie umieściłem w nim niczego zbędnego.
Następnie przyjąłem dwie zasady projektowe.
Po pierwsze, nie dodaję silnika webowego do zależności.
Po drugie, jeśli dokument się nie zmienia i nie ma żadnej animacji, nie planuję klatek, liczników czasu ani tyknięć.
Te zasady trudno wprowadzić później. Silnik webowy nie jest zwykłą biblioteką — zmienia strukturę aplikacji. Gdy raz się go doda, jego usunięcie oznacza w praktyce zbudowanie aplikacji od nowa. Jeśli zaś nie ma reguł dotyczących liczników czasu, każda funkcja zaczyna otrzymywać własny.
W zamian musiałem zrezygnować z łatwiejszych rozwiązań. Nie mogłem sprawdzać zmian w pliku za pomocą odpytywania ani powierzyć renderowania wzorów widokowi webowemu. Potrzebny układ musiałem stworzyć samodzielnie.
Aby te zasady nie uległy rozmyciu, przy każdym wydaniu ponownie wykonuję pomiary na rzeczywistym urządzeniu.

Przy interpretowaniu liczb również trzeba zachować ostrożność
Eksperyment przeprowadziłem przy użyciu renderowania programowego. Użycie CPU jest wówczas wyższe niż w środowisku z rzeczywistym GPU. Dlatego należy patrzeć na proporcje, a nie na wartości bezwzględne.
Również 0,7% użycia CPU przez prototyp w stanie bezczynności wynikało głównie z migania kursora. Po wyłączeniu migania wynik był trudny do odróżnienia od zera.
Nie oznacza to, że wszystkie aplikacje oparte na silniku webowym mają ten sam problem. Te wyniki dotyczą wyłącznie apostrophe. Istnieją również silniki, takie jak te z rodziny Chromium, które ograniczają pracę w stanie bezczynności.
Prototyp nie miał też wielu funkcji. Brakowało w nim tabel, wzorów, eksportowania i wyszukiwania. Porównanie obejmuje jedynie koszt renderowania.
Jedno było jednak pewne.
To nie Markdown generował koszty. Robiły to silnik renderujący i pętla, która nigdy się nie zatrzymywała.
Gdy dokładnie ustali się przyczynę, lekkość przestaje być nową funkcją, a staje się rezultatem odejmowania.