Performa di .NET Core

Performa di .NET Core

Performa di .NET Core

Halo semua! Artikel ini adalah kumpulan Praktik Terbaik yang telah lama saya dan kolega saya gunakan saat mengerjakan berbagai proyek.

Informasi tentang mesin tempat penghitungan dilakukan:BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
CPU Intel Core i5-8250U 1.60GHz (Kaby Lake R), 1 CPU, 8 inti logis dan 4 inti fisik
.NET Inti SDK=3.0.100
[Host]: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64bit RyuJIT
Inti: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64bit RyuJIT
[Host]: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT
Inti: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT

Pekerjaan=Waktu Proses Inti=Inti

ToList vs ToArray dan Siklus


Saya berencana untuk menyiapkan informasi ini dengan rilis .NET Core 3.0, tetapi mereka mengalahkan saya, saya tidak ingin mencuri kejayaan orang lain dan menyalin informasi orang lain, jadi saya hanya akan menunjukkannya tautan ke artikel bagus yang perbandingannya dijelaskan secara rinci.

Atas nama saya sendiri, saya hanya ingin menyajikan kepada Anda pengukuran dan hasil saya; saya menambahkan loop terbalik ke dalamnya untuk pecinta loop penulisan "gaya C++".

Kode:

public class Bench
    {
        private List<int> _list;
        private int[] _array;

        [Params(100000, 10000000)] public int N;

        [GlobalSetup]
        public void Setup()
        {
            const int MIN = 1;
            const int MAX = 10;
            Random random = new Random();
            _list = Enumerable.Repeat(0, N).Select(i => random.Next(MIN, MAX)).ToList();
            _array = _list.ToArray();
        }

        [Benchmark]
        public int ForList()
        {
            int total = 0;
            for (int i = 0; i < _list.Count; i++)
            {
                total += _list[i];
            }

            return total;
        }
        
        [Benchmark]
        public int ForListFromEnd()
        {
            int total = 0;t
            for (int i = _list.Count-1; i > 0; i--)
            {
                total += _list[i];
            }

            return total;
        }

        [Benchmark]
        public int ForeachList()
        {
            int total = 0;
            foreach (int i in _list)
            {
                total += i;
            }

            return total;
        }

        [Benchmark]
        public int ForeachArray()
        {
            int total = 0;
            foreach (int i in _array)
            {
                total += i;
            }

            return total;
        }

        [Benchmark]
        public int ForArray()
        {
            int total = 0;
            for (int i = 0; i < _array.Length; i++)
            {
                total += _array[i];
            }

            return total;
        }
        
        [Benchmark]
        public int ForArrayFromEnd()
        {
            int total = 0;
            for (int i = _array.Length-1; i > 0; i--)
            {
                total += _array[i];
            }

            return total;
        }
    }

Kecepatan kinerja di .NET Core 2.2 dan 3.0 hampir sama. Inilah yang bisa saya dapatkan di .NET Core 3.0:

Performa di .NET Core

Performa di .NET Core

Kita dapat menyimpulkan bahwa pemrosesan berulang dari kumpulan Array lebih cepat karena optimalisasi internal dan alokasi ukuran koleksi yang eksplisit. Perlu juga diingat bahwa kumpulan Daftar memiliki kelebihannya sendiri dan Anda harus menggunakan koleksi yang tepat tergantung pada perhitungan yang diperlukan. Bahkan jika Anda menulis logika untuk bekerja dengan loop, jangan lupa bahwa ini adalah loop biasa dan juga tunduk pada kemungkinan optimasi loop. Sebuah artikel telah diterbitkan di habr beberapa waktu yang lalu: https://habr.com/ru/post/124910/. Masih relevan dan direkomendasikan untuk dibaca.

Melemparkan

