myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
» Regeln
» Wie poste ich richtig?
» Forum-FAQ

Mitglieder
» Liste / Suche
» Wer ist wo online?

Ressourcen
» openbook: Visual C#
» openbook: OO
» Microsoft Docs

Team
» Kontakt
» Übersicht
» Wir über uns

» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Entwicklung » Basistechnologien und allgemeine .NET-Klassen » Performanterer Weg in einem Byte Array ein anderes (mehrfach) zu suchen
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | Thema zu Favoriten hinzufügen

Seiten (2): « vorherige 1 [2] Antwort erstellen
Zum Ende der Seite springen  

Performanterer Weg in einem Byte Array ein anderes (mehrfach) zu suchen

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
4dk2 4dk2 ist männlich
myCSharp.de-Mitglied

Dabei seit: 11.12.2018
Beiträge: 12


4dk2 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ja es geht um .Net Framework.
Hab es editiert. Es geht darum das es auch noch Leute geben soll, die mit .net Framework arbeiten müssen. (Ich z.b)
Der Code von stackoverflow ist .net Framework kompatibel, dort schneller und zugleich auch noch bei .net core genausoschnell. Mehr gibt’s dazu nicht zu sagen
19.12.2019 19:34 Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 13.580
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Also ist das Fazit: der Code ist allgemein schneller, egal in welcher Runtime. fröhlich

Vermutlich einfach, weil es ein anderer Algorithmus ist, der insgesamt performanter ist - egal in welcher Runtime.
Kannst ja selbst mit  https://github.com/dotnet/BenchmarkDotNet oder einem Profiler Deiner Wahl analyseren, was genau schneller/langsamer ist. Augenzwinkern
19.12.2019 19:42 Beiträge des Benutzers | zu Buddylist hinzufügen
4dk2 4dk2 ist männlich
myCSharp.de-Mitglied

Dabei seit: 11.12.2018
Beiträge: 12


4dk2 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

in 3.1 Core und 4.7.2 Framework scheint er es zu sein.
Es gibt sicher noch bessere, und ich bin mir sicher UNSAFE{} wird am schnellsten sein.
Und das klingt gut mit benchmarkdotnet, werd ich mal reinhaken.
19.12.2019 21:54 Beiträge des Benutzers | zu Buddylist hinzufügen
gfoidl gfoidl ist männlich
myCSharp.de-Team

avatar-2894.jpg


Dabei seit: 07.06.2009
Beiträge: 6.631
Entwicklungsumgebung: VS 2019
Herkunft: Waidring


gfoidl ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo 4dk2,

Zitat:
Es gibt sicher noch bessere, und ich bin mir sicher UNSAFE{} wird am schnellsten sein.

Pauschal bin ich mir da wiederum nicht so sicher. Allgemein:
  • Egal ob unsafe od. Unsafe, die Runtime kann keine Sicherheiten in Bezug auf Indexgrenzen, Typsicherheit, etc. mehr gewährleisten und das kann potentielle Bugs mit sich bringen. Will man diese Sicherheit dennoch, so sind indizierte Zugriffe explizit zu validieren, etc. und schon ist der vermeintliche Vorteil von unsicherem Code wieder weg.
  • Es gibt ein paar "Tricks" mit denen sicherer Code genauso effizient wie unsicherer Code ist, da der JIT gleichen bzw. gleichwertigen Maschinencode erzeugt. Da denke ich v.a. an das Vermeiden von Bound-Checks bei indizierten Zugriffen.
  • ...
da gäbe es noch viel zu schreiben, aber das geht an diesem Thema vorbei.


Statt #if isCORE (und dem #define) kannst du die vordefinierten Präprozessor Symbole NETCOREAPP (mit od. ohne Version) auch verwenden. Siehe dazu  Target frameworks in SDK-style projects / How to specify target frameworks.

Vorab: wie Abt erwähnt hat, hab ich keine Fußnote für den schnellsten Code ;-)
V.a. dann nicht wenn ich das eher schnell runtertippe, als wirklich als Aufgabe optimierten Code zu liefern.

