Halo, Habr! Saya mempersembahkan kepada Anda terjemahan artikel
β5 Alasan Anda Harus Berhenti Menggunakan System.Drawing from ASP.NETβ .

Yah, mereka berhasil. Tim corefx akhirnya menyetujui
berbagai permintaan dan termasuk System.Drawing dalam .NET Core.
(artikel asli bertanggal Juli 2017)
Paket
System.Drawing.Common yang akan
datang akan berisi sebagian besar fungsionalitas System.Drawing System dari .NET Framework dan dimaksudkan untuk digunakan sebagai opsi kompatibilitas bagi mereka yang ingin bermigrasi ke .NET Core tetapi tidak dapat melakukan ini karena ketergantungan. Dari perspektif ini, Microsoft melakukan hal yang benar. Gesekan perlu dikurangi karena adopsi .Net Core adalah tujuan yang lebih berharga.
Di sisi lain, System.Drawing adalah salah satu area termiskin dan paling miskin dari .Net Framework dan banyak dari kita berharap bahwa implementasi .NET Core akan berarti lambatnya kematian System.Drawing. Dan seiring dengan kematian ini harus menjadi kesempatan untuk melakukan sesuatu yang lebih baik.
Misalnya, tim Mono membuat pembungkus yang kompatibel dengan .NET untuk pustaka grafis
Skia lintas platform Google, yang disebut
SkiaSharp . Untuk mempermudah instalasi, Nuget telah banyak mendukung perpustakaan asli untuk setiap platform. Skia adalah fitur lengkap dan kinerjanya melakukan System.Drawing.
Tim
ImageSharp juga telah melakukan pekerjaan luar biasa dengan mengulangi sebagian besar fungsionalitas System.Drawing, tetapi dengan API terbaik dan implementasi 100% C #. Mereka masih belum siap untuk eksploitasi produktif, tetapi tampaknya mereka sudah cukup dekat dengan ini. Peringatan kecil tentang perpustakaan ini, karena kita berbicara tentang penggunaan dalam aplikasi server: sekarang, dalam konfigurasi default, Paralel. Untuk digunakan di dalam untuk mempercepat beberapa operasi, yang berarti bahwa lebih banyak alur kerja dari kumpulan ASP.NET akan digunakan, akhirnya Akibatnya,
mengurangi throughput keseluruhan aplikasi . Saya berharap perilaku ini akan ditinjau sebelum rilis, tetapi bahkan sekarang sudah cukup untuk mengubah satu baris konfigurasi untuk membuatnya lebih cocok untuk digunakan di server.
Dalam kasus apa pun, jika Anda menggambar, merencanakan, atau merender teks menjadi gambar dalam aplikasi di server, Anda harus serius mempertimbangkan mengubah System.Drawing apa pun, terlepas dari apakah Anda beralih ke .NET Core atau tidak.
Untuk bagian saya, saya telah merakit pipa pemrosesan gambar berkinerja tinggi untuk .NET dan .NET Core, yang memberikan kualitas gambar yang tidak dapat disediakan oleh System.Drawing, dan melakukannya dalam arsitektur yang sangat skalabel yang dirancang khusus untuk digunakan pada server. Sejauh ini, ini hanya untuk Windows, namun, cross-platform ada dalam rencana. Jika Anda menggunakan System.Drawing (atau yang lain) untuk mengubah ukuran gambar di server, yang terbaik adalah mempertimbangkan
MagicScaler sebagai pengganti.
Tetapi sebuah System.Drawing kebangkitan, yang membuat transisi lebih mudah bagi beberapa pengembang, kemungkinan akan membunuh sebagian besar momentum yang diterima proyek-proyek ini, karena pengembang dipaksa untuk mencari alternatif. Sayangnya dalam ekosistem .NET, perpustakaan dan paket Microsoft akan selalu menang, tidak peduli seberapa unggul alternatifnya.
Posting ini merupakan upaya untuk memperbaiki beberapa kesalahan perhitungan System.Drawing dengan harapan bahwa pengembang akan mengeksplorasi alternatif bahkan jika System.Drawing tetap menjadi pilihan.
Saya akan mulai dengan
disclaimer yang sering dikutip
dari dokumentasi System.Drawing. Penolakan ini telah dimunculkan beberapa kali dalam
diskusi tentang Github ketika membahas System.Drawing.Common .
"Kelas dengan System. Menggambar namespace tidak didukung untuk digunakan di Windows atau layanan ASP.NET. Mencoba menggunakan kelas-kelas ini dengan jenis aplikasi ini dapat menyebabkan masalah yang tidak terduga, seperti penurunan kinerja server dan kesalahan runtime. "
Seperti banyak dari Anda, saya membaca disclaimer ini sejak lama, dan kemudian saya melewatkannya dan masih menggunakan System.Drawing dalam aplikasi ASP.NET saya. Mengapa Karena aku suka bahaya. Entah itu, atau tidak ada opsi lain yang ditemukan. Dan tahukah Anda? Tidak ada hal buruk yang terjadi. Kemungkinan besar saya seharusnya tidak mengatakan ini, tetapi saya bertaruh bahwa banyak dari Anda telah mengalami hal yang sama. Jadi mengapa tidak terus menggunakan System.Drawing atau pustaka berdasarkan itu?
Alasan # 1: Penjelas GDI
Jika Anda pernah mengalami masalah menggunakan System.Drawing di server, ini kemungkinan besar terjadi. Jika tidak diuji, maka ini adalah salah satu penyebab yang paling mungkin.
System.Drawing untuk sebagian besar adalah pembungkus tipis di sekitar Windows GDI + API. Banyak objek System.Drawing didukung oleh
deskriptor GDI , dan mereka memiliki batas kuantitatif pada sesi prosesor dan pengguna. Jika ambang ini tercapai, Anda akan mendapatkan pengecualian "Kehabisan memori" dan / atau kesalahan 'generik' GDI +.
Masalahnya adalah bahwa dalam. NET, pengumpulan sampah dan penghentian proses dapat menunda pelepasan deskriptor ini pada saat Anda mencapai batas, bahkan di bawah beban ringan. Jika Anda lupa (atau tidak tahu apa yang Anda butuhkan) untuk memanggil Buang () pada objek yang berisi deskriptor seperti itu, Anda sangat berisiko menemukan kesalahan seperti itu di lingkungan Anda. Dan seperti kebanyakan bug terkait dengan keterbatasan sumber daya atau kebocoran, kemungkinan besar situasi ini akan berhasil diuji dan menyengat Anda dalam operasi yang produktif. Tentu, ini akan terjadi ketika aplikasi Anda berada di bawah beban terbesar, sehingga jumlah maksimum pengguna tahu tentang rasa malu Anda.
Pembatasan prosesor dan sesi pengguna bergantung pada versi sistem operasi, dan batas prosesor disesuaikan. Tetapi versi tidak masalah, karena Deskriptor GDI diwakili secara internal oleh tipe data USHORT, sehingga ada batasan ketat 65.536 deskriptor per sesi pengguna, dan bahkan aplikasi yang ditulis dengan baik berisiko mencapai batas ini di bawah beban yang cukup. Ketika Anda percaya bahwa server yang lebih kuat akan memungkinkan Anda untuk melayani lebih banyak dan lebih banyak pengguna secara paralel pada satu contoh, risiko ini menjadi lebih nyata. Dan sungguh, siapa yang ingin membuat perangkat lunak dengan batas skalabilitas yang terkenal?
Alasan # 2: Konkurensi
GDI + selalu memiliki masalah dengan konkurensi, meskipun banyak dari mereka terkait dengan
perubahan arsitektur di Windows7 / Windows Server 2008 R2 , Anda masih melihat beberapa di antaranya dalam versi baru. Yang paling penting adalah
penguncian proses yang diatur oleh GDI + selama operasi DrawImage (). Jika Anda mengubah ukuran gambar di server menggunakan System.Drawing (atau perpustakaan yang membungkusnya), metode DrawImage () mungkin merupakan dasar dari kode ini.
Terlebih lagi, ketika melakukan beberapa panggilan ke DrawImage () secara bersamaan,
semuanya akan diblokir sampai
semuanya dijalankan. Sekalipun waktu respons bukan masalah bagi Anda (mengapa tidak? Apakah Anda membenci pengguna Anda?) Perlu diingat bahwa sumber daya memori apa pun yang terkait dengan permintaan ini dan semua deskriptor GDI yang dipegang oleh objek yang terkait dengan permintaan ini terkait dengan runtime. Bahkan, tidak butuh terlalu banyak memuat di server untuk mulai menyebabkan masalah.
Tentu saja, ada solusi untuk masalah khusus ini. Misalnya, beberapa pengembang membuat proses eksternal untuk setiap operasi DrawImage (). Namun pada kenyataannya, solusi semacam itu hanya menambah kerapuhan ekstra, yang seharusnya tidak Anda lakukan.
Alasan # 3: Memori
Pertimbangkan penangan ASP.NET yang menghasilkan bagan. Dia harus melakukan sesuatu seperti ini:
- Buat bitmap sebagai kanvas
- Gambarlah beberapa bentuk pada bitmap menggunakan pena dan / atau kuas
- Gambarlah teks menggunakan satu atau lebih font
- Simpan bitmap sebagai PNG ke MemoryStream
Katakanlah bagan itu mengukur 600 dengan 400 poin. Ini adalah total 240.000 titik, dikalikan 4 byte untuk sebuah titik untuk format RGBA default, total 960.000 byte untuk bitmap, ditambah sedikit untuk menggambar objek dan buffer penyimpanan. Biarkan 1MB untuk seluruh permintaan. Kemungkinan besar Anda tidak akan mendapatkan masalah memori untuk skenario seperti itu, dan jika Anda mengalami sesuatu, maka kemungkinan besar Anda akan memiliki batasan pada jumlah deskriptor, yang saya sebutkan sebelumnya, karena gambar, kuas, pena, dan font memiliki deskriptor sendiri.
Masalah sebenarnya datang ketika System.Drawing digunakan untuk tugas-tugas pencitraan. System.Drawing terutama merupakan pustaka grafis, dan pustaka grafis biasanya dibangun dengan gagasan bahwa semuanya adalah bitmap dalam memori. Ini bagus ketika Anda memikirkan hal-hal kecil. Tetapi gambar bisa sangat besar, dan mereka semakin besar setiap hari, karena kamera dengan banyak megapiksel terus-menerus semakin murah.
Jika Anda mengambil pendekatan naif System.Drawing untuk membangun gambar, maka Anda mendapatkan sesuatu seperti ini untuk penangan ukuran:
- Buat bitmap sebagai kanvas untuk gambar tujuan.
- Muat gambar asli ke dalam bitmap lain.
- Panggil DrawImage () dengan parameter "sumber gambar" untuk gambar tujuan, menggunakan pengubahan ukuran.
- Simpan bitmap target dalam format JPEG ke aliran memori.
Misalkan gambar target akan menjadi 600x400, seperti pada contoh sebelumnya, sekali lagi kita memiliki 1MB untuk
gambar target dan aliran memori. Tetapi mari kita asumsikan bahwa seseorang mengunggah gambar 24-megapiksel dari DSLR baru mereka yang mewah, maka kita membutuhkan 6000x4000 piksel dengan masing-masing 3 byte untuk masing-masing (72 MB) untuk bitmap sumber yang diterjemahkan dalam format RGB. Dan kita akan menggunakan resampling HighQualityBicubic dari System.Drawing, karena itu hanya terlihat bagus. Maka kita perlu memperhitungkan 6000x4000 poin lainnya dengan masing-masing 4 byte, untuk
konversi PRGBA yang terjadi di dalam metode yang disebut , menambahkan 96mb tambahan memori yang digunakan. Secara total, 169mb (!) Diperoleh untuk permintaan untuk mengonversi satu gambar.
Sekarang bayangkan Anda memiliki lebih dari satu pengguna melakukan hal-hal seperti itu. Sekarang ingat bahwa permintaan diblokir sampai semuanya dieksekusi sepenuhnya. Berapa lama untuk kehabisan memori? Dan bahkan jika Anda tidak khawatir bahwa Anda akan sepenuhnya menghabiskan semua yang tersedia, ingatlah bahwa ada banyak cara untuk lebih baik menggunakan memori server Anda daripada menahan banyak piksel. Pertimbangkan efek tekanan memori pada bagian lain dari aplikasi / sistem:
- Cache ASP.NET Dapat Mulai Membilas Item Yang Mahal Untuk Diciptakan
- Pengumpul sampah akan mulai lebih sering, memperlambat aplikasi
- Cache kernel IIS atau cache sistem file Windows dapat menghapus elemen yang berguna
- Kumpulan aplikasi dapat melebihi batas memori dan dapat dimulai kembali
- Windows dapat mulai menukar memori ke disk, memperlambat seluruh sistem
Anda benar-benar tidak menginginkan semua ini?
Perpustakaan yang dirancang khusus untuk tugas pemrosesan gambar mendekati masalah ini dengan cara yang sangat berbeda. Mereka tidak perlu memuat seluruh sumber atau gambar target ke dalam memori. Jika Anda tidak akan menggambar di atasnya, Anda tidak perlu kanvas / bitmap. Ini dilakukan lebih seperti ini:
- Buat streaming untuk encoder JPEG dari gambar target
- Muat satu baris dari gambar asli dan kompres secara horizontal
- Ulangi sebanyak yang diperlukan untuk membentuk satu baris untuk file target
- Kompres garis yang dihasilkan secara vertikal
- Ulangi dari langkah 2 hingga semua baris file sumber diproses.
Dengan menggunakan metode ini, gambar yang sama dapat diproses menggunakan total memori 1MB, dan bahkan gambar yang jauh lebih besar akan membutuhkan sedikit peningkatan overhead.
Saya tahu hanya satu. NET perpustakaan yang dioptimalkan oleh prinsip ini, dan saya akan memberi Anda petunjuk: ini bukan System.Drawing.
Alasan # 4: CPU
Efek samping lain dari System.Drawing menjadi lebih berorientasi grafis daripada berorientasi gambar adalah bahwa DrawImage () cukup tidak efisien dalam hal penggunaan CPU. Saya membahas hal ini secara terperinci dalam
posting sebelumnya , tetapi diskusi ini dapat diringkas oleh fakta-fakta berikut:
- Di System.Drawing, konversi skala HighQualityBicubic hanya berfungsi dengan format PRGBA. Di hampir semua skenario, ini berarti salinan tambahan dari gambar. Tidak hanya menggunakan lebih banyak memori tambahan, tetapi juga membakar siklus prosesor untuk mengkonversi dan memproses saluran alfa tambahan.
- Bahkan setelah gambar dalam format aslinya, konversi skala HighQualityBicubic melakukan sekitar 4 kali lebih banyak perhitungan daripada yang diperlukan untuk mendapatkan hasil konversi yang benar.
Fakta-fakta ini menambahkan sejumlah besar siklus CPU yang terbuang. Dalam lingkungan berawan dengan pembayaran per menit, ini secara langsung berkontribusi pada biaya hosting. Dan tentu saja, waktu respons Anda akan berkurang.
Dan pikirkan fakta bahwa tambahan listrik akan dihabiskan dan panas dihasilkan. Penggunaan Anda atas System.Drawing untuk tugas pemrosesan gambar secara langsung memengaruhi pemanasan global. Kamu adalah monster.
Alasan # 5: Pemrosesan gambar tampak rumit
Selain kinerja, System. Menggambar dengan berbagai cara mencegah gambar diproses dengan benar. Menggunakan System.Drawing berarti hidup dengan output yang salah, atau mempelajari segala sesuatu tentang profil ICC, kuantisasi warna, orientasi exif, koreksi, dan banyak hal spesifik lainnya. Ini adalah lubang kelinci, yang sebagian besar pengembang tidak punya waktu atau keinginan untuk mengeksplorasi.
Perpustakaan seperti ImageResizer dan ImageProcessor telah mendapatkan banyak penggemar, mengurus beberapa detail ini, tetapi hati-hati, mereka memiliki System.Drawing di dalam, dan mereka datang bersama dengan semua bagasi yang saya jelaskan secara rinci dalam artikel ini.
Alasan bonus: Anda bisa melakukan yang lebih baik
Jika Anda, seperti saya, harus mengenakan kacamata pada suatu saat dalam hidup Anda, Anda mungkin ingat bagaimana ini adalah pertama kalinya Anda memakainya. Saya pikir saya melihat secara normal, dan jika saya menyipit dengan benar, maka semuanya akan menjadi cukup jelas. Tapi kemudian saya memakai kacamata ini, dan dunia menjadi jauh lebih rinci daripada yang bisa saya bayangkan.
Sistem. Gambarnya hampir sama. Ini melakukan hal yang benar jika Anda
mengisi pengaturan dengan benar , tetapi Anda akan terkejut betapa jauh lebih baik gambar Anda dapat terlihat jika Anda menggunakan utilitas terbaik.
Saya hanya akan meninggalkan ini di sini sebagai contoh. Ini adalah System.Drawing terbaik yang bisa dilakukan dibandingkan dengan pengaturan MagicScaler default. Mungkin aplikasi Anda akan mendapat manfaat dari mendapatkan poin ...
Gdi:

MagicScaler:
Foto oleh Jakob OwensLihatlah sekeliling, jelajahi alternatif, dan tolong, atas nama cinta untuk anak kucing, berhenti menggunakan System.Drawing dalam ASP.NET