Open gaenseklein opened 6 years ago
nix geändert | neue seiten werden eingefügt | eine seite wird ausgetauscht | zwei seiten werden ausgetauscht durch eine neue | eine seite wird verdoppelt | eine seite wird gelöscht | eine doppelte seite wird verändert/ausgetauscht | eine mehrfach vorkommende seite wird ausgetauscht |
---|---|---|---|---|---|---|---|
1 1 | 1 1 | 1 1 | 1 1 | 1 1 | 1 1 | 1 1 | 1 1 |
2 2 | 2 a | 2 a | 2 a | 2 2 | 2 3 | 2 a | 2 a |
3 3 | 3 b | 3 3 | 3 4 | 3 2 a | 3 4 | 2 2 | 2 2 |
4 4 | 4 2 | 4 4 | 4 5 | 4 3 | 4 5 | 3 3 | 2 2 |
5 5 | 5 3 | 5 5 | 5 6 | 5 4 | 5 6 | 4 4 | 3 3 |
Gehe durch die Seiten durch und vergleiche sie. Sind sie nicht mehr gleich, ist das die Startseite. In den Beispielen oben habe ich dafür immer die 2. Seite gewählt. Aller Inhalt vor der Startseite muss gleich sein. Braucht also nicht neu gerendert zu werden :)
Hier wirds komplizierter. Aus der Tabelle oben kann aber eine einfache Logik erkannt werden:
Seiten verschieben sich. Es muss also diese Verschiebung festgestellt werden. In den ersten Beispielen ist dies recht einfach. Die letzten sind diejenigen, welche Probleme machen.
Die Logik funktioniert folgendermaßen:
Nimm die nächste Seite des neuen Inhalts. Vergleiche diese Seite mit der Seite auf der Position der Startseite im alten Inhalt. Stimmen sie überein gibts einen Treffer. Wenn nicht, gehe den alten Inhalt durch, bis du entweder einen Treffer findest oder ein bestimmtes Maß überschreitest - bspw. 5 Seiten. Hast du innerhalb der nächsten 5 Seiten keinen Treffer gefunden, nimm die nächste Seite aus dem neuen Inhalt und versuche hier eine Übereinstimmung zu finden. Wiederhole dies mit den nächsten 5 Seiten aus dem neuen Inhalt bis du einen Treffer findest. Hast du nach all dem keinen neuen Treffer erzielt - brich das ganze ab und render alles neu ab der Startseite.
Hast du einen Treffer erzielt merke dir die Startseite und die Endseite.
Diese Logik funktioniert prima. Bis auf die blöden Sonderfälle. Wenn nämlich doppelte Seiten im alten Inhalt vorkommen (bspw. der User hat erstmal leere Seiten angelegt per Copy/Paste o.ä.) kommt es hier zu Fehlern. Diese müssen also noch abgefangen werden. Da der Fehler nur in diesem Fall auftritt - also wenn es doppelte bzw. mehrfache Seiten im alten Inhalt gibt - kann er aber abgefangen werden durch die Abfrage: Ist die nächste Seite nach der Startseitenposition im alten Inhalt gleich der Seite auf der Startseitenposition im alten Inhalt? Wenn ja, dann suche solange weiter bis du eine sich davon abweichende Seite gefunden hast - und suche diese im neuen Index ab der Startposition. Hast du beide gefunden so kannst du diese für die Endseite bzw. die Verschiebung nutzen. Dazu musst du zwar evtl. doch mehr Rendern, aber das ist halt so und immer noch schneller als alles neu zu rendern.
Hole dir die entsprechenden Zeilen der zu ändernden Seiten. Sind es zu wenige um dort den neuen Inhalt einzufügen, füge neue Zeilen ein bis die Zeilen wieder gleich viele wie vom neuen Inhalt sind. Sind es zu viele lösche so viele Zeilen aus dem DOM ab der Startseite bis die Zeilen wieder gleich viele wie vom neuen, einzufügenden Inhalt sind. Jetzt gehe die entsprechenden Zeilen durch und tausche ihren Inhalt durch neu gerenderten Inhalt aus dem neuen Parsen aus. Zum Schluss gehst du noch alle Pagebreaks nach der Startseite durch und tauschst die Pagenummern aus, da diese sich verschoben haben können und es nicht lohnt, dies ebenfalls zu überprüfen.
Im Bestcase wird nur eine Seite neu gerendert - und zwar die Letzte. Das sollte das Rendern auf deutlich unter 20 Ms drücken. Im Worstcase, die erste Seite wird verändert und die folgenden Seiten sind Kopien der ersten Seite, kann es passieren, dass alles neu gerendert werden muss - und noch zusätzlich alle Abfragen neu gestellt werden mussten. Das könnte geschätzt dazu führen, dass das Rendern sich zusätzlich um 1-5Ms verlangsamt durch die vielen zusätzlichen Abfragen. Es kommt natürlich auf die Länge der Präsentation an, sowie dem Maximum, was man dem Renderer zum Prüfen erlaubt. Wenn die zusätzlichen Überprüfungen länger dauern als das Rendern lohnt es sich nicht. Da aber Überprüfungen viel schneller sind als Textveränderungen und Domveränderungen (siehe neuen Parser) denke ich, dass das keine Rolle spielen sollte und evtl. der oben genannte Treshhold (5 Seiten max) sogar auf alle folgenden Seiten ausgedehnt werden könnte. Aber das müsste ein Praxistext der Logik zeigen - das sind alles nur Schätzungen.
Solange Parsen und Rendern zusammen unter 100Ms bleiben lohnt sich das Beschleunigen kaum. Es wird für den User nicht zu einem spürbaren Ergebnis kommen. Dadurch, dass ich das neu Rendern eh nur dann mache, wenn es wirklich notwendig ist und nicht, wenn einfach getippt wird, kann bereits jetzt ein flüssiges Arbeiten bemerkt werden und die Verzögerung tritt nur in bestimmten Fällen auf wie bspw. das Löschen, das Drücken von Enter oder das Einfügen von MD-Code. Die Benchmarks, die ich in #31 augestellt habe, zeigen, dass bei einem Core i 7-Quadcore Rechner sich der Aufwand erst nach geschätzt 30 Seiten Präsentation bemerkbar machen. Selbst 50 Seiten führen "nur" zu einer Verzögerung von ca. 200Ms, ab da fängt es aber an deutlich zu nerven. Allerdings: Ein sechs Jahre alter i-7 kann immer noch deutlich schneller sein als ein I-Pad oder ähnliches. Auch wenn von Javascript aus Sicherheitsgründen nur ein Prozessor gleichzeitig benutzt werden kann und ich somit die selbe Geschwindigkeit habe wie ein Single-Core Rechner mit 1,6Ghz... kann ich den als Maßstab nehmen? Oder sollten wir versuchen, den Editor "so schnell wie möglich" zu machen? Dann sollten wir die Logik einführen und ausprobieren. Geschätzter Arbeitsaufwand: 1-2 Arbeitstage
Wenn Parsen und Rendern so schnell sind, dass sie insgesamt unter 50 Ms kommen, kann überlegt werden, die Edge-Cases komplett aufzugeben, da sie sich nicht mehr lohnen. Dadurch kommt es zu einer besseren, da schnelleren Rückkopplung der eingegebenen Sachen. Allerdings: schneller als die "normale" Eingabe wird es nie. Ich denke, dass es sehr wohl Sinn macht, ihn so schnell wie möglich zu machen. Blöd wäre nur, wenn dadurch Fehler entstehen.
Fehlerüberprüfung für Sonderfall funktioniert leider nicht wenn doppelte Seiten erneut eingefügt werden. Dann kommt es zu Fehlern. Da der Fall zu schwierig abzuarbeiten ist und nur vorkommt nach copy-paste lieber alles ab startpage neu rendern und hoffen, dass das Problem alsbald vom user behoben ist und das rendern wieder schneller wird?
Der Flaschenhals beim Erstellen des Hintergrundcodes für dem MD-Codeeditor liegt inzwischen ja beim Rendern und nicht mehr beim Parsen. Daher wäre es schön, wenn das Rendern beschleunigt werden kann. Grundidee ist hierbei, möglichst wenig zu rendern, also nur das, was wirklich gerendert werden muss. Dabei tun sich natürlich gewisse Gefahren auf: Das Renderergebnis muss richtig sein und darf nie, unter keinen Umständen, vom richtigen Ergebnis abweichen. Klingt banal, ist es aber leider nicht. Beispiel Zeilenbasiert: Wenn ich 4 Zeilen habe:
habe ich in diesen vier Zeilen 4 Elemente. Lösche ich bspw. in der zweiten Zeile das Sternchen, so hat das auch Auswirkungen auf die erste Zeile. Für den Renderer sieht das aber gleich aus: die erste Zeile hat sich ja nicht verändert. Klar, könnte ich jetzt auch noch zusätzlich Abfragen, ob in der ersten Zeile sich denn die Elemente verändert haben, aber auch das ist fehleranfällig. Im Endeffekt würde ich so viele Abfragen machen, dass das Rendern durch diese vielen Abfragen wieder gebremst wird.
Lösungsidee: Ich vergleiche stattdessen Pages - so muss im Best-Case (der sehr häufig auftritt) nur eine Seite neu gerendert werden. Auch hier gibt es Fehlerquellen die auszumerzen sind. Um nicht zu viele Sonderfälle zu bekommen konzentriere ich mich auf eine Logik, die möglichst viele Fälle abdeckt und versucht, sparsam zu sein. Stellt die Logik fest, dass sie nicht greift rendert sie im Zweifel lieber alles neu und nimmt den Geschwindigkeitsverlust dafür in Kauf.