Aber als ich über den von dir geposteten Code geflogen bin, konnte ich nicht glauben dass dieser so wesentlich schneller ist, da
  • in IndexOfSequence jedesmal ein Array alloziiert um dann dorthin zu kopieren -- das Array hat immer die gleich Größe, daher könnte einmal alloziiert und dann wiederverwendet werden od. um die Allokation überhaupt zu sparen stackalloc (vorzugsweise zusammen mit Span<byte> verwendet werden damit die Bound-Checks erhalten bleiben)

  • bei .NET Full in gleicher Methode Linq für SequenceEqual verwendet wird und das kann nicht schneller sein als eine vektorisierte Variante wie sie per Span gegeben ist. Auch wenn Linq prüft ob es eine IList<T> ist und dann per for-Schleife iteriert. Es wird dann jedesmal ein Vergleich mit EqualityComparer<byte>.Default durchgeführt. Dieser wird in neueren Versionen des JITs (.NET Core 2.1 denke ich und dann ab .NET 4.8 da diese Optimierungen zurückportiert wurden) devirtualisiert, da byte ein Werttyp ist. Nichtsdestotrotz kann das nicht schneller als vektorisierter Code sein, da es Element für Element (wenn auch indiziert) passiert.

  • für jeden Aufruf von IndexOfSequence eine List<int> alloziiert wird -- gut bei meiner Variante wird jedesmal der vom Compiler generierte Iterator alloziiert, aber das ist nur ein Objekt, während es bei der Liste zwei sind, eins für die Liste selbst und eins für das Array, welches die Liste intern verwendet

  • i = Array.IndexOf(buffer, pattern[0], i + 1); "rückt" nur um i weiter wenn es ein Match gab, anstatt i + searchPattern.Length, erinnert ein wenig an Schlemiel dem Maler (;-))

  • Array.IndexOf sucht vom startIndex bis zum Ende des Arrays. Das ist bei sequentiellem Code kein Problem, aber bei der parallelen Version, da die obere Index-Schranke range.Item2 nicht berücksichtigt wird, d.h. es wird potentiell in einer anderen Range weitergesucht obwohl das nicht nötig wäre. (Es wird dann geprüft ob der gefunden Index innerhalb der gültigen Range ist, daher ist das Ergebnis korrekt.)

  • ebenso wird taskCurrent alloziiert, kann vermieden werden und stattdessen direkt mit source gearbeitet werden
Kurz also jede Menge vermeidbarer Allokationen und sequentieller Code statt vektorisiertem.

Grunsätzlich sollte .NET Core "schneller" sein als .NET Full, da dort die Entwicklung vorangetrieben wird. Bei einem Vergleich sollte jedoch auch Tiered-Compilation berücksichtigt werden, die in .NET Core standardmäßig aktiviert ist. D.h. dort wird während einer "Startphase" der IL-Code nur minimal optimiert, so dass eben der Programmstart rasch fortschreiten kann. Erst wenn eine Methode mehrmals (aktuell 30x) ausgeführt wurde und auch erst nach Ende der Startphase (~150ms nachdem die letzte Methode geJITet wurde) wird diese erneut geJITet, diesmal aber mit maximaler Optimierung (im Sinne des dem JIT möglichen).
Wurde also der Vergleich rein mit meinem simplen Ansatz aus dem Projekt oben durchgeführt, so hakt es schon destwegen.

Soweit die Theorie, die ich (natürlich) validiert habe.


Dass nicht alle Tests vom oben angehängten Projekt mit deiner Lösung passieren lasse ich hier außer Acht, sollte aber wenn schon verglichen wird auch berücksichtigt werden. Daher hab ich für die Vergleiche unten die nötigen null-Checks, etc. eingebaut, so dass die Tests passieren und es vergleichbarer ist.

Dein Code sortiert die Liste, das hab ich bei den Vergleichen entfernt, damit es vergleichbarer ist und da es lt. Aufgabenstellung nicht gefordert ist.
Das Sortieren wäre für alle Varianten ohnehin gleicher Aufwand.




Zitat:
C#-Code:
if (totalLength < ThreshouldForParallel)
{
    //nicht per tasks...
    indices = new List<int>(source.IndexOfSequence(searchPattern));
    return indices.Count > 0;
}
else
{
    // ...
}

Wenn IndexOfSequence als Ergebnis eine List<int> liefert, warum weist du dann das Ergebnis nicht direkt indices zu?
Da hast du wohl die Codes falsch zusammenkopiert ;-)

