Pengguna aktif Telegram, terutama mereka yang berlangganan Pavel Durov, mungkin mendengar sesuatu tentang fakta bahwa Telegram mengadakan kompetisi untuk iOS, Android dan pengembang JavaScript, serta untuk desainer, di situs internet Anda ini. Terlepas dari kenyataan bahwa itu adalah peristiwa yang agak epik dengan distribusi hadiah padat (salah satu peserta menerima $ 50k untuk tempat pertama, setelah menulis aplikasi Android tercepat dan termudah), mereka entah bagaimana menulis sedikit tentang hal itu, setidaknya di Runet. Posting debut saya akan mencoba untuk memperbaiki situasi.

Karena saya adalah seorang pengembang penuh-tumpukan JavaScript (tepatnya, kemudian seorang pengembang TypeScript), saya memutuskan untuk menguji diri. Manila bukan hanya dana hadiah, tetapi juga formatnya sendiri: ini bukan kompetisi pemrograman di mana keabstrakan dan kecepatan berpikir sangat penting. Segala sesuatu di kompleks itu penting di sini: pengalaman, kecepatan pengembangan dalam jangka menengah, rasa dalam masalah UI, pengetahuan ilmu komputer pada umumnya, dan kritik-diri. Menurut ketentuan kompetisi, perlu untuk mengembangkan perpustakaan untuk menampilkan grafik untuk salah satu platform: iOS, Android atau Web.

