Dikatakan bahwa Saudara Jiang Min melakukan tes kinerja yang cukup ketat pada metode Array.Sort dan Enumerable.OrderBy beberapa waktu lalu. Kesimpulan tersebut tampaknya tidak sesuai dengan teori dan ekspektasi, namun kesimpulan ini diukur dalam lingkungan yang relatif ketat, yang juga membangkitkan minat para ahli. Saya juga mengujinya di mesin saya, tentu saja, saya memilah beberapa kode yang tidak relevan terlebih dahulu:
menggunakan Sistem;
menggunakan System.Collections.Generik;
menggunakan Sistem.Diagnostik;
menggunakan System.Linq;
menggunakan System.Runtime.InteropServices;
menggunakan Sistem.Threading;
ujian namespace11
{
CodeTimer kelas statis publik
{
Inisialisasi kekosongan statis publik()
{
Proses.GetCurrentProcess().PriorityClass = ProcessPriorityClass.Tinggi;
Thread.CurrentThread.Priority = ThreadPriority.Tertinggi;
Waktu( "", 1, () => { } );
}
Waktu kekosongan statis publik (nama string, iterasi int, Tindakan tindakan)
{
jika ( String.IsNullOrEmpty( nama ) ) kembali;
// pemanasan
tindakan();
// 1.
ConsoleColor currentForeColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Kuning;
Console.WriteLine( nama );
// 2.
GC.Collect( GC.MaxGeneration, GCCollectionMode.Forced );
int[] gcCounts = int baru[GC.MaxGeneration + 1];
untuk ( int i = 0; i <= GC.MaxGeneration; i++ )
{
gcCounts[i] = GC.CollectionCount(i);
}
// 3.
Jam tangan stopwatch = Stopwatch baru();
tonton.Mulai();
ulong cycleCount = GetCycleCount();
for ( int i = 0; i < iterasi; i++ ) action();
ulong cpuCycles = GetCycleCount() - cycleCount;
tonton.Stop();
// 4.
Console.ForegroundColor = currentForeColor;
Console.WriteLine( "tWaktu Berlalu:t" + jam tangan.ElapsedMilliseconds.ToString( "N0" ) + "ms" );
Console.WriteLine( "tCPU Cycles:t" + cpuCycles.ToString( "N0" ) );
// 5.
untuk ( int i = 0; i <= GC.MaxGeneration; i++ )
{
int hitungan = GC.CollectionCount(i) - gcCounts[i];
Console.WriteLine( "tGen " + i + ": tt" + hitungan );
}
Konsol.WriteLine();
}
ulong statis pribadi GetCycleCount()
{
ulong cycleCount = 0;
QueryThreadCycleTime( GetCurrentThread(), ref cycleCount );
kembali cycleCount;
}
[Impor Dll( "kernel32.dll" )]
[kembali: MarshalAs(UnmanagedType.Bool)]
bool eksternal statis QueryThreadCycleTime(IntPtr threadHandle, ref ulong cycleTime);
[Impor Dll( "kernel32.dll" )]
statis eksternal IntPtr GetCurrentThread();
}
Program kelas
{
kekosongan statis Utama( string[] args )
{
var random = new Random(DateTime.Now.Millisecond);
var array = Enumerable.Repeat( 0, 50000 ).Pilih( _ => Orang baru { ID = acak.Berikutnya() } ).ToArray();
//Program Lao Zhao
CodeTimer.Inisialisasi();
CodeTimer.Time( "SortWithCustomComparer", 500, () => SortWithCustomComparer( CloneArray( array ) ) );
CodeTimer.Waktu( "SortWithLinq", 500, () => SortWithLinq( CloneArray( array ) ) );
Konsol.ReadLine();
}
kekosongan statis pribadi SortWithCustomComparer( Array Orang[] )
{
Array.Sort( array, PersonComparer baru() );
}
kekosongan statis pribadi SortWithLinq( Array Orang[] )
{
var diurutkan =
(dari orang dalam array
dipesan berdasarkan orang.ID
pilih orang ).ToList();
}
statis pribadi T[] CloneArray<T>( T[] sumber )
{
var dest = T baru[sumber.Panjang];
Array.Copy( sumber, tujuan, sumber.Panjang );
tujuan kembali;
}
}
Orang kelas publik
{
string publik Nama Depan
{
mendapatkan;
mengatur;
}
string publik Nama Belakang
{
mendapatkan;
mengatur;
}
ID int publik
{
mendapatkan;
mengatur;
}
}
PersonComparer kelas publik : IComparer<Person>
{
public int Bandingkan (Orang x, Orang y)
{
kembalikan x.ID - y.ID;
}
}
}
Hasil pengujian memang menunjukkan keuntungan nyata dari Enumerable.OrderBy, bahkan dalam mode kompilasi Rilis.
Pada prinsipnya hal ini tidak mungkin, jadi pasti ada persaingan tidak sehat di suatu tempat.
Saya meninjau kembali seluruh kode dan menyesuaikan posisi kedua kode pengujian:
CodeTimer.Waktu( "SortWithLinq", 100, () => SortWithLinq( CloneArray( array ) ) );
CodeTimer.Time( "SortWithCustomComparer", 100, () => SortWithCustomComparer( CloneArray( array ) ) );
Tentu saja hal ini tidak akan berpengaruh apa pun. Kedua, saya menemukan bahwa instance pembanding ini dibuat beberapa kali (sebenarnya hanya 100 kali), jadi saya mengoptimalkannya sebagai berikut:
pembanding PersonComparer statis pribadi = PersonComparer baru();
kekosongan statis pribadi SortWithCustomComparer( Array Orang[] )
{
Array.Sort(array, pembanding);
}
Tidak ada perbaikan pada hasilnya.
Jadi saya hanya bisa fokus pada implementasi pembanding:
PersonComparer kelas publik : IComparer<Person>
{
public int Bandingkan (Orang x, Orang y)
{
kembalikan x.ID - y.ID;
}
}
Ini bukan pendekatan yang adil, namun seharusnya tidak menimbulkan masalah kinerja. Ubahlah ke bentuk berikut:
public int Bandingkan (Orang x, Orang y)
{
kembalikan x.ID.CompareTo( y.ID );
}
Performanya masih belum ada peningkatan, hanya ada sedikit penurunan (hal ini wajar).
Sepertinya kita menemui jalan buntu? Faktanya, kami sangat dekat dengan kebenaran. Faktanya, judulnya sudah mengumumkan jawabannya. Karena masalahnya terletak pada person.ID sebagai atribut, metode ini sebenarnya dipanggil tiga kali di sini.
Jadi cukup ubah ID ke suatu bidang, dan hasil kinerja akan mendekati yang diharapkan.
Jadi mengapa LINQ mempunyai keunggulan di sini? Ah, jika Anda memahami prinsip pengurutan LINQ, tidak sulit membayangkan LINQ bisa menyimpan banyak panggilan ke Person.get_ID. Saya yakin analisa ini akan dijabarkan di artikel Daniel berikutnya, jadi saya tidak akan pamer di sini.
Jadi kesimpulannya bisa diambil, tapi masih sedikit mengejutkan. LINQ memang memiliki keunggulan kinerja di lingkungan tertentu. Namun yang ingin saya katakan adalah sebenarnya ada banyak faktor yang akan mempengaruhi kesimpulan ini. Kami tidak dapat mengatakan secara umum bahwa LINQ harus memiliki keunggulan kinerja atau Array.Sort harus memiliki keunggulan kinerja.