Genauso ist "die Else" hier nicht nötig, da beim return die Methode ohnehin verlassen wird.


Wie vorhin erwähnt ist einfaches Messen nicht so einfach bzw. ist die Gefahr groß, dass die Ergebnisse nicht sinnvoll verwertbar sind. Daher ist z.B. BenchmarkDotNet (BDN) vorzuziehen, da mit diesem Werkzeug etliche Fallstricke berücksichtigt werden (ist aber dennoch kein Allheilmittel, denn die Ergebnisse müssen auch korrekt gelesen werden (können)).

Sequentieller Code-Pfad

Zuerst eine Betrachtung für rein sequentiellen Code, also ohne Parallel.ForEach.

Code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
|        Method |       Runtime |     Mean |   Error |  StdDev | Ratio |       Gen 0 | Gen 1 | Gen 2 |   Allocated |
|-------------- |-------------- |---------:|--------:|--------:|------:|------------:|------:|------:|------------:|
|  AllIndicesOf |      .NET 4.8 | 170.0 ms | 1.88 ms | 1.76 ms |  0.26 |           - |     - |     - |      2731 B |
| AllIndicesOf2 |      .NET 4.8 | 654.1 ms | 4.25 ms | 3.97 ms |  1.00 | 116000.0000 |     - |     - | 366754144 B |
| AllIndicesOf4 |      .NET 4.8 | 167.8 ms | 0.95 ms | 0.89 ms |  0.26 |           - |     - |     - |      2731 B |
|               |               |          |         |         |       |             |       |       |             |
|  AllIndicesOf | .NET Core 3.0 | 160.1 ms | 0.99 ms | 0.93 ms |  0.73 |           - |     - |     - |       645 B |
| AllIndicesOf2 | .NET Core 3.0 | 218.8 ms | 1.75 ms | 1.64 ms |  1.00 |  44666.6667 |     - |     - | 140643485 B |
| AllIndicesOf4 | .NET Core 3.0 | 159.2 ms | 1.16 ms | 0.97 ms |  0.73 |           - |     - |     - |       244 B |

AllIndicesOf ist dabei der Code den ich oben gepostet haben.
AllIndicesOf2 ist der von dir gepostete Code (bei dir hieß es AllIndicesOfFaster)
AllIndicesOf4 ist eine andere Variante die ich gerade erstellt habe und die ich momentan als ganz brauchbar empfinde (ohne auf "unsafe" zurückzugreifen).

AllIndicesOf3 gibt es auch noch und entspricht AllIndicesOf nur dass der Iterator selbst als ref struct ausgeführt wurde um die Allokation vom Iterator-Objekt und den Interface-Dispatch bei MoveNext (von der foreach-Schleife) zu vermeiden. Die Allokation wurde zwar geringer, aber der Laufzeit-Gewinn ist im Bereich des Messfehlers, da der "heiße Teil" das IndexOf ist, daher nicht in den Ergebnissen angeführt.

In den Ergebnissen ist zum Einen zu sehen dass .NET Full (hier 4.8 statt 4.7.2. (das hab ich nicht installiert)) wie erwartet nicht schneller als die .NET Core Version ist. Sogar ziemlich gegenteilig (v.a. wegen Span-basierten / vektorisiertem IndexOf und SequenceEqual).

Weiters ist zu sehen, dass AllIndicesOf2 jede Menge Allokationen hat und aufgrund der suboptimalen Implementierung doch recht klar langsamer ist.
Die Eingangs erwähnte Theorie ist zumindest beim sequentiellen Pfad bestätigt.

Paralleler Code-Pfad