Setahun yang lalu, saya bekerja di sebuah perusahaan pada proyek lama, dalam proyek itu normal untuk memproses validasi lapangan melalui konstruksi coba-tangkap-lempar. Saya sudah memahami bahwa ini adalah logika bisnis yang tidak sehat untuk proyek tersebut, jadi bila memungkinkan saya mencoba untuk tidak menggunakan desain seperti itu. Namun mari kita cari tahu mengapa pendekatan penanganan kesalahan dengan konstruksi seperti itu buruk. Saya menulis kode kecil untuk membandingkan kedua pendekatan dan membuat tolok ukur untuk setiap opsi.

Kode:

        public bool ContainsHash()
        {
            bool result = false;
            foreach (var file in _files)
            {
                var extension = Path.GetExtension(file);
                if (_hash.Contains(extension))
                    result = true;
            }

            return result;
        }

        public bool ContainsHashTryCatch()
        {
            bool result = false;
            try
            {
                foreach (var file in _files)
                {
                    var extension = Path.GetExtension(file);
                    if (_hash.Contains(extension))
                        result = true;
                }
                
                if(!result) 
                    throw new Exception("false");
            }
            catch (Exception e)
            {
                result = false;
            }

            return result;
        }

Hasil di .NET Core 3.0 dan Core 2.2 memiliki hasil yang serupa (.NET Core 3.0):

Performa di .NET Core

Performa di .NET Core

Try catch membuat kode lebih sulit untuk dipahami dan meningkatkan waktu eksekusi program Anda. Namun jika Anda memerlukan konstruksi ini, Anda sebaiknya tidak memasukkan baris kode yang tidak diharapkan dapat menangani kesalahan - ini akan membuat kode lebih mudah dipahami. Faktanya, bukan penanganan pengecualian yang memuat sistem, melainkan pelemparan kesalahan itu sendiri melalui konstruksi Pengecualian baru.

Melempar pengecualian lebih lambat daripada beberapa kelas yang akan mengumpulkan kesalahan dalam format yang diperlukan. Jika Anda sedang memproses formulir atau beberapa data dan Anda mengetahui dengan jelas kesalahan apa yang seharusnya terjadi, mengapa tidak memprosesnya?

Anda tidak boleh menulis konstruksi throw new Exception() jika situasi ini tidak luar biasa. Menangani dan melempar pengecualian sangat mahal!!!

ToLower, ToLowerInvariant, ToUpper, ToUpperInvariant

Selama 5 tahun pengalaman saya bekerja pada platform .NET, saya telah menemukan banyak proyek yang menggunakan pencocokan string. Saya juga melihat gambar berikut: ada satu solusi Perusahaan dengan banyak proyek, yang masing-masing melakukan perbandingan string secara berbeda. Namun apa yang harus digunakan dan bagaimana menyatukannya? Di buku CLR via C# karya Richter, saya membaca informasi bahwa metode ToUpperInvariant() lebih cepat daripada ToLowerInvariant().

Kutipan dari buku:

Performa di .NET Core

Tentu saja, saya tidak mempercayainya dan memutuskan untuk menjalankan beberapa tes pada .NET Framework dan hasilnya mengejutkan saya - peningkatan kinerja lebih dari 15%. Kemudian, saat tiba di tempat kerja keesokan paginya, saya menunjukkan pengukuran ini kepada atasan saya dan memberi mereka akses ke kode sumbernya. Setelah ini, 2 dari 14 proyek diubah untuk mengakomodasi pengukuran baru, dan mengingat kedua proyek ini ada untuk memproses tabel Excel yang besar, hasilnya lebih dari signifikan bagi produk.

Saya juga menyajikan kepada Anda pengukuran untuk berbagai versi .NET Core, sehingga Anda masing-masing dapat memilih solusi yang paling optimal. Dan saya hanya ingin menambahkan bahwa di perusahaan tempat saya bekerja, kami menggunakan ToUpper() untuk membandingkan string.

Kode:

