Da ich im Wettbewerb um JavaScript-Entwickler von Telegram nicht den ersten Platz belegt habe

Aktive Benutzer von Telegram, insbesondere diejenigen, die Pavel Durov abonniert haben, haben wahrscheinlich etwas darüber gehört, dass Telegram auf Ihren Internetseiten einen Wettbewerb für iOS-, Android- und JavaScript-Entwickler sowie für Designer veranstaltet hat. Trotz der Tatsache, dass es ein ziemlich episches Ereignis mit der Verteilung solider Preise war (einer der Teilnehmer erhielt 50.000 US-Dollar für den ersten Platz, nachdem er die schnellste und einfachste Android-Anwendung geschrieben hatte), schrieben sie zumindest auf Runet irgendwie wenig darüber. Mein Debütbeitrag wird versuchen, die Situation zu beheben.


Da ich ein Full-Stack-JavaScript-Entwickler bin (um genau zu sein ein TypeScript-Entwickler), habe ich mich entschlossen, mich selbst zu testen. Manila ist nicht nur ein Preisfonds, sondern auch das Format selbst: Dies ist kein Programmierwettbewerb, bei dem Abstraktheit und Denkgeschwindigkeit wichtig sind. Hier war alles im Komplex wichtig: Erfahrung, mittelfristige Entwicklungsgeschwindigkeit, Geschmack in UI-Fragen, Kenntnisse der gesamten Informatik und Selbstkritik. Gemäß den Wettbewerbsbedingungen musste eine Bibliothek für die Anzeige von Grafiken für eine der Plattformen entwickelt werden: iOS, Android oder Web.


Entwickler für verschiedene Plattformen konkurrierten nicht miteinander und jede Plattform hatte ihre eigenen Gewinner. Die Hauptkriterien waren: Arbeitsgeschwindigkeit (auch auf älteren Geräten), Konsistenz mit dem Design, reibungslose Animation und minimale Anwendungsgröße. Bereits vorhandene Lösungen und Bibliotheken konnten nicht verwendet werden, alles musste von Grund auf neu geschrieben werden.

Zuvor nahm ich an Wettbewerben für Entwickler teil, bei denen nicht mehr als 5 Stunden für alle Aufgaben vorgesehen waren. Diese Stunden mussten in enormem Stress verbracht werden. Trotz der Tatsache, dass das Telegramm keine solche Belastung erforderte, um die Aufgabe zu erfüllen, ist es einer der schwierigsten Wettbewerbe, an denen ich teilnehmen musste. Die scheinbar unkomplizierte Aufgabe erwies sich als so umfangreich, dass ich, wenn ich dafür bezahlt würde, diese „Grafiken“ monatelang schneiden könnte, um einen Kompromiss zwischen der Codeleistung und ihrer architektonischen Harmonie zu finden. Es hat geholfen, dass drei ( upd: zwei, danke an vlad2711 für die Änderung) Wochen für die Lösung zugewiesen wurden. Einige der Rivalen haben sich speziell verabschiedet, um mehr Zeit für den Wettbewerb aufzuwenden , und ich habe beschlossen, die Entwicklung des Wettbewerbs abends und am Wochenende wie gewohnt mit der Arbeit in „ Ontanta “ zu verbinden.

LEINWAND gegen SVG


Das wichtigste architektonische Problem, mit dem wir alle konfrontiert waren, war die Wahl eines Grafik-Rendering-Tools. Derzeit bieten uns Webstandards zwei Ansätze: durch die Erzeugung von On-the-Fly-SVG-Grafiken und die gute alte Leinwand. Hier sind die Vor- und Nachteile von jedem.

Leinwand


+ Absolute Vielseitigkeit - Mit der Möglichkeit, die Farbe jedes Pixels auf der Leinwand zu ändern, können Sie alles zeichnen, was Sie wollen.
+ [Potenzial] Hohe Leistung - Wenn Sie Leinwand vorbereiten können, kann dies eine gute Leistung zeigen. Es wäre toll, Webgl zu verwenden, aber die Unterstützung auf Smartphones ist schlecht.

