Es heißt, dass Bruder Jiang Min vor einiger Zeit einen ausreichend strengen Leistungstest für die Methoden Array.Sort und Enumerable.OrderBy durchgeführt hat. Die Schlussfolgerung scheint nicht mit der Theorie und den Erwartungen übereinzustimmen, aber diese Schlussfolgerung wurde in einem relativ strengen Umfeld gemessen, was auch das Interesse von Experten weckte. Ich habe es auch auf meinem Rechner getestet, natürlich habe ich zuerst einige irrelevante Codes aussortiert:
Verwenden des Systems;
mit System.Collections.Generic;
Verwenden von System.Diagnostics;
mit System.Linq;
Verwenden von System.Runtime.InteropServices;
Verwenden von System.Threading;
Namespace-Prüfung11
{
öffentliche statische Klasse CodeTimer
{
öffentliches statisches void Initialize()
{
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
Thread.CurrentThread.Priority = ThreadPriority.Highest;
Zeit( "", 1, () => { } );
}
public static void Time( string name, int iteration, Action action )
{
if ( String.IsNullOrEmpty( name ) ) return;
// sich warm laufen
Aktion();
// 1.
ConsoleColor currentForeColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine( name );
// 2.
GC.Collect( GC.MaxGeneration, GCCollectionMode.Forced );
int[] gcCounts = new int[GC.MaxGeneration + 1];
for ( int i = 0; i <= GC.MaxGeneration; i++ )
{
gcCounts[i] = GC.CollectionCount(i);
}
// 3.
Stoppuhr watch = new Stopwatch();
watch.Start();
ulongcycleCount = GetCycleCount();
for ( int i = 0; i < iteration; i++ ) action();
ulong cpuCycles = GetCycleCount() -cycleCount;
watch.Stop();
// 4.
Console.ForegroundColor = currentForeColor;
Console.WriteLine( "tTime Elapsed:t" + watch.ElapsedMilliseconds.ToString( "N0" ) + "ms" );
Console.WriteLine( "tCPU Cycles:t" + cpuCycles.ToString( "N0" ) );
// 5.
for ( int i = 0; i <= GC.MaxGeneration; i++ )
{
int count = GC.CollectionCount(i) - gcCounts[i];
Console.WriteLine( "tGen " + i + ": tt" + count );
}
Console.WriteLine();
}
privater statischer ulong GetCycleCount()
{
ulong CycleCount = 0;
QueryThreadCycleTime( GetCurrentThread(), refcycleCount );
returncycleCount;
}
[DllImport( "kernel32.dll" )]
[Rückgabe: MarshalAs(UnmanagedType.Bool)]
static extern bool QueryThreadCycleTime(IntPtr threadHandle, ref ulongcycleTime);
[DllImport( "kernel32.dll" )]
static extern IntPtr GetCurrentThread();
}
Klassenprogramm
{
static void Main( string[] args )
{
var random = new Random(DateTime.Now.Millisecond);
var array = Enumerable.Repeat( 0, 50000 ).Select( _ => new Person { ID = random.Next() } ).ToArray();
//Lao Zhao-Programm
CodeTimer.Initialize();
CodeTimer.Time( "SortWithCustomComparer", 500, () => SortWithCustomComparer( CloneArray( array ) ) );
CodeTimer.Time( "SortWithLinq", 500, () => SortWithLinq( CloneArray( array ) ) );
Console.ReadLine();
}
private static void SortWithCustomComparer( Person[] array )
{
Array.Sort( array, new PersonComparer() );
}
private static void SortWithLinq( Person[] array )
{
var sortiert =
(von Person in Array
Bestellung nach Personen-ID
Person auswählen ).ToList();
}
privates statisches T[] CloneArray<T>( T[] Quelle )
{
var dest = new T[source.Length];
Array.Copy( Quelle, Ziel, Quelle.Länge );
Rückkehrziel;
}
}
Person der öffentlichen Klasse
{
öffentliche Zeichenfolge FirstName
{
erhalten;
Satz;
}
öffentliche Zeichenfolge Nachname
{
erhalten;
Satz;
}
öffentliche int-ID
{
erhalten;
Satz;
}
}
öffentliche Klasse PersonComparer: IComparer<Person>
{
public int Compare( Person x, Person y )
{
return x.ID - y.ID;
}
}
}
Die Testergebnisse zeigen tatsächlich die offensichtlichen Vorteile von Enumerable.OrderBy, sogar im Release-Kompilierungsmodus.
Im Prinzip ist das unmöglich, es muss also irgendwo unlauteren Wettbewerb geben.
Ich habe den gesamten Code noch einmal durchgesehen und die Position der beiden Testcodes angepasst:
CodeTimer.Time( "SortWithLinq", 100, () => SortWithLinq( CloneArray( array ) ) );
CodeTimer.Time( "SortWithCustomComparer", 100, () => SortWithCustomComparer( CloneArray( array ) ) );
Das wird natürlich keine Auswirkungen haben. Zweitens habe ich festgestellt, dass diese Vergleichsinstanz mehrmals erstellt wurde (eigentlich nur 100 Mal), also habe ich sie wie folgt optimiert:
privater statischer PersonComparer-Vergleicher = neuer PersonComparer();
private static void SortWithCustomComparer( Person[] array )
{
Array.Sort(Array, Vergleicher);
}
Es gibt keine Verbesserung der Ergebnisse.
Daher kann ich mich nur auf die Implementierung des Vergleichers konzentrieren:
öffentliche Klasse PersonComparer: IComparer<Person>
{
public int Compare( Person x, Person y )
{
return x.ID - y.ID;
}
}
Dies ist kein fairer Ansatz, sollte jedoch keine Leistungsprobleme verursachen. Ändern Sie ihn in die folgende Form:
public int Compare( Person x, Person y )
{
return x.ID.CompareTo( y.ID );
}
Es gibt immer noch keine Verbesserung der Leistung, sondern einen leichten Rückgang (das ist normal).
Scheint, als wären wir in einer Sackgasse? Tatsächlich sind wir der Wahrheit sehr nahe. Tatsächlich hat der Titel bereits die Antwort verkündet. Da das Problem darin liegt, dass person.ID ein Attribut ist, wird die Methode hier tatsächlich dreimal aufgerufen.
Ändern Sie also einfach die ID in ein Feld, und die Leistungsergebnisse werden nahezu den Erwartungen entsprechen.
Warum hat LINQ hier einen Vorteil? Ah, wenn Sie das Sortierprinzip von LINQ verstehen, ist es nicht schwer zu glauben, dass LINQ viele Aufrufe von Person.get_ID einsparen kann. Ich glaube, dass diese Analyse in Daniels nächstem Artikel ausführlicher behandelt wird, daher werde ich hier nicht angeben.
Daraus lässt sich die Schlussfolgerung ziehen, aber es ist dennoch ein wenig überraschend, dass LINQ in bestimmten Umgebungen Leistungsvorteile bietet. Aber was ich sagen möchte ist, dass es tatsächlich viele Faktoren gibt, die diese Schlussfolgerung beeinflussen. Wir können wirklich nicht pauschal sagen, dass LINQ Leistungsvorteile haben muss oder Array.Sort Leistungsvorteile haben muss.