Code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
|        Method |       Runtime |      Mean |     Error |    StdDev |    Median | Ratio | RatioSD |     Gen 0 | Gen 1 | Gen 2 |   Allocated |
|-------------- |-------------- |----------:|----------:|----------:|----------:|------:|--------:|----------:|------:|------:|------------:|
|  AllIndicesOf |      .NET 4.8 | 42.610 ms | 0.5172 ms | 0.4838 ms | 42.533 ms |  1.33 |    0.08 |         - |     - |     - |       14 KB |
| AllIndicesOf2 |      .NET 4.8 | 29.801 ms | 0.6081 ms | 1.7449 ms | 29.199 ms |  1.00 |    0.00 | 4843.7500 |     - |     - | 14929.02 KB |
| AllIndicesOf4 |      .NET 4.8 | 42.765 ms | 0.5323 ms | 0.4979 ms | 42.821 ms |  1.33 |    0.08 |         - |     - |     - |        8 KB |
|               |               |           |           |           |           |       |         |           |       |       |             |
|  AllIndicesOf | .NET Core 3.0 | 42.147 ms | 0.8226 ms | 0.8802 ms | 42.177 ms |  4.23 |    0.16 |         - |     - |     - |    11.79 KB |
| AllIndicesOf2 | .NET Core 3.0 |  9.916 ms | 0.1976 ms | 0.2958 ms |  9.785 ms |  1.00 |    0.00 | 1859.3750 |     - |     - |  5725.27 KB |
| AllIndicesOf4 | .NET Core 3.0 | 42.019 ms | 0.7827 ms | 0.8038 ms | 41.805 ms |  4.22 |    0.16 |         - |     - |     - |     6.22 KB |

Ui, da schaut es sehr unerwartet aus...*
Dass es auf einmal gravierend langsamer ist entspricht überhaupt nicht dem was ich erwartet habe und entbehrt(e) sich jeglicher Logik.
Nach eingehender Untersuchung (PerView, VTune) konnte ich einen "Bug" in .NET Core als Übeltäter ausfindig machen, der im Span-basierten Code zum Tragen kommt, während diesem Umstand mit der Array-basierten Methode aus dem Weg gegangen wurde.
Bug ist in "" da der Code ja fehlerfrei funktioniert, aber dennoch ein Bug ist, da die Laufzeit-Ansprüche verfehlt wurden.
Das Schöne an .NET Core ist dass es Open Source ist, somit kann auch gleich die  Lösung für den Bug vorgeschlagen werden.

Einen Bug im Framework zu finden ist nicht so einfach, nicht da wenige vorhanden sind, sondern weil -- zumindest ich -- zuerst den Fehler überall anders suche.
Z.B. sind bei den Vergleichen zwischen den beiden Varianten bestimmte verwendete (Framework-) Methoden auszuschließen, da die Implementierung gleich ist.
Span<T>.IndexOf(ROS, ROS) führt zuerst ein IndexOf(ROS, ROS[0]) (also nach dem ersten Element des Suchmusters) durch und bei einem Treffer wird mit dem restlichen Suchmuster verglichen (ohne ROS[0], das wurde ja schon gefunden).
AllIndicesOf2 macht das gleichwertig, nur mit Array<T>.IndexOf(T[], T, int, int) und das wiederum -- zumindest in .NET Core -- delegiert zur Span-Version. AllIndicesOf2 vergleicht mit dem gesamten Suchmuster, obwohl nur searchPattern[1..] nötig wäre.
Summa summarum ist dieser Teil gleichwertig. Der JIT sollten das bischen Overhead vom weiterdelegieren wegoptimieren können und ein paar CPU-Zyklen mehr od. weniger sind im Bereich der Messgenauigkeit.
Dass nun dieser Bug genau bei IndexOf vorhanden ist, daran dachte ich vorerst nicht.

Wird obiger Benchmark (ohne den Fix) mit einer source ausgeführt, die lauter 0 enthält, außer dort wo das Suchmuster hinkopiert wurde, so ist AllIndicesOf2 wesentlich langsamer.

Code:
1:
2:
3:
4:
|        Method |      Mean |     Error |    StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
|-------------- |----------:|----------:|----------:|------:|------:|------:|------:|----------:|
| AllIndicesOf2 | 412.04 ms | 305.56 ms | 16.749 ms |  1.00 |     - |     - |     - |  15.42 KB |
| AllIndicesOf4 |  36.32 ms |  11.20 ms |  0.614 ms |  0.09 |     - |     - |     - |   6.18 KB |

Das trifft sich genau mit dem Verhalten vom Bug, da so dieser unperformante Framework-Code nur selten -- nur dort wo das Suchmuster tatsächlich ist -- ausgeführt wird.
Die anderen Punkte aus eingangs erwähnter Theorie zeigen sich hier doch recht deutlich.