- Alle Berechnungen und das gesamte Rendern von Hand - im Gegensatz zu SVG, wo die Zwischenpunkte der Polylinie einmal festgelegt werden können und Sie dann das Ansichtsfeld manipulieren können, um die „Kamera“ entlang der Abschnitte der Polylinie zu bewegen, mit Leinwand ist alles komplizierter: Es gibt hier keine „Kameras“ nur Koordinaten von der oberen linken Ecke; Wenn Sie den aktuellen Anzeigebereich des Diagramms "verschieben" müssen, müssen Sie alle Koordinaten aller seiner Punkte relativ zur neuen Position des Anzeigebereichs neu berechnen. Mit anderen Worten, das Ansichtsfeld, das in svg standardmäßig enthalten ist, muss manuell im Canvas implementiert werden.
- Die gesamte Animation ist manuell - basierend auf dem vorherigen Absatz werden alle möglichen Animationen realisiert, indem die Koordinaten, Farb- und Transparenzwerte neu berechnet und die gesamte Szene N-mal pro Sekunde neu gezeichnet werden. Je öfter die Szene nachgezählt und neu gezeichnet werden konnte, desto glatter wurde die Animation.

Svg


+ Einfaches Zeichnen - Fügen Sie SVG nur einmal die erforderlichen Linien, Formen und mehr hinzu, indem Sie die Parameter Ansichtsfenster, Farbe und Transparenz ändern und eine Diagrammnavigation bereitstellen.
+ Einfache Implementierung von Animationen - wieder reicht es aus, Ne basierend auf dem vorherigen Absatz, neue Werte für das Ansichtsfeld, die Farbe und die Transparenz mehrmals pro Sekunde anzugeben, und das Bild wird selbst neu gezeichnet, der Browser kümmert sich darum. Vergessen Sie außerdem nicht, dass Formen und Grundelemente in SVG in CSS gestaltet werden können, sodass sie mithilfe von CSS3-Animationen animiert werden können. Dies eröffnet die größten Möglichkeiten, mit minimalem Aufwand coole Animationen zu erhalten.
+ Standardmäßig gute Leistung - Wenn Sie leicht etwas Langsames und Hunderte von Ressourcen auf die Leinwand bringen können, sieht das auf SVG basierende Ergebnis immer recht leicht, anständig und flüssig aus.

Aber die Münze hat eine Kehrseite.

- Bescheidene Optimierungsmöglichkeiten - da wir nicht svg, sondern den Browser zeichnen, ist es unmöglich, diesen Prozess zu steuern. Wenn Sie die Leistung steigern möchten, indem Sie beispielsweise bereits einzelne gezeichnete Elemente zwischenspeichern, können Sie dies in keiner Weise tun. Höchstwahrscheinlich wird dies bereits vom Browser durchgeführt, aber wir können nicht sicher sein, bis zum Ende.
- Begrenzte Werkzeuge - In SVG steuern wir nicht mehr jedes Pixel der Leinwand, sondern denken und codieren im Rahmen von Vektorprimitiven. Für diese Aufgabe ist dies jedoch ein unbedeutendes Minus, das im Rahmen der Wettbewerbsaufgabe einige wiederum unbedeutende Einschränkungen auferlegt.

Ich musste mich nie mit der Wahl eines Instruments quälen, da ich ein ekelhaftes Charaktermerkmal habe - ich bin Maximalist und habe in meiner Arbeit nur mein Lieblingsinstrument verwendet. So kam es, dass mein Lieblingswerkzeug seit meiner Studienzeit, als ich mich mit DirectDraw amüsierte, immer eine Leinwand war, auf der "mach was du willst". Und Leinwand zur Lösung eines Wettbewerbsproblems erwies sich als wirklich gut, aber es spielte mir nur ein Plus davon in die Hände: die breitesten Möglichkeiten für Optimierungen, da das Hauptkriterium immer noch die Anwendungsleistung war.

Guter Code ist nicht gut


Die Aufgabe ist klar: Sie müssen Punkte an der richtigen Stelle und zur richtigen Zeit auf die Leinwand zeichnen. Es bleibt der Code zu schreiben. Wieder war es notwendig zu wählen, diesmal zwischen dem Schreiben eines produktiven kompakten Codes mit einem „Fußtuch“ in einem prozeduralen Stil oder nicht sehr produktiv und noch weniger kompakt in meinem bevorzugten objektorientierten. Sie haben wahrscheinlich bereits vermutet, dass ich die zweite Option gewählt habe und sie mit einem anderen meiner Favoriten gewürzt habe - TypeScript.