Pengembang untuk platform yang berbeda tidak bersaing satu sama lain, dan masing-masing platform memiliki pemenangnya sendiri. Kriteria utama adalah: kecepatan kerja (termasuk pada perangkat yang lebih tua), konsistensi dengan desain, animasi yang halus dan ukuran aplikasi minimal. Solusi dan perpustakaan yang sudah ada tidak dapat digunakan, semuanya harus ditulis dari awal.
Sebelum itu, saya berpartisipasi dalam kompetisi untuk pengembang, di mana tidak lebih dari 5 jam dialokasikan untuk semua tugas, jam-jam ini harus dihabiskan dalam tekanan yang luar biasa. Terlepas dari kenyataan bahwa Telegram tidak memerlukan tekanan seperti itu untuk menyelesaikan tugas, itu adalah salah satu kontes yang paling sulit di mana saya harus berpartisipasi. Tugas yang tampaknya tidak rumit ternyata sangat luas sehingga jika saya dibayar untuk itu, saya bisa memotong "grafik" ini selama berbulan-bulan, mencoba mencari kompromi antara kinerja kode dan harmoni arsitekturnya. Itu membantu bahwa tiga (
pembaruan: dua, berkat
vlad2711 untuk amandemen) minggu dialokasikan untuk solusi. Beberapa saingan secara khusus mengambil cuti untuk mencurahkan lebih banyak waktu untuk kontes, dan saya memutuskan untuk menggabungkan pengembangan untuk kontes di malam hari dan pada akhir pekan dengan bekerja di "
Ontanta " seperti biasa.
KANVAS versus SVG
Masalah arsitektur paling penting yang dihadapi kita semua adalah pilihan alat rendering grafik. Saat ini, standar web menawarkan dua pendekatan: melalui generasi grafis svg on-the-fly dan kanvas tua yang bagus. Berikut adalah pro dan kontra dari masing-masing.
Kanvas
+ Fleksibilitas absolut - memiliki kemampuan untuk mengubah warna piksel pada kanvas, Anda dapat menggambar apa pun yang Anda inginkan.
+ [Potensi] Performa tinggi - jika Anda dapat menyiapkan kanvas, itu dapat menunjukkan kinerja yang baik. Akan lebih bagus menggunakan webgl, tetapi dukungannya pada smartphone buruk.
- Semua perhitungan dan semua render dengan tangan - tidak seperti SVG, di mana titik-titik tengah dari polyline dapat diatur satu kali, dan kemudian Anda dapat memanipulasi kotak tampilan untuk memindahkan "kamera" di sepanjang bagian-bagian dari polyline, dengan kanvas semuanya lebih rumit: tidak ada "kamera" di sini, ada hanya koordinat dari sudut kiri atas; jika Anda perlu "memindahkan" area tampilan grafik saat ini, Anda harus menghitung ulang semua koordinat semua poin relatif terhadap posisi baru area tampilan. Dengan kata lain, viewbox, yang dalam svg di luar kotak, perlu diimplementasikan dalam kanvas secara manual.
- Seluruh animasi adalah manual - berdasarkan paragraf sebelumnya, semua animasi yang mungkin diwujudkan dengan menghitung ulang koordinat, warna dan nilai transparansi dan menggambar ulang seluruh adegan N-th jumlah kali per detik, dan semakin banyak itu mungkin untuk menghitung ulang dan menggambar ulang adegan, semakin lancar animasi.
Svg
+ Gambar sederhana - cukup tambahkan garis, bentuk, dan banyak lagi yang diperlukan ke SVG sekali, dengan memanipulasi viewport, warna dan parameter transparansi, menyediakan navigasi grafik.
+ Implementasi animasi yang sederhana - sekali lagi, berdasarkan paragraf sebelumnya, cukup Ne untuk menentukan nilai baru untuk viewbox, warna dan transparansi beberapa kali per detik, dan gambar akan digambar ulang sendiri, browser akan mengurus ini. Selain itu, jangan lupa bahwa bentuk dan primitif dalam SVG dapat ditata dalam CSS, sehingga mereka dapat dianimasikan menggunakan animasi CSS3, yang membuka kemungkinan seluas-luasnya untuk mendapatkan animasi keren dengan upaya minimal.
+ Performa yang bagus secara default - jika Anda dapat dengan mudah meletakkan sesuatu yang lambat dan menghabiskan ratusan sumber daya di atas kanvas, hasilnya berdasarkan SVG akan selalu terlihat sangat ringan, layak dan halus.
Tetapi ada sisi lain dari koin.
- Peluang sederhana untuk pengoptimalan - karena kita bukan menggambar svg, tetapi browser, maka tidak mungkin untuk mengontrol proses ini - jika Anda ingin meningkatkan kinerja, misalnya, dengan melakukan caching elemen yang diambil secara individu, Anda tidak dapat melakukannya dengan cara apa pun. Kemungkinan besar ini sudah dilakukan oleh browser, tetapi kami tidak bisa memastikan sampai akhir.
- Alat terbatas - dalam SVG kami tidak lagi mengontrol setiap piksel kanvas, tetapi berpikir dan kode dalam kerangka vektor primitif. Namun, untuk tugas ini, ini adalah minus yang tidak signifikan, memaksakan beberapa pembatasan yang tidak signifikan lagi dalam konteks tugas kompetisi.
Saya tidak pernah perlu khawatir tentang memilih alat, karena saya memiliki sifat karakter yang menjijikkan - saya seorang yang maksimal dan hanya menggunakan alat favorit saya dalam pekerjaan saya. Kebetulan sejak masa mahasiswa saya, ketika saya menghibur diri dengan DirectDraw, alat favorit saya selalu berupa kanvas di mana "lakukan apa yang Anda inginkan." Dan kanvas untuk memecahkan masalah persaingan benar-benar bagus, tetapi benar-benar hanya ada satu di tangan saya: peluang terluas untuk optimisasi, karena kriteria utamanya adalah kinerja aplikasi.
Kode yang baik tidak baik
Tugasnya jelas: Anda perlu menggambar titik-titik pada kanvas di tempat yang tepat dan pada waktu yang tepat. Masih menulis kode. Sekali lagi, perlu untuk memilih, kali ini antara menulis kode ringkas produktif dengan satu "alas kaki" dengan gaya prosedural atau tidak terlalu produktif dan bahkan kurang kompak dalam berorientasi objek favorit saya. Anda mungkin sudah menebak bahwa saya memilih opsi kedua, membumbui dengan favorit saya yang lain - TypeScript.
Dan pilihan ini tidak terlalu benar. Karena penggunaan abstraksi dan enkapsulasi, tidak selalu mungkin untuk menyimpan, mengirim dan menggunakan kembali hasil perhitungan menengah, yang mempengaruhi kinerja dengan buruk. Dan karena meluasnya penggunaan ini, yang tanpanya OOP di JS tidak mungkin, kode ini diperkecil dengan buruk, sementara ukurannya juga penting.
Saatnya memberikan tautan ke github:
github.com/native-elements/telechart . Jika tertarik, saya sarankan untuk memperhatikan riwayat commit, itu menyimpan memori cobaan optimasi dan upaya yang gagal untuk memeras beberapa frame rendering tambahan per detik.
Nah, dalam kompetisi saya tidak mengambil hadiah. Dan masalahnya, seperti yang sering terjadi pada kita programmer, ternyata bukan pengalaman yang tidak mencukupi, kecerdasan atau kecepatan, tetapi tidak cukup kritik-diri: fakta bahwa saya berhasil melakukannya bekerja dan terlihat seperti dalam gambar, saya senang, tetapi senang Adapun rem rendering, saya pikir saya melakukan semua yang saya bisa, sisanya mungkin melakukan hal yang sama. Saya malu membicarakan hal ini, tetapi saya yakin bahwa saya akan menempati posisi pertama atau kedua. Bahkan, ternyata saya menulis program pengereman dan kereta, bukan yang terburuk, tetapi jauh dari yang terbaik. Ketika saya melihat karya pengembang lain, saya menyadari bahwa saya tidak punya peluang dan hanya bisa menggigit siku saya. Jika saya tidak memihak pada pekerjaan saya, saya akan terlibat dalam produktivitas, bagian terpenting dari tugas kompetisi.
Salah satu pelajaran paling berharga dalam kehidupan profesional saya yang tidak membuat saya bosan adalah bahwa seorang insinyur yang baik, tidak seperti, misalnya, seorang seniman, wajib mengevaluasi secara objektif kualitas pekerjaannya, membuang kepercayaan diri, karena hasil karyanya tidak hanya menyenangkan mata. tetapi harus bekerja dengan benar dan baik.
Ini adalah tahap pertama kompetisi. Pemenang diberi hadiah dengan murah hati. Betapa sukacitaku yang tak terlukiskan, kisah itu tidak berakhir di sana, karena tahap kedua diumumkan:
Itu perlu untuk memperbaiki kerajinan Anda, hanya dalam seminggu menerapkan jenis grafik tambahan. Saya akan segera menunjukkan apa yang terjadi, dan di bawah ini saya akan memberi tahu Anda bagaimana itu terjadi.
Dalam kasus saya, sebelum menambahkan fungsionalitas baru, saya harus memahami kinerja yang lama. Masalah pertama yang saya pecahkan adalah
Animasi berkedutBahkan jika Anda memiliki kekuatan yang cukup untuk menghasilkan 60 frame per detik, animasi tidak akan mulus jika posisi elemen atau transparansi tidak ditentukan oleh waktu yang berlalu sejak awal animasi. Ini disebabkan oleh interval waktu yang tidak sama antara kutu: misalnya, satu kutu bekerja setelah 10 ms, dan kutu kedua setelah 40, sedangkan untuk kutu pertama dan kedua objek bergerak ke kiri sebesar 1 piksel - yaitu, kecepatan gerakannya terus mengambang, secara visual itu terlihat seperti "kedutan". Dengan kata lain, Anda perlu melakukan sesuatu yang salah:
let left = 10, interval = setInterval(() => { left += 1 if (left >= 90) { clearInterval(interval) } }, 10)
Jadi:
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) } })
Karena ada banyak parameter animasi dalam kode, saya memfilmkan
kelas universal yang membuat tugas lebih mudah, dan juga menambahkan ising ke animasi. Sangat mudah digunakan:
let left = Telemation.create(10, 90, 1000) … drawVerticalLine(left.value)
Kemudian aturan 60 fps mulai berlaku. Gamer PC akan mengerti saya: agar animasi terlihat sempurna, animasi harus dibuat dengan kecepatan setidaknya 60 fps. Dengan demikian, setiap rendering frame harus tidak lebih dari 1/60 detik. Ini membutuhkan perangkat keras yang kuat dan kode yang baik.
Penelitian lebih lanjut menunjukkan hal itu
Lukisan kanvas melambat jika ada elemen html di atas kanvas .
Awalnya, saya menggunakan elemen html "kosong" untuk menerapkan kontrol atas viewport saat ini:
Elemen-elemen ini ditempatkan di atas kanvas, dan meskipun fakta bahwa mereka tidak memiliki konten, mereka digunakan hanya untuk melacak peristiwa mouse, sebagai hasil percobaan ternyata keberadaan mereka mengurangi kinerja rendering. Dengan menghapusnya dan mempersulit logika menentukan peristiwa untuk mengontrol area tampilan sedikit, saya meningkatkan kecepatan rendering frame.
Tetap untuk menarik paku terakhir dari tutup peti mati kinerja: saya lakukan
Caching MinimapSebelum ini, untuk garis minimap ditarik setiap frame lagi. Ini adalah operasi yang mahal karena menampilkan seluruh jadwal untuk tahun ini (365 poin per baris). Solusi yang jelas, yang saya terlalu malas untuk implementasikan sejak awal, adalah menggambar garis-garis grafik untuk minimap satu kali, menyimpan hasilnya ke cache dan menggunakan cache ini di masa depan. Setelah optimasi ini, kinerja aplikasi tidak lagi memalukan.
Apa selanjutnya
Masih banyak yang berhasil dan tidak terlalu memperjuangkan kinerja: upaya untuk men-cache hasil perhitungan koordinat, percobaan dengan parameter lineJoin CanvasRenderingContext2D (mitra lebih cepat), tetapi mereka tidak begitu menarik, karena mereka tidak memberikan kenaikan kinerja yang nyata atau tidak memberikannya sama sekali.
Dari delapan hari, saya menghabiskan lima untuk mempercepat kode, dan hanya tiga untuk menyelesaikan fungsi baru. Ya, saya hanya perlu tiga hari untuk menambahkan jenis bagan baru, dan di sini OOP ternyata sangat berguna, dengan itu basis kode sedikit meningkat. Saya tidak punya cukup waktu untuk menyelesaikan tugas bonus (+5 grafik tambahan). Saya percaya bahwa lima hari yang saya habiskan untuk menghilangkan konsekuensi dari kepercayaan diri saya, saya bisa habiskan untuk memecahkan masalah bonus.
Namun demikian, pekerjaan saya menghasilkan hasilnya: tempat ke-4 dan hadiah "hiburan" seribu dolar:
Ngomong-ngomong, kompetisi berlanjut, tapi tanpa aku.
Saya senang dengan partisipasi: selain menjadi hanya menarik dan petualangan yang menarik, saya mendapat pengalaman profesional yang baik dan pelajaran hidup.
Selain itu, saya menggunakan perpustakaan ini dalam pengembangan timetracker perusahaan kami, yang juga akan saya bicarakan dalam waktu dekat.
Untuk diskusi, saya mengusulkan pertanyaan berikut: mengapa Telegram membutuhkan semua ini? Saya percaya bahwa untuk uang yang memadai, Telegram akan menerima perpustakaan terbaik di dunia untuk menampilkan grafik: hasil terbaik dari ratusan upaya untuk melakukan lebih baik daripada yang lain. Prinsip kompetitif memungkinkan Anda untuk mendapatkan tingkat kualitas yang tinggi sehingga tidak ada yang dapat melakukan pesanan dan tanpa uang.
Dan beberapa tautan: