Wie kann ich das schnell machen?
Klar kann ich das machen:
static bool ByteArrayCompare(byte[] a1, byte[] a2)
{
if (a1.Length != a2.Length)
return false;
for (int i=0; i<a1.Length; i++)
if (a1[i]!=a2[i])
return false;
return true;
}
Aber ich suche entweder nach einer BCL- Funktion oder nach einer hochoptimierten, bewährten Methode, um dies zu tun.
java.util.Arrays.equals((sbyte[])(Array)a1, (sbyte[])(Array)a2);
funktioniert gut, aber es sieht nicht so aus, als würde das für x64 funktionieren.
Beachten Sie meine superschnelle Antwort hier .
IStructuralEquatable
Antworten:
Sie können die Enumerable.SequenceEqual- Methode verwenden.
Wenn Sie .NET 3.5 aus irgendeinem Grund nicht verwenden können, ist Ihre Methode in Ordnung.
Die Compiler \ Runtime-Umgebung optimiert Ihre Schleife, sodass Sie sich keine Gedanken über die Leistung machen müssen.
quelle
P / Invoke Kräfte aktivieren!
quelle
In .NET 4 gibt es dafür eine neue integrierte Lösung - IStructuralEquatable
quelle
StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2)
. NeinNullReferenceException
s hier.Benutzer gil schlug unsicheren Code vor, der diese Lösung hervorbrachte:
Dies führt einen 64-Bit-basierten Vergleich für so viel Array wie möglich durch. Diese Art von zählt auf der Tatsache, dass die Arrays qword ausgerichtet beginnen. Es wird funktionieren, wenn qword nicht ausgerichtet ist, nur nicht so schnell, als ob es wäre.
Es führt ungefähr sieben Timer schneller aus als die einfache
for
Schleife. Die Verwendung der J # -Bibliothek entspricht der ursprünglichenfor
Schleife. Die Verwendung von .SequenceEqual läuft etwa siebenmal langsamer. Ich denke nur, weil es IEnumerator.MoveNext verwendet. Ich stelle mir vor, dass LINQ-basierte Lösungen mindestens so langsam oder schlechter sind.quelle
Span<T>
bietet eine äußerst wettbewerbsfähige Alternative, ohne verwirrende und / oder nicht tragbare Flusen in die Codebasis Ihrer eigenen Anwendung werfen zu müssen:Die (Eingeweide der) Implementierung ab .NET Core 3.1.0 finden Sie hier .
Ich habe @ EliArbels Kern überarbeitet , um diese Methode hinzuzufügen, indem
SpansEqual
die meisten weniger interessanten Darsteller in den Benchmarks anderer gelöscht, mit unterschiedlichen Arraygrößen ausgeführt, Diagramme ausgegeben undSpansEqual
als Basislinie markiert werden, damit angegeben wird, wie die verschiedenen Methoden verglichen werdenSpansEqual
.Die folgenden Zahlen stammen aus den Ergebnissen und wurden leicht bearbeitet, um die Spalte "Fehler" zu entfernen.
Ich war überrascht zu sehen
SpansEqual
, dass die Methoden mit maximaler Array-Größe nicht die Nase vorn haben, aber der Unterschied ist so gering, dass ich nicht denke, dass es jemals eine Rolle spielen wird.Meine Systeminfo:
quelle
{ReadOnly,}Span<T>
hat eine eigene Version vonSequenceEqual
(gleicher Name, da es den gleichen Vertrag wie die entsprechendeIEnumerable<T>
Erweiterungsmethode hat, es ist nur schneller). Beachten Sie, dass Erweiterungsmethoden aufgrund der Einschränkungen für Typen{ReadOnly,}Span<T>
nicht verwendetIEnumerable<T>
werdenref struct
können.Span<T>
Implementierungen fürnetstandard1.1
und darüber (spielen Sie also mit diesem interaktiven Diagramm, um zu sehen, um welche es sich handelt). "Schnell"Span<T>
ist derzeit nur in .NET Core 2.1 verfügbar. Beachten Sie jedoch, dass insbesondere fürSequenceEqual<T>
"schnell" und "langsam" / "portabel" nur ein sehr geringer Unterschied bestehennetstandard2.0
sollte (obwohl die Ziele aufgrund dieser Ziele eine leichte Verbesserung erfahren sollten habe den vektorisierten Codepfad).Wenn Sie nicht dagegen sind, können Sie die J # -Baugruppe "vjslib.dll" importieren und die Methode Arrays.equals (byte [], byte []) verwenden ...
Beschuldige mich nicht, wenn dich jemand auslacht ...
EDIT: Für das Wenige, das es wert ist, habe ich Reflector verwendet, um den Code dafür zu zerlegen, und hier ist, wie es aussieht:
quelle
.NET 3.5 und höher haben einen neuen öffentlichen Typ,
System.Data.Linq.Binary
der kapseltbyte[]
. Es implementiert,IEquatable<Binary>
dass (tatsächlich) zwei Byte-Arrays verglichen werden. Beachten Sie, dassSystem.Data.Linq.Binary
auch implizite Konvertierungsoperator von hatbyte[]
.MSDN-Dokumentation: System.Data.Linq.Binary
Reflektordekompilierung der Equals-Methode:
Interessant ist, dass sie nur dann zu einer byteweisen Vergleichsschleife übergehen, wenn die Hashes der beiden Binärobjekte gleich sind. Dies geht jedoch zu Lasten der Berechnung des Hash im Konstruktor von
Binary
Objekten (durch Durchlaufen des Arrays mitfor
Schleife :-)).Die obige Implementierung bedeutet, dass Sie im schlimmsten Fall die Arrays möglicherweise dreimal durchlaufen müssen: zuerst den Hash von Array1 berechnen, dann den Hash von Array2 berechnen und schließlich (da dies das Worst-Case-Szenario ist, Längen und Hashes gleich) vergleichen Bytes in Array1 mit Bytes in Array 2.
Obwohl dies
System.Data.Linq.Binary
in BCL integriert ist, ist es meiner Meinung nach nicht der schnellste Weg, zwei Byte-Arrays zu vergleichen: - |.quelle
Ich habe eine ähnliche Frage zur Überprüfung gestellt, ob Byte [] voller Nullen ist. (Der SIMD-Code wurde geschlagen, daher habe ich ihn aus dieser Antwort entfernt.) Hier ist der schnellste Code aus meinen Vergleichen:
Gemessen an zwei 256-MB-Byte-Arrays:
quelle
quelle
Fügen wir noch eine hinzu!
Kürzlich hat Microsoft ein spezielles NuGet-Paket veröffentlicht, System.Runtime.CompilerServices.Unsafe . Es ist etwas Besonderes, weil es in IL geschrieben ist und Funktionen auf niedriger Ebene bietet, die in C # nicht direkt verfügbar sind.
Eine seiner Methoden
Unsafe.As<T>(object)
ermöglicht das Umwandeln eines beliebigen Referenztyps in einen anderen Referenztyp, wobei Sicherheitsüberprüfungen übersprungen werden. Dies ist normalerweise eine sehr schlechte Idee, aber wenn beide Typen dieselbe Struktur haben, kann es funktionieren. Wir können dies also verwenden, um abyte[]
in a umzuwandelnlong[]
:Beachten Sie, dass
long1.Length
die ursprüngliche Array-Länge weiterhin zurückgegeben wird, da sie in einem Feld in der Speicherstruktur des Arrays gespeichert ist.Diese Methode ist nicht ganz so schnell wie andere hier gezeigte Methoden, aber sie ist viel schneller als die naive Methode, verwendet keinen unsicheren Code oder P / Invoke oder Pinning und die Implementierung ist recht einfach (IMO). Hier sind einige BenchmarkDotNet- Ergebnisse von meinem Computer:
Ich habe auch einen Kern mit allen Tests erstellt .
quelle
NewMemCmp
Antwort aktualisiert , um AVX-2 zu verwendenIch habe eine Methode entwickelt, die auf meinem PC leicht schlägt
memcmp()
(Antwort von Sockel) und sehr leicht schlägt (Antwort vonEqualBytesLongUnrolled()
Arek Bulski). Grundsätzlich wird die Schleife um 4 statt um 8 abgewickelt.Update 30. März 2019 :
Ab .NET Core 3.0 bieten wir SIMD-Unterstützung!
Diese Lösung ist auf meinem PC mit großem Abstand am schnellsten:
quelle
null
.Span
undmemcpy
?Ich würde unsicheren Code verwenden und die
for
Schleife ausführen, um Int32-Zeiger zu vergleichen.Vielleicht sollten Sie auch in Betracht ziehen, die Arrays als nicht null zu überprüfen.
quelle
Wenn Sie sich ansehen, wie .NET string.Equals ausführt, sehen Sie, dass es eine private Methode namens EqualsHelper verwendet, die eine "unsichere" Zeigerimplementierung aufweist. .NET Reflector ist Ihr Freund, um zu sehen, wie Dinge intern erledigt werden.
Dies kann als Vorlage für den Byte-Array-Vergleich verwendet werden, für den ich eine Implementierung im Blog-Beitrag Fast Byte-Array-Vergleich in C # durchgeführt habe . Ich habe auch einige rudimentäre Benchmarks durchgeführt, um festzustellen, wann eine sichere Implementierung schneller ist als die unsichere.
Das heißt, wenn Sie nicht wirklich Killer-Performance brauchen, würde ich einen einfachen Fr-Loop-Vergleich anstellen.
quelle
Ich konnte keine Lösung finden, mit der ich völlig zufrieden bin (angemessene Leistung, aber kein unsicherer Code / Pinvoke), also habe ich mir etwas ausgedacht, nichts wirklich Originelles, aber es funktioniert:
Leistung im Vergleich zu einigen anderen Lösungen auf dieser Seite:
Einfache Schleife: 19837 Zecken, 1,00
* BitConverter: 4886 Ticks, 4.06
UnsafeCompare: 1636 Ticks, 12.12
EqualBytesLongUnrolled: 637 Ticks, 31.09
P / Memcmp aufrufen: 369 Ticks, 53,67
Getestet in Linqpad, 1000000 Bytes identische Arrays (Worst-Case-Szenario), jeweils 500 Iterationen.
quelle
Es scheint, dass EqualBytesLongUnrolled das Beste aus dem oben vorgeschlagenen ist.
Übersprungene Methoden (Enumerable.SequenceEqual, StructuralComparisons.StructuralEqualityComparer.Equals) waren nicht geduldig für langsam. Auf 265 MB Arrays habe ich Folgendes gemessen:
quelle
NewMemCmp
Antwort aktualisiert , um AVX-2 zu verwendenIch habe hier nicht viele linq-Lösungen gesehen.
Ich bin mir der Auswirkungen auf die Leistung nicht sicher, halte mich jedoch im Allgemeinen an
linq
die Faustregel und optimiere sie später, falls erforderlich.Bitte beachten Sie, dass dies nur funktioniert, wenn es sich um Arrays gleicher Größe handelt. Eine Erweiterung könnte so aussehen
quelle
Ich habe einige Messungen mit dem angehängten Programm .net 4.7 Release Build ohne den angehängten Debugger durchgeführt. Ich denke, die Leute haben die falsche Metrik verwendet, da es bei der Geschwindigkeit darum geht, wie lange es dauert, herauszufinden, ob zwei Byte-Arrays gleich sind. dh Durchsatz in Bytes.
Wie Sie sehen, gibt es keinen besseren Weg als
memcmp
und es ist um Größenordnungen schneller. Eine einfachefor
Schleife ist die zweitbeste Option. Und es verwirrt mich immer noch, warum Microsoft nicht einfach eineBuffer.Compare
Methode einschließen kann.[Program.cs]:
quelle
Für den Vergleich von Kurzbyte-Arrays ist Folgendes ein interessanter Hack:
Dann würde ich wahrscheinlich auf die in der Frage aufgeführte Lösung zurückgreifen.
Es wäre interessant, eine Leistungsanalyse dieses Codes durchzuführen.
quelle
Für diejenigen unter Ihnen, die sich für die Reihenfolge interessieren (dh möchten, dass Sie
memcmp
einint
ähnliches Ergebnis wie nichts zurückgeben), enthält .NET Core 3.0 (und vermutlich .NET Standard 2.1, auch bekannt als .NET 5.0) eineSpan.SequenceCompareTo(...)
Erweiterungsmethode (plus aSpan.SequenceEqualTo
), die dies kann verwendet werden, um zweiReadOnlySpan<T>
Instanzen zu vergleichen (where T: IComparable<T>
).Im ursprünglichen GitHub-Vorschlag umfasste die Diskussion Ansätze mit Sprungtabellenberechnungen, Lesen von a
byte[]
aslong[]
, SIMD-Verwendung und p / invoke für die CLR-Implementierungmemcmp
.In Zukunft sollte dies Ihre Methode sein, um Byte-Arrays oder Byte-Bereiche zu vergleichen (wie sie
Span<byte>
anstellebyte[]
Ihrer .NET Standard 2.1-APIs verwendet werden sollten), und sie ist schnell genug, sodass Sie sich nicht mehr um die Optimierung kümmern sollten (und nein, trotz der Ähnlichkeiten im Namen ist es nicht so miserabel wie das schrecklicheEnumerable.SequenceEqual
).quelle
Ich dachte über Blocktransfer-Beschleunigungsmethoden nach, die in viele Grafikkarten eingebaut sind. Aber dann müssten Sie alle Daten byteweise kopieren, was Ihnen nicht viel hilft, wenn Sie nicht einen ganzen Teil Ihrer Logik in nicht verwaltetem und hardwareabhängigem Code implementieren möchten ...
Eine andere Möglichkeit zur Optimierung, die dem oben gezeigten Ansatz ähnelt, besteht darin, von Anfang an so viele Daten wie möglich in einem langen [] statt in einem Byte [] zu speichern, beispielsweise wenn Sie sie nacheinander aus einer Binärdatei lesen. oder wenn Sie eine Speicherzuordnungsdatei verwenden, lesen Sie Daten als lange [] oder einzelne lange Werte ein. Dann benötigt Ihre Vergleichsschleife nur 1/8 der Anzahl der Iterationen, die sie für ein Byte [] mit derselben Datenmenge ausführen müsste. Es kommt darauf an, wann und wie oft Sie vergleichen müssen und wann und wie oft Sie byteweise auf die Daten zugreifen müssen, z. B. um sie in einem API-Aufruf als Parameter in einer erwarteten Methode zu verwenden ein Byte []. Am Ende können Sie nur feststellen, ob Sie den Anwendungsfall wirklich kennen ...
quelle
Dies ist mit ziemlicher Sicherheit viel langsamer als jede andere hier angegebene Version, aber es hat Spaß gemacht, sie zu schreiben.
quelle
Ich entschied mich für eine Lösung, die von der von ArekBulski veröffentlichten EqualBytesLongUnrolled-Methode inspiriert war, mit einer zusätzlichen Optimierung. In meinem Fall liegen die Array-Unterschiede in Arrays in der Regel nahe am Ende der Arrays. Beim Testen stellte ich fest, dass der Vergleich von Array-Elementen in umgekehrter Reihenfolge bei großen Arrays einen enormen Leistungsgewinn gegenüber der memcmp-basierten Lösung bedeutet. Hier ist diese Lösung:
quelle
Entschuldigung, wenn Sie nach einer verwalteten Methode suchen, die Sie bereits korrekt ausführen, und meines Wissens gibt es in der BCL keine integrierte Methode, um dies zu tun.
Sie sollten einige anfängliche Nullprüfungen hinzufügen und sie dann einfach wiederverwenden, als ob sie in BCL wären.
quelle
Verwenden Sie
SequenceEquals
dazu zum Vergleich.quelle
Wenn Sie nach einem sehr schnellen Vergleich von Byte-Array-Gleichheit suchen, empfehlen wir Ihnen, diesen Artikel von STSdb Labs zu lesen: Vergleich von Byte-Array-Gleichheit. Es enthält einige der schnellsten Implementierungen für den Vergleich von Byte [] -Array-Gleichheit, die vorgestellt, auf Leistung getestet und zusammengefasst werden.
Sie können sich auch auf folgende Implementierungen konzentrieren:
BigEndianByteArrayComparer - schneller byte [] Array Vergleich von links nach rechts (BigEndian) BigEndianByteArrayEqualityComparer - - schnellen byte [] Gleichheitsvergleich von links nach rechts (BigEndian) LittleEndianByteArrayComparer - schnellen byte [] Array Vergleich von rechts nach links (LittleEndian) LittleEndianByteArrayEqualityComparer - schneller Byte [] Gleichheitsvergleich von rechts nach links (LittleEndian)
quelle
Die kurze Antwort lautet:
Auf diese Weise können Sie den optimierten .NET-Zeichenfolgenvergleich verwenden, um ein Byte-Array zu vergleichen, ohne dass unsicherer Code geschrieben werden muss. So geht das im Hintergrund :
quelle
Compare(new byte[]{128}, new byte[]{ 255 }) == true
überhaupt nichtDa viele der oben genannten ausgefallenen Lösungen nicht mit UWP funktionieren und ich Linq und funktionale Ansätze liebe, drücke ich Ihnen meine Version für dieses Problem. Um dem Vergleich zu entgehen, wenn der erste Unterschied auftritt, habe ich .FirstOrDefault () gewählt.
quelle
IndexOutOfRangeException
wenn nicht-leere Arrays zu vergleichen , weil Sie den Zugriff auf Elemente1
durch ,ba0.Length
wenn es sein soll ,0
durchba0.Length - 1
. Wenn Sie das beheben,Enumerable.Range(0, ba0.Length)
wird immer noch fälschlicherweisetrue
für Arrays gleicher Länge zurückgegeben, bei denen sich nur die ersten Elemente unterscheiden, da Sie nicht zwischen den ersten Elementen unterscheiden können, die zufriedenstellend sind,predicate
und keinen Elementen, die zufriedenstellend sindpredicate
.FirstOrDefault<int>()
kehrt0
in beiden Fällen zurück.