public const string defaultString =  "VXTDuob5YhummuDq1PPXOHE4PbrRjYfBjcHdFs8UcKSAHOCGievbUItWhU3ovCmRALgdZUG1CB0sQ4iMj8Z1ZfkML2owvfkOKxBCoFUAN4VLd4I8ietmlsS5PtdQEn6zEgy1uCVZXiXuubd0xM5ONVZBqDu6nOVq1GQloEjeRN8jXrj0MVUexB9aIECs7caKGddpuut3";

        [Benchmark]
        public bool ToLower()
        {
            return defaultString.ToLower() == defaultString.ToLower();
        }

        [Benchmark]
        public bool ToLowerInvariant()
        {
            return defaultString.ToLowerInvariant() == defaultString.ToLowerInvariant();
        }

        [Benchmark]
        public bool ToUpper()
        {
            return defaultString.ToUpper() == defaultString.ToUpper();
        }

        [Benchmark]
        public bool ToUpperInvariant()
        {
            return defaultString.ToUpperInvariant() == defaultString.ToUpperInvariant();
        }

Performa di .NET Core

Performa di .NET Core

Di .NET Core 3.0, peningkatan untuk masing-masing metode ini adalah ~x2 dan menyeimbangkan implementasi satu sama lain.

Performa di .NET Core

Performa di .NET Core

Kompilasi Tingkat

Dalam artikel terakhir saya, saya menjelaskan fungsi ini secara singkat, saya ingin mengoreksi dan melengkapi kata-kata saya. Kompilasi multi-level mempercepat waktu startup solusi Anda, tetapi Anda mengorbankan bagian kode Anda untuk dikompilasi menjadi versi yang lebih optimal di latar belakang, yang dapat menimbulkan sedikit overhead. Dengan munculnya NET Core 3.0, waktu pembangunan untuk proyek dengan kompilasi tingkat yang diaktifkan telah berkurang dan bug yang terkait dengan teknologi ini telah diperbaiki. Sebelumnya, teknologi ini menyebabkan kesalahan pada permintaan pertama di ASP.NET Core dan macet selama build pertama dalam mode kompilasi multi-level. Saat ini diaktifkan secara default di .NET Core 3.0, tetapi Anda dapat menonaktifkannya jika diinginkan. Jika Anda berada di posisi pemimpin tim, senior, menengah, atau Anda adalah kepala departemen, maka Anda harus memahami bahwa pengembangan proyek yang cepat meningkatkan nilai tim dan teknologi ini akan memungkinkan Anda menghemat waktu bagi kedua pengembang. dan waktu proyek itu sendiri.

.NET naik level

Tingkatkan versi .NET Framework / .NET Core Anda. Seringkali, setiap versi baru memberikan peningkatan kinerja tambahan dan menambahkan fitur baru.

Tapi apa sebenarnya manfaatnya? Mari kita lihat beberapa di antaranya:

  • .NET Core 3.0 memperkenalkan gambar R2R yang akan mengurangi waktu startup aplikasi .NET Core.
  • Dengan versi 2.2, Kompilasi Tingkat muncul, sehingga pemrogram akan menghabiskan lebih sedikit waktu untuk meluncurkan proyek.
  • Dukungan untuk Standar .NET baru.
  • Dukungan untuk versi baru bahasa pemrograman.
  • Optimasi, dengan setiap versi baru, optimalisasi perpustakaan dasar Collection/Struct/Stream/String/Regex dan banyak lagi meningkat. Jika Anda bermigrasi dari .NET Framework ke .NET Core, Anda akan mendapatkan peningkatan kinerja yang besar. Sebagai contoh, saya lampirkan tautan ke beberapa optimasi yang ditambahkan ke .NET Core 3.0: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/

Performa di .NET Core

Kesimpulan

Saat menulis kode, ada baiknya memperhatikan berbagai aspek proyek Anda dan menggunakan fitur bahasa pemrograman dan platform Anda untuk mencapai hasil terbaik. Saya akan senang jika Anda berbagi pengetahuan Anda terkait optimasi di .NET.

Tautan ke github

Sumber: www.habr.com

Tambah komentar