Und diese Wahl war nicht sehr richtig. Aufgrund der Verwendung von Abstraktionen und Kapselungen ist es nicht immer möglich, Zwischenberechnungsergebnisse zu speichern, zu übertragen und wiederzuverwenden, was sich negativ auf die Leistung auswirkt. Und aufgrund der weit verbreiteten Verwendung, ohne die OOP in JS nicht möglich ist, wird der Code schlecht minimiert, während auch die Größe eine Rolle spielt.

Es ist Zeit, einen Link zum Github zu geben: github.com/native-elements/telechart . Bei Interesse empfehle ich, auf die Historie der Commits zu achten, da sie an Optimierungsprüfungen und erfolglose Versuche erinnert, ein paar zusätzliche Rendering-Frames pro Sekunde herauszuquetschen.

Nun, im Wettbewerb habe ich den Preis nicht angenommen. Und das Problem, wie es bei uns Programmierern oft vorkommt, stellte sich nicht als unzureichende Erfahrung, schneller Verstand oder Geschwindigkeit heraus, sondern als unzureichende Selbstkritik: Die Tatsache, dass ich es geschafft habe, funktioniert und wie auf dem Bild aussieht, hat mich gefreut, aber Was die Rendering-Bremsen angeht, dachte ich, ich hätte alles getan, was ich konnte, der Rest tat wahrscheinlich das Gleiche. Ich schäme mich, darüber zu sprechen, aber ich war mir sicher, dass ich den ersten oder zweiten Platz einnehmen würde. Tatsächlich stellte sich heraus, dass ich ein Brems- und Buggy-Programm geschrieben habe, nicht das schlechteste, aber alles andere als das beste. Als ich die Arbeit anderer Entwickler sah, wurde mir klar, dass ich keine Chance hatte und nur meine Ellbogen beißen konnte. Wenn ich bei meiner Arbeit unparteiisch wäre, würde ich mich mit Produktivität befassen, dem wichtigsten Teil der Wettbewerbsaufgabe.

Eine der wertvollsten Lektionen in meinem Berufsleben, die ich nicht müde werde, ist, dass ein guter Ingenieur im Gegensatz zu beispielsweise einem Künstler verpflichtet ist, die Qualität seiner Arbeit objektiv zu bewerten und das Selbstvertrauen zu verwerfen, da das Ergebnis seiner Arbeit nicht nur das Auge erfreuen sollte sollte aber richtig und gut funktionieren.

Dies war die erste Phase des Wettbewerbs. Die Gewinner wurden großzügig belohnt. Zu meiner unbeschreiblichen Freude endete die Geschichte nicht dort, weil die zweite Stufe angekündigt wurde:


Es war notwendig, Ihr Handwerk in nur einer Woche zu verfeinern und zusätzliche Diagrammtypen zu implementieren. Ich werde sofort zeigen, was passiert ist, und unten werde ich Ihnen erzählen, wie es passiert ist.


In meinem Fall musste ich vor dem Hinzufügen neuer Funktionen die Leistung der alten verstehen. Das erste Problem, das ich gelöst habe, ist

Zuckende Animation

Selbst wenn Sie genug Leistung haben, um 60 Bilder pro Sekunde zu erzeugen, ist die Animation nicht flüssig, wenn die Position des Elements oder seine Transparenz nicht durch die seit dem Start der Animation verstrichene Zeit bestimmt wird. Dies ist auf ungleiche Zeitintervalle zwischen den Ticks zurückzuführen: Beispielsweise arbeitete ein Tick nach 10 ms und der zweite nach 40 ms, während das Objekt beim ersten und zweiten Tick um 1 Pixel nach links verschoben wurde - das heißt, seine Bewegungsgeschwindigkeit schwebt ständig. optisch sieht es aus wie ein "Zucken". Mit anderen Worten, Sie müssen etwas falsch machen:

let left = 10, interval = setInterval(() => { left += 1 if (left >= 90) { clearInterval(interval) } }, 10) 

Und so:

 let left = 10, startLeft = 10, targetLeft = 90, startTime = Date.now(), duration = 1000, interval = setInterval(() => { left = startLeft + (targetLeft - startLeft) * (Date.now() - startTime) / duration if (left >= targetLeft) { left = targetLeft clearInterval(interval) } }) 

Da der Code viele animierte Parameter enthält, habe ich eine universelle Klasse gefilmt, die die Aufgabe erleichtert und der Animation auch ising hinzufügt. Es ist ganz einfach zu bedienen:

 let left = Telemation.create(10, 90, 1000) … drawVerticalLine(left.value) //      ,  . 

