ว่ากันว่าบราเดอร์ Jiang Min ได้ทำการทดสอบประสิทธิภาพอย่างเข้มงวดเพียงพอกับวิธี Array.Sort และ Enumerable.OrderBy เมื่อไม่นานนี้ ข้อสรุปดูเหมือนจะไม่สอดคล้องกับทฤษฎีและความคาดหวัง แต่ข้อสรุปนี้วัดกันในสภาพแวดล้อมที่ค่อนข้างเข้มงวด ซึ่งกระตุ้นความสนใจของผู้เชี่ยวชาญด้วย ฉันยังทดสอบมันบนเครื่องของฉันด้วย แน่นอนว่าฉันได้แยกโค้ดที่ไม่เกี่ยวข้องออกก่อน:
ใช้ระบบ;
ใช้ System.Collections.Generic;
โดยใช้ระบบการวินิจฉัย
ใช้ System.Linq;
โดยใช้ System.Runtime.InteropServices;
โดยใช้ System.Threading;
ข้อสอบเนมสเปซ11
-
CodeTimer คลาสคงที่สาธารณะ
-
โมฆะคงสาธารณะเริ่มต้น ()
-
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
Thread.CurrentThread.Priority = ThreadPriority.Highest;
เวลา( "", 1, () => { } );
-
เวลาโมฆะคงสาธารณะ (ชื่อสตริง, การวนซ้ำ int, การดำเนินการการกระทำ)
-
ถ้า ( String.IsNullOrEmpty( name ) ) กลับมา;
//อุ่นเครื่อง
การกระทำ();
// 1.
ConsoleColor currentForeColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine (ชื่อ);
// 2.
GC.Collect( GC.MaxGeneration, GCCollectionMode.Forced );
int[] gcCounts = int ใหม่[GC.MaxGeneration + 1];
สำหรับ (int i = 0; i <= GC.MaxGeneration; i++)
-
gcCounts[i] = GC.CollectionCount(i);
-
// 3.
นาฬิกาจับเวลา = นาฬิกาจับเวลาใหม่ ();
ดู.เริ่ม();
รอบ CycleCount = GetCycleCount();
สำหรับ (int i = 0; i < iteration; i++ ) action();
ยาว cpuCycles = GetCycleCount() - รอบนับ;
นาฬิกาหยุด ();
// 4.
Console.ForegroundColor = ปัจจุบันForeColor;
Console.WriteLine( "tTime Elapsed:t" + watch.ElapsedMilliseconds.ToString( "N0" ) + "ms" );
Console.WriteLine( "tCPU รอบ:t" + cpuCycles.ToString( "N0" ) );
// 5.
สำหรับ (int i = 0; i <= GC.MaxGeneration; i++)
-
จำนวน int = GC.CollectionCount(i) - gcCounts[i];
Console.WriteLine( "tGen " + i + ": tt" + นับ );
-
คอนโซล.WriteLine();
-
ส่วนตัวคงที่ GetCycleCount()
-
รอบการนับ = 0;
QueryThreadCycleTime( GetCurrentThread(), อ้างอิง cycleCount );
รอบการนับกลับ;
-
[DllImport( "kernel32.dll" )]
[กลับมา: MarshalAs (UnmanagedType.Bool)]
บูลภายนอกแบบคงที่ QueryThreadCycleTime (IntPtr threadHandle, อ้างอิง CycleTime ที่ยาวนาน);
[DllImport( "kernel32.dll" )]
ภายนอกคงที่ IntPtr GetCurrentThread();
-
โปรแกรมชั้นเรียน
-
โมฆะคงที่หลัก (สตริง [] args)
-
var Random = สุ่มใหม่ (DateTime.Now.Millisecond);
var array = Enumerable.Repeat( 0, 50000 ).Select( _ => บุคคลใหม่ { ID = Random.Next() } ).ToArray();
//โปรแกรมลาวจ้าว
CodeTimer.Initialize();
CodeTimer.Time( "SortWithCustomComparer", 500, () => SortWithCustomComparer( CloneArray( array ) ) );
CodeTimer.Time( "SortWithLinq", 500, () => SortWithLinq( CloneArray( array ) ) );
Console.ReadLine();
-
โมฆะคงที่ส่วนตัว SortWithCustomComparer (อาร์เรย์บุคคล [])
-
Array.Sort( อาร์เรย์ PersonComparer ใหม่ () );
-
โมฆะคงที่ส่วนตัว SortWithLinq (อาร์เรย์บุคคล [])
-
var เรียงลำดับ =
(จากบุคคลในอาเรย์.
สั่งซื้อโดย person.ID
เลือกบุคคล ).ToList();
-
ส่วนตัวคงที่ T[] CloneArray<T>( T[] แหล่งที่มา)
-
var dest = ใหม่ T [source.Length];
Array.Copy( แหล่งที่มา ปลายทาง แหล่งที่มา ความยาว );
ปลายทางขากลับ;
-
-
บุคคลในชั้นเรียนสาธารณะ
-
ชื่อสตริงสาธารณะ
-
รับ;
ชุด;
-
นามสกุลสตริงสาธารณะ
-
รับ;
ชุด;
-
ID สาธารณะ
-
รับ;
ชุด;
-
-
PersonComparer คลาสสาธารณะ : IComparer<Person>
-
เปรียบเทียบ int สาธารณะ (บุคคล x, บุคคล y)
-
ส่งคืน x.ID - y.ID;
-
-
-
ผลการทดสอบแสดงให้เห็นข้อดีที่ชัดเจนของ Enumerable.OrderBy แม้ในโหมดการรวบรวม Release
โดยหลักการแล้ว สิ่งนี้เป็นไปไม่ได้ ดังนั้นจะต้องมีการแข่งขันที่ไม่ยุติธรรมที่ไหนสักแห่ง
ฉันกลับมาดูโค้ดทั้งหมดอีกครั้งและปรับตำแหน่งของโค้ดทดสอบทั้งสอง:
CodeTimer.Time( "SortWithLinq", 100, () => SortWithLinq( CloneArray( array ) ) );
CodeTimer.Time( "SortWithCustomComparer", 100, () => SortWithCustomComparer( CloneArray( array ) ) );
แน่นอนว่าสิ่งนี้จะไม่มีผลใดๆ ประการที่สอง ฉันพบว่าอินสแตนซ์เปรียบเทียบนี้ถูกสร้างขึ้นหลายครั้ง (จริงๆ แล้วมีเพียง 100 ครั้งเท่านั้น) ดังนั้นฉันจึงปรับให้เหมาะสมดังนี้:
ตัวเปรียบเทียบ PersonComparer แบบคงที่ส่วนตัว = PersonComparer ใหม่ ();
โมฆะคงที่ส่วนตัว SortWithCustomComparer (อาร์เรย์บุคคล [])
-
Array.Sort (อาร์เรย์, ตัวเปรียบเทียบ);
-
ไม่มีการปรับปรุงในผลลัพธ์
ดังนั้นฉันจึงสามารถมุ่งเน้นไปที่การใช้งานตัวเปรียบเทียบเท่านั้น:
PersonComparer คลาสสาธารณะ : IComparer<Person>
-
เปรียบเทียบ int สาธารณะ (บุคคล x, บุคคล y)
-
ส่งคืน x.ID - y.ID;
-
-
นี่ไม่ใช่แนวทางที่ยุติธรรม แต่ไม่ควรทำให้เกิดปัญหาด้านประสิทธิภาพ เปลี่ยนเป็นแบบฟอร์มต่อไปนี้:
เปรียบเทียบ int สาธารณะ (บุคคล x, บุคคล y)
-
กลับ x.ID.CompareTo( y.ID );
-
ประสิทธิภาพยังไม่มีการปรับปรุง แต่ลดลงเล็กน้อย (ซึ่งเป็นเรื่องปกติ)
ดูเหมือนว่าเรากำลังอยู่ในทางตันใช่ไหม? จริงๆแล้วเราเข้าใกล้ความจริงมากแล้ว จริง ๆ แล้วชื่อเรื่องได้ประกาศคำตอบแล้ว เนื่องจากปัญหาอยู่ที่ person.ID ที่เป็นแอตทริบิวต์ จริงๆ แล้ววิธีนี้จึงถูกเรียกสามครั้งที่นี่
ดังนั้นเพียงเปลี่ยน ID เป็นฟิลด์ และผลลัพธ์ประสิทธิภาพจะใกล้เคียงกับที่คาดไว้
แล้วทำไม LINQ ถึงได้เปรียบที่นี่? อา ถ้าคุณเข้าใจหลักการเรียงลำดับของ LINQ ก็ไม่ยากเลยที่จะคิดว่า LINQ สามารถบันทึกการโทรจำนวนมากไปยัง Person.get_ID ได้ ฉันเชื่อว่าการวิเคราะห์นี้จะมีการอธิบายอย่างละเอียดในบทความถัดไปของ Daniel ดังนั้นฉันจะไม่อวดที่นี่
ดังนั้นจึงสามารถสรุปได้ แต่ก็ยังน่าแปลกใจเล็กน้อย LINQ มีข้อได้เปรียบด้านประสิทธิภาพในบางสภาพแวดล้อม แต่สิ่งที่ฉันอยากจะพูดคือจริงๆ แล้วมีหลายปัจจัยที่จะส่งผลต่อข้อสรุปนี้ เราไม่สามารถพูดโดยทั่วไปได้ว่า LINQ จะต้องมีข้อได้เปรียบด้านประสิทธิภาพ หรือ Array.Sort จะต้องมีข้อได้เปรียบด้านประสิทธิภาพ