Somit gut dass du deinen Code gepostet hast, sonst wäre dieser Bug wohl nicht entdeckt worden. Danke dafür!

Zum Prüfen der gefixten Version (per lokalem Build von .NET Core, etc.) hab ich jetzt keine Muße, aber ich gehe davon aus dass AllIndicesOf4 die schneller und weniger allozierende Variante bleibt. Der rein sequentielle Code-Pfad (Benchmark oben) dürft dann ebenfalls bessere Ergebnisse liefern.

Unabhängig vom Bug noch ein paar Anmerkungen zu den Ergebnissen / Code.

Da die Ergebnisse von isolierten Benchmarks stammen gehören diese auch entsprechend interpretiert / gelesen. V.a. in Hinblick auf Real-System ist die GC-Arbeit, durch die Allokationen, nicht zu vernachlässigen.
Ebenso wurden für die Messungen nur ein Suchmuster mit Länge 15 das viermal in die Quelle kopiert wurde verwendet. Da müssten schon ordentlich mehr Vergleiche durchgeführt werden um richtige Aussagen treffen zu können und letztlich wird es -- allgemein gesprochen -- wohl ein Kompromiss in die ein od. andere Richtung werden. Alleine schon wenn ich an ThreshouldForParallel denke. Was auf einem System besser sein mag, kann auf einem anderen System schlecht sein.

Einschub: aus den Ergebnissen ist auch zu sehen dass der Garbage Collector (GC) in .NET super Arbeit leistet. Das alloziieren ist i.d.R. nur ein Pointer-Move und daher sehr schnell. Aufwändiger ist das Abräumen (collect & compact) und hier leistet der GC -- v.a. für Gen0 -- wirklich effiziente Arbeit.

Anmerkung zu Parallel.ForEach

Parallel.ForEach versucht den ThreadPool -- sofern der TaskScheduler.Default verwendet wird -- zur Gänze auszulasten, somit kann auch das "System einfrieren".

Dies gilt es bei GUI-Anwendungen zu beachte, da so auch die GUI einfrieren kann. Hier kann mittels ParallelOptions.MaxDegreeOfParallelism eine Grenze gesetzt werden, so dass der GUI-Thread reaktiv bleibt.

Bei Server-Anwendungen, v.a. wenn viele gleichzeitige Anfragen zu erwarten sind, würde ich Parallel.ForEach eher verzichten, da die Parallelität eh über die Request behandelt wird. Ggf. sollten die RPS gemessen werden um entscheiden zu können ob der Einsatz der parallelen Schleife sinnvoll ist od. nicht.

mfG Gü


* bei diesem Ergebnis bin ich mir auch nicht ganz sicher ob der Wert stimmt. Hab zwar mehrmals den Benchmark laufen lassen und da kamen gleich Ergebnisse heraus.
Meine Zweifel konnte auch ein Profiler nicht widerlegen, da dort die CPU recht wenig Threads ausführt anstatt mit allen Kernen voll zu arbeiten. Näher hab ich das -- v.a. wegen des Bugs -- nicht untersucht. Es kann auch sein dass aufgrund der Test-Konstellation (4x Suchmuster reinkopiert) das genau mit dem Partitioner der parallen Schleife zusammenpasst. Um das zu Falsifizieren könnte ein eigener Partitioner erstellt werden, so dass in allen Varianten die parallen Schleifen gleiche Ranges erhalten. Aber auch dazu hab ich jetzt keine Lust mehr ;-)

Edit: der Fix ist bereits im master-Branch.

Edit: weiteres Optimierungspotential liegt darin die "Range-Splits" direkt in der parallelen Schleife zu behandeln, so dass das nachherige sequentielle Abarbeiten entfällt, sowie das führen dieser speziellen Liste.


Dateianhang:
unknown AllIndicesOf.zip (15,33 KB, 0 mal heruntergeladen)
21.12.2019 23:15 Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 13.580
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Chapeau! für den Post Gü!
21.12.2019 23:40 Beiträge des Benutzers | zu Buddylist hinzufügen
T-Virus T-Virus ist männlich
myCSharp.de-Mitglied

Dabei seit: 17.04.2008
Beiträge: 1.435
Entwicklungsumgebung: Visual Studio, Codeblocks, Edi
Herkunft: Nordhausen, Nörten-Hardenberg