Dann kommt die 60-fps-Regel ins Spiel. PC-Spieler werden mich verstehen: Damit eine Animation perfekt aussieht, muss sie mit einer Geschwindigkeit von mindestens 60 fps gerendert werden. Dementsprechend sollte jedes Rendern des Rahmens nicht länger als 1/60 Sekunde dauern. Dies erfordert leistungsstarke Hardware und guten Code.

Weitere Untersuchungen haben dies gezeigt

Das Malen der Leinwand wird langsamer, wenn sich HTML-Elemente über der Leinwand befinden .

Anfangs habe ich "leere" HTML-Elemente verwendet, um die Kontrolle über das aktuelle Ansichtsfenster zu implementieren:


Diese Elemente wurden auf der Leinwand platziert, und obwohl sie keinen Inhalt hatten, wurden sie nur zum Verfolgen von Mausereignissen verwendet. Als Ergebnis von Experimenten stellte sich heraus, dass ihre Anwesenheit die Renderleistung verringert. Indem ich sie entfernte und die Logik der Bestimmung von Ereignissen zur Steuerung des Anzeigebereichs etwas komplizierter machte, erhöhte ich die Geschwindigkeit beim Rendern des Rahmens.

Es blieb, den letzten Nagel vom Deckel des Sarges der Leistung zu ziehen: Ich tat es

Minimap-Caching

Zuvor wurden für die Minikarte wieder Linien in jedem Frame gezeichnet. Dies ist eine teure Operation, da der gesamte Zeitplan für das Jahr angezeigt wird (365 Punkte pro Zeile). Die offensichtliche Lösung, für deren Implementierung ich von Anfang an einfach zu faul war, bestand darin, die Linien des Diagramms für die Minikarte einmal zu zeichnen, das Ergebnis im Cache zu speichern und diesen Cache in Zukunft zu verwenden. Nach dieser Optimierung ist die Anwendungsleistung nicht mehr peinlich.

Was kommt als nächstes?


Es gab immer noch viele erfolgreiche und nicht sehr um die Leistung kämpfende Versuche: Versuche, die Ergebnisse von Koordinatenberechnungen zwischenzuspeichern, Experimente mit den lineJoin-Parametern von CanvasRenderingContext2D (schnellere Gehrung), aber sie sind nicht so interessant, weil sie keinen merklichen Leistungsgewinn oder gar keinen erzielten.

Von den acht Tagen habe ich fünf für die Beschleunigung des Codes und nur drei für die Fertigstellung der neuen Funktionalität aufgewendet. Ja, ich habe nur drei Tage gebraucht, um neue Diagrammtypen hinzuzufügen, und hier hat sich OOP als sehr praktisch herausgestellt, wodurch sich die Codebasis leicht erhöht hat. Ich hatte nicht genug Zeit, um die Bonusaufgabe abzuschließen (+5 zusätzliche Charts). Ich glaube, dass ich diese fünf Tage, die ich damit verbracht habe, die Konsequenzen meines Selbstbewusstseins zu beseitigen, damit verbringen könnte, das Bonusproblem zu lösen.

Trotzdem ergab meine Arbeit das Ergebnis: 4. Platz und ein Trostpreis von tausend Dollar:


Der Wettbewerb ging übrigens weiter, aber ohne mich.

Ich freue mich über die Teilnahme: Ich war nicht nur interessant und ein interessantes Abenteuer, sondern habe auch eine gute Berufserfahrung und eine Lektion fürs Leben erhalten.

Darüber hinaus habe ich diese Bibliothek bei der Entwicklung unseres Corporate Timetrackers verwendet, über den ich in naher Zukunft auch sprechen möchte.

Zur Diskussion schlage ich folgende Frage vor: Warum braucht Telegramm das alles? Ich glaube, dass Telegram für angemessenes Geld die beste Bibliothek der Welt für die Anzeige von Diagrammen erhalten wird: das beste Ergebnis aus Hunderten von Versuchen, es besser zu machen als andere. Das Wettbewerbsprinzip ermöglicht es Ihnen, ein so hohes Qualitätsniveau zu erreichen, dass niemand auf Bestellung und ohne Geld arbeiten kann.

Und ein paar Links:


Source: https://habr.com/ru/post/de460625/


All Articles