T-Virus ist offline Füge T-Virus Deiner Kontaktliste hinzu

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

@gfoidl
Hut ab für den Post sowie das auffinden und melden des Bugs ;)
Hat doch was für sich, wenn man solche Themen mal im Detail durchleuchtet.
Ich hatte mich auch schon gewundert wie der Code mit .NET Framework schneller sein sollte als mit .NET Core.
Gerade auch bei den Optimierungen in .NET Core kann ich es mir nicht vorstellen, dass Framework Code wie ideser noch zu Core Code aufschließen könnte.
Alleine die Vektoriersierung in Core holt schon einen gewisse Größenordnung mehr Performance raus.

T-Virus
22.12.2019 10:12 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
gfoidl gfoidl ist männlich
myCSharp.de-Team

avatar-2894.jpg


Dabei seit: 07.06.2009
Beiträge: 6.631
Entwicklungsumgebung: VS 2019
Herkunft: Waidring


gfoidl ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo T-Virus,

dazu eine kleine Ergänzung...

Zitat:
die Vektoriersierung in Core

Span.IndexOf (die Erweiterungsmethode, eigentlich MemoryMarshal.IndexOf<T>(ROS, ROS)) schaut ob T ein byte ist wenn ja nimmt es die optimierte Version von SpanHelper.IndexOf(byte...) um für das erste Element einen Treffer zu erzielen. Ist dieser vorhanden, so wird der Rest mit dem Suchmusterrest (das je erste Element wurde ja schon gematcht) per SpanHelper.SequenceEqual verglichen.

SequenceEqual ist wiederum der generische "Einstiegspunkt", der prüft ob T ein byte ist und falls ja den spezialisierten Code nimmt. Falls nicht muss der Vergleich generisch durchgefürht werden, d.h. einfach per Schleife (ist auch abgewickelt / "unrolled") drüberriterieren und per EqualityComparer<T>.Default auf Gleichheit prüfen. Das ist aufwändig.

Wird hingegen der byte-spezialisierte Pfad genommen, so können mehre Optimieren durchgeführt werden:
  • vektorisierter Vergleich -- für SSE sind dazu mind. 16 bytes nötig, für AVX 32, für AVX-512 64, bei ARM analog

  • statt byte für byte, können z.B. je 8 bytes per long verglichen werden, dann je 4 bytes per int, je 2 bytes per short -- das ist auch Endianess-sicher, da es nur um gleich od. ungleich geht
Anmerkung:
Das gilt nicht nur für byte, sondern allgemein für alle T deren Größe (sizeof) 1 ist. Somit auch für sbyte.
Für sizeof(T) == 2 (char, short, ushort) wird eine ähnliche Spezialisierung vorgenommen.

In der Aufgabe hier hat das Suchmuster die Länge 15, ist also für den vektorisierten SequenceEqual-Pfad zu kurz, dafür können jedoch die im 2. Punkt genannten Tricks angewandt werden. D.h. idealisiert und vereinfacht (dem  Amdahlsches Gesetz nicht ganz genüge getan) sollte die Laufzeit dadurch 4...8x verkürzt werden.

Zitat:
den Optimierungen in .NET Core

Hier v.a. das besser interne Handling von Spans, welche der JIT direkt unterstützt und mehr od. weniger nur Prüfungen auf Einhaltung der Grenzen (bound checks) und Pointer-Bewegungen sind. Ziemlich ähnlich wie es bei sz-Arrays der Fall ist (nur dass eben keine Sub-Arrays, Array-Kopien, etc. nötig sind).
Ob diese direkte Unterstützung zu .NET 4.8 zurückportiert wurde weiß ich gerade nicht.

Dann gab es eine Menge Optimierungen in der Thread-Infrastruktur auf welchen die parallen Schleifen beruhen.



mfG Gü
22.12.2019 15:19 Beiträge des Benutzers | zu Buddylist hinzufügen
Seiten (2): « vorherige 1 [2] Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als 5 Monate.
Der letzte Beitrag ist älter als 3 Monate.
Antwort erstellen


© Copyright 2003-2020 myCSharp.de-Team | Impressum | Datenschutz | Alle Rechte vorbehalten. | Dieses Portal verwendet zum korrekten Betrieb Cookies. 01.04.2020 18:46