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

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 »
peterpan4711
myCSharp.de-Mitglied

Dabei seit: 05.10.2019
Beiträge: 19


peterpan4711 ist offline

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

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

Hi,

ich bin auf der Suche nach einem schnellen und performanten Weg um in einem Byte Array ein anderes Byte Array zu suchen.

Aktuell nutze ich folgende Funktion:

C#-Code:
private int IndexOf(int index, byte[] AllBytes, byte[] searchByteArray)
        {
            for (int i = index; i <= AllBytes.Length - 1 - searchByteArray.Length - 1; i++)
            {

                for (int j = 0; j <= searchByteArray.Length - 1; j++)
                {
                    if (AllBytes[i + j] == searchByteArray[j])
                    {
                        if (j + 1 == searchByteArray.Length)
                            return i;
                    }
                    else
                        break;
                }
            }
            return -1;
        }

Funktioniert gut, gibt mir den Index des ersten Bytes dann wieder.

Nur hat mein großes Array nun ca 900000000 Bytes. Und das suchArray besteht aus 10-15 Bytes. Das ganze dauert dann ewig.

Hat jemand von euch eine Abhilfe?

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von peterpan4711 am 06.10.2019 16:51.

05.10.2019 17:07 E-Mail | 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.321
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

Die Frage ist, was das Ziel dieser Suche ist.
Und auch warum du 900MB nach 10-15 Bytes absuchen willst.
Mich würde der Grund für diese Umsetzung interessieren, da es irgenwie nicht sinnvoll klingt sowas umzusetzen.

T-Virus
05.10.2019 17:33 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


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


Abt ist offline

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

Was haste denn bisher versucht oder recherchiert??
Bisher hast ja einfach nur zwei simple Schleifen.

Allgemeine Tipps:
- Können die 900 MB wirklich zeitgleich in den RAM? -> Asynchrone Streams (erfordert C# 8)
- Unnötige Allocations können durch Span vermieden werden
- Kann man das Array parallel lesen? -> Paralelle Schleife (Achte auf die .NET Pitfalls hier, siehe Doku)
05.10.2019 17:37 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.321
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

Ich habe mal einen Code genommen und etwas umgebaut, da die Schleife etwas sperrig war und scheinbar nie bis zum Ende durchläuft.

Aber um in den 900 MB dann 15 Bytes sogar am Ende zu finden, brauche ich hier gerade mal 3,8 Sek.
Wenn du also nicht X mal pro Sekunde damit in dem Array suchen musst, ist das noch recht schnell.

Nachtrag:
Hier der Code von mir, mit entfernen des if/else zu einem einfachem if mit break, habe ich sogar nochmal 0,3 Sek. gespart.

C#-Code:
        private int IndexOf(byte[] bytes, byte[] searchBytes, int index = 0)
        {
            for (int i = index; i <= bytes.Length - searchBytes.Length; i++)
            {
                for (int j = 0; j < searchBytes.Length; j++)
                {
                    if (bytes[i + j] != searchBytes[j])
                        break;

                    if (j + 1 == searchBytes.Length)
                        return i;
                }
            }

            return -1;
        }

T-Virus

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von T-Virus am 05.10.2019 17:55.

05.10.2019 17:50 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
peterpan4711
myCSharp.de-Mitglied

Dabei seit: 05.10.2019
Beiträge: 19

Themenstarter Thema begonnen von peterpan4711

peterpan4711 ist offline

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

Danke. Das problem ist, das ich die 900MB mehrfach durchsuchen muss, daher auch der Index.

Nehmen wir an ich suche 0x10, 0x11, 0x12, 0x13, 0x14,...., 0x50 (eben 10-15Bytes) dann kommen die evtl. 50x in den 900MB vor. Ich möchte dann von jeder Stelle den Index wissen, das mache ich über eine Schleife:

C#-Code:
  for (int i = (IndexOf(0, allData, suchBytes) - 1); i < allData.Length; i++)
                                {
                                    Debug.WriteLine(i);
                                    tmpIndex = IndexOf(i, allData, suchBytes);

                                    if (tmpIndex > -1)
                                    {

                                        Byte1_Index.Add(tmpIndex + suchBytes.Length);
                                        Debug.WriteLine("Counter: " + Byte_Index_Counter);
                                        i = tmpIndex;
                                        Byte_Index_Counter++;

                                    }
                                }

Das Dauert dann wirklich ewig. Ein Hex Editor macht das allerdings in ca 8-10 Sek, also muss es ja möglich sein.
05.10.2019 19:43 E-Mail | 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.321
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

Dann würde ich genau für den Fall, wie auch Abt schon vorgeschlagen hat, die Suchläufe auch parallel laufen lassen.
Dann kannst du deine Methode auch als async umsetzen und per Tasks die parallelen Suchen laufen lassen.

Dein Hexeditor hat nur den Voteil, dass er die Daten als Text darstellt.
Hier ist es dann auch einfacher zu suchen, da er lediglich im Text nach den Muster suchen muss.
Ist also nicht direkt vergleichbar mit der Suche einer Elementen Kette in einem Array.

Nachtrag:
Dein aktueller Ansatz prüft aber nicht ob der Eintrag überhaupt vorkommt.
Entsprechend startet deine äußere Schleife immer bei -1 und würde alle Einträge durchlaufen.
Diesen Fall solltest du abfangen, da du dann die ganze Schleife überspringen könntest.

T-Virus

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von T-Virus am 05.10.2019 19:52.

05.10.2019 19:49 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
peterpan4711
myCSharp.de-Mitglied

Dabei seit: 05.10.2019
Beiträge: 19

Themenstarter Thema begonnen von peterpan4711

peterpan4711 ist offline

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

Zitat:
Dein Hexeditor hat nur den Voteil, dass er die Daten als Text darstellt.
Hier ist es dann auch einfacher zu suchen, da er lediglich im Text nach den Muster suchen muss.
Ist also nicht direkt vergleichbar mit der Suche einer Elementen Kette in einem Array.

Wäre es dann hier sinnvoll beide Arrays in Strings umzuwandeln und dann zu suchen? Ich hab sowas mal gelesen...

Zitat:
Dein aktueller Ansatz prüft aber nicht ob der Eintrag überhaupt vorkommt.
Entsprechend startet deine äußere Schleife immer bei -1 und würde alle Einträge durchlaufen.
Diesen Fall solltest du abfangen, da du dann die ganze Schleife überspringen könntest.

Stimmt! Wird in der Praxis bei mir vermutlich nicht passieren, aber es einzubauen schadet nicht.

Zitat:
Dann würde ich genau für den Fall, wie auch Abt schon vorgeschlagen hat, die Suchläufe auch parallel laufen lassen.
Dann kannst du deine Methode auch als async umsetzen und per Tasks die parallelen Suchen laufen lassen.

Gearbeitet habe ich damit noch nie. Wie funktioniert das dann mit dem Index? Starten die Suchen ja dann vermutlich mit einem unterschiedlichen Index? - Wie bekomme ich das dynamisch aufgeteilt?

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von peterpan4711 am 05.10.2019 19:55.

05.10.2019 19:52 E-Mail | 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.321
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

Dann müsstest du am Ende auch mehrfach per IndexOf im String nach den Byte String samt Index suchen.
Somit verlagerst du das Problem anur von Byte Array Suche zu String Muster Suche.
Und dann musst du die String Positionen noch auf die Byte Array Positionen zurück mappen.

Am Ende dürftest du vermutlich mit einer parallelen Suche besser arbeiten können.

T-Virus
05.10.2019 19:56 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
peterpan4711
myCSharp.de-Mitglied

Dabei seit: 05.10.2019
Beiträge: 19

Themenstarter Thema begonnen von peterpan4711

peterpan4711 ist offline

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

Zitat von T-Virus:
Dann müsstest du am Ende auch mehrfach per IndexOf im String nach den Byte String samt Index suchen.
Somit verlagerst du das Problem anur von Byte Array Suche zu String Muster Suche.
Und dann musst du die String Positionen noch auf die Byte Array Positionen zurück mappen.

Ja, hab ich befürchtet.

Zitat:
Am Ende dürftest du vermutlich mit einer parallelen Suche besser arbeiten können.

Hast Du da etwas weitereführende Doku oder Code für mich? Ich habe tatsächlich noch nie mit parallalen Prozessen gearbeitet.
05.10.2019 19:58 E-Mail | 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.321
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

Die passenden Begriffe zur Suche sind async/Task und die Klasse Parallell.
Ansonsten hilft auch ein Blick in die Doku.
Code habe ich keinen für dich, da kannst du dich auch dran versuchen :)

 Doku

T-Virus
05.10.2019 20:02 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
peterpan4711
myCSharp.de-Mitglied

Dabei seit: 05.10.2019
Beiträge: 19

Themenstarter Thema begonnen von peterpan4711

peterpan4711 ist offline

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

Danke! Sieht jetzt gar nicht so wild aus.

Wie passe ich das denn mit dem Index an, wenn ich mehrere Suchen gleichzeitig starte? Teile ich die Gesamtbytes durch die Anzahl der Suchen die ich parallel starte?

Ich hab noch das hier gefunden:  https://coders-corner.net/2013/02/17/mul...ach-entwickeln/

Sieht super simpel aus, weiß eben nur nicht wie ich den Index aufteile damit ich alle Schleifen von vorn suchen und somit ja keine Zeitersparniss da ist.

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von peterpan4711 am 05.10.2019 20:20.

05.10.2019 20:04 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


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


Abt ist offline

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

Prinzipiell kannst Du, musst aber nicht selbst skalieren; das übernimmt alles TPL für Dich.
Wichtig sind die Pitfalls zu beachten:  Potential Pitfalls in Data and Task Parallelism

async wäre in diesem Fall kontraproduktiv, wenn bereits alle Informationen im Speicher direkt zugegriffen werden können.
Würde sich lohnen, wenn die Daten dynamisch geladen werden müssen.

PS: schau Dir auch Span<T> an. Gibt i.d.R. bei sowas einen deutlichen Performance Boost.
 https://adamsitnik.com/Span/
05.10.2019 20:21 Beiträge des Benutzers | zu Buddylist hinzufügen
peterpan4711
myCSharp.de-Mitglied

Dabei seit: 05.10.2019
Beiträge: 19

Themenstarter Thema begonnen von peterpan4711

peterpan4711 ist offline

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

Ich komme in Gedanken nicht so richtig weiter. Wenn man nun das einfache Beispiel nimmt:

C#-Code:
Parallel.For(1, 10, i => DoWork(i));
static void DoWork(int i)
    {
       Console.Write(i + " ");
   }

Dann müsste mein Code so (ähnlich) aussehen:

C#-Code:
Parallel.For((IndexOf(0, allData, suchBytes) - 1), allData.Length, i => DoWork(i));

static void DoWork(int i)
    {
       Debug.WriteLine(i);
                                    tmpIndex = IndexOf(i, allData, suchBytes);
                                    if (tmpIndex > -1)
                                    {
                                        Byte1_Index.Add(tmpIndex + suchBytes.Length);
                                        Debug.WriteLine("Counter: " + Byte_Index_Counter);
                                        i = tmpIndex;
                                        Byte_Index_Counter++;
                                    }
   }

Aber dann würden doch mehrere Prozesse das gleiche machen oder nicht?
05.10.2019 20:29 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


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


Abt ist offline

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

Zitat von peterpan4711:
Aber dann würden doch mehrere Prozesse das gleiche machen oder nicht?

Tasks sind keine Prozesse!

Tasks sind abstraktionen von Threads, und Threads wiederum sind Betriebssystem-Features und teil eines Prozesses.
Tasks sind jedoch .NET managed, benötigen weniger Ressourcen (.. paar andere Dinge hier..) und lassen sich in .NET viel einfacher nutzen als Threads.

Du musst das schon richtig einsetzen, ansonsten ist Paralellelität kontraproduktiv.
Insgesamt hast Du 3 Schleifen, wobei die zweite Schleife (innerhalb von IndexOf) die eigentlichen Durchlauf durch die Bytes durchführt.

I.d.R. lassen sich solche "Suchschleifen" meist sehr gut parallelisieren; aber nicht mit dem aktuellen Codeaufbau.
Du callst IndexOf mehrmals, was dazu führt, dass jedes mal alles durchsucht wird.
Das solltest Du minimieren.

PS: bitte nich immer alles zitieren.
 [Hinweis] Wie poste ich richtig? Punkt 2.3
05.10.2019 20:45 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.321
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

@Abt
Kann ich bestätigen, dass sich die Suchschleife deutlich performanter lösen lässt :)

Ich habe mal meinen Code angepasst und einen Mix aus einem Span<byte> Suchblock sowie der Parallel.For gebaut.
Laut VS Profiler ist er nach gerade mal 206ms durch und liefert mir auch den richtigen Index am Ende des Blocks.
Ich habe hierbei die Schleife vereinfacht, da ich per Span jeweils einen Block mit der länge der Such Bytes baue und nur noch den Block gegen die Suchbytes prüfe, was die gesamten Interationen erheblich verkürzt.

Den Code poste ich aber nicht sofort, hier sollte der TE ja auch was lernen und ein wenige testen ;)

T-Virus

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von T-Virus am 06.10.2019 09:06.

06.10.2019 09:05 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
peterpan4711
myCSharp.de-Mitglied

Dabei seit: 05.10.2019
Beiträge: 19

Themenstarter Thema begonnen von peterpan4711

peterpan4711 ist offline

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

Ich komme nicht weiter. Ich glaube das ich ja für meinen Fall eine verschachtelte Schleife brauche wie zb:

C#-Code:
static void Main(string[] args)

    {

    Parallel.For(1, 5, i =>

    {

    for (int k = 1; k < 10; k++)

    {

    DoWork(i, k);

    }

    });



   Console.ReadKey();

   }



   static void DoWork(int i, int k)

   {

   Console.Write(i + "," + k + " ");

    }

Aber ich verstehe es nicht so ganz wie ich ein return hinbekomme dann.

Folgendeen Code habe ich probiert:

C#-Code:
private int IndexOf(int index, byte[] AlleBytes, byte[] suchByteArray)
        {
            for (int ix = index; ix <= AlleBytes.Length - 1 - suchByteArray.Length - 1; ix++)
            {

                Parallel.For(0, suchByteArray.Length - 1, i => DoWork(i, index, AlleBytes, suchByteArray));

            }
            return -1;
        }

        static int DoWork(int i, int index, byte[] AlleBytes, byte[] suchByteArray)

        {
            Debug.WriteLine("Loop: " + i);
            if (AlleBytes[index + i] == suchByteArray[i])
                {
                    if (index + 1 == suchByteArray.Length)
                        return i;

                return -1;
                }
                else
            {
                return -1;
            }



        }

Aber die Ausgabe gibt nun zufällige Werte von 0-23 aus. Ich glaube ich bin auf dem falschen Weg.
06.10.2019 12:43 E-Mail | 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.321
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

Mit deinem Code wirst du nicht sinnvoll mit Parallel.For arbeiten können.
Ich habe es wie folgt gelöst.

Ich habe eine Methode AnyIndexOf angelegt.
Diese bekommt dann das ursprüngliche Array und das Array mit dem Suchmuster.
Als Rückgabe Wert gibt es dann dort ein int Array mit den Indizies der Positionen.

Die eigentliche äußere Schleife wird durch die Parallel.For Methode abgebildet.
Diese ruft dann im Action Parameter eine Methde AddIndex auf.
Diese bekommt dann ebenfalls das Array, das Suchmuster und den aktuellen Index.
Intern ruft diese dann die Suchmethode auf.

Die Suchmethode baut dann über Span<byte> einfach den Datenabschnitt zur Prüfung auf.
Somit braucht jede Interation nur durch die Byte Anzahl des Suchmuster durchlaufen.
Ggf. kann man hier auch auf das Span verzichten und direkt durch die Bytes ab dem Index laufen.
Habe ich aber noch nicht getestet, da ich Span<byte> mal verwenden wollte :)

Hier zur Hilfe mein Ansatz ohne die eigentliche IndexOf Implementierung, die solltest du dann hinbekommen.
Für Verbesserungsvorschläge bin ich offen :)

C#-Code:
int[] indizies = AnyIndexOf(bytes, searchBytes);

foreach(int idx in indizies)
    Console.WriteLine(idx);

private static int[] AnyIndexOf(byte[] bytes, byte[] searchBytes)
{
    ConcurrentBag<int> indizies = new ConcurrentBag<int>();

    Parallel.For(0,
        bytes.Length - searchBytes.Length + 1,
        i => AddIndex(indizies, bytes, searchBytes, i));

    return indizies.ToArray();
}

private static void AddIndex(ConcurrentBag<int> indizies, byte[] bytes, byte[] searchBytes, int index)
{
    // Wenn der aktuelle Index kein Treffer ist, dann abbruch
    if(IsMatch(bytes, searchBytes, index) == false)
        return;

    indizies.Add(index);
}

private static bool IsMatch(byte[] bytes, byte[] searchBytes, int index)
{
    for (int i = 0; i < searchBytes.Length; i++)
    {
        if (bytes[index + i] != searchBytes[i])
            return false;
    }

    return true;
}

Nachtrag:
Die Methoden sind bei mir static, da ich den Code aktuell in einer Test Konsolenanwendung habe.
Den Code sollte man in eine eigene Klasse überführen, dann kann diese auch immer wieder verwendet werden.

Nachtrag 2:
Ich habe den Code noch weiter optimiert und durch sinnvollere interne Verarbeitung mit IsMatch Methode ersetzt.
Contains kann ich mir auch sparen, da jeder Index ja eindeutig ist!
Hat die Ursprüngliche Suchzeit nochmals verkürzt.
Also noch optimierter geht es kaum :)
Man kann noch das HashSet durch eine Liste<int> ersetzen, da die Prüfung auf doppelte Indexe entfällt!

Was aber nicht gemacht wird, falls es den Fall gibt, dass dein Suchmuster und der Byte Block mehrere gleiche Bytes hat, dann bekommst du ggf. auch mehrere angereihte Indexe, was du ggf. abfangen müsstest.

T-Virus

Dieser Beitrag wurde 5 mal editiert, zum letzten Mal von T-Virus am 06.10.2019 16:00.

06.10.2019 13:29 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
peterpan4711
myCSharp.de-Mitglied

Dabei seit: 05.10.2019
Beiträge: 19

Themenstarter Thema begonnen von peterpan4711

peterpan4711 ist offline

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

Danke. Werde mir jetzt Span ansehen.

Wie lang dauert der komplette Scan der 900MB inkl. Finden von ca 50 Einträgen bei Dir ca?
06.10.2019 14:00 E-Mail | 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.321
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

Bisher habe ich nur ein 900 MB Byte Array mit den letzten 15 Stellen als 0x01 Bytes geprüft mit 15 0x01 Bytes als Suchmuster.
Müsste mir hier noch Testfälle anlegen um das zu prüfen.
Aber die Laufzeit dürfte hier bei allen Durchläufen gleich sein, da die Laufzeit nur von der Größe des Byte Array sowie der Länge des Suchmusters abhängig ist.
Der Rest wird dann so oder so durch die parallele Verarbeitung erledigt.
Je nach Kernzahl deiner CPU kann dann die Ausführungsdauer nochmals abnehmen/steigen.

Ansonsten sollte die Methode nun aber schnell genug sein, würde mich überraschen wenn es immer noch nicht reich :)

T-Virus

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von T-Virus am 06.10.2019 14:04.

06.10.2019 14:02 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.594
Entwicklungsumgebung: VS 2019
Herkunft: Waidring


gfoidl ist offline

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

Hallo peterpan4711,

auf TPL würde ich hier verzichten, stattdessen  Span IndexOf verwenden. Das ist (zumindest) in der .NET Core 3.0 Version vektorisiert und wird sicher gute Ergebnisse erzielen.

Sollte dennoch die TPL (Parallel.ForEach) verwendet werden wollen, so die Überladung welche Ranges erzeugt und dann jede Range per Span IndexOf durchsuchen und im Post-Schritt das Ergebnis zusammenbauen.

C#-Code (sequentiell / vektorisiert):
public static int IndexOf(byte[] source, int startIndex, byte[] searchPattern)
    => source.AsSpan(startIndex).IndexOf(searchPattern);

C#-Code (parallelisiert / vektorisiert):
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp4
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] source = new byte[150_000];
            byte[] searchPattern = { 0xde, 0xad, 0xbe, 0xef };

            var rnd = new Random();
            rnd.NextBytes(source);

            searchPattern.AsSpan().CopyTo(source.AsSpan(75_000));

            int index = IndexOf(source, 10, searchPattern);
            bool success = index == 75_000;

            // "poor man's unit tests" ;-)
            // Richtige Unit-Tests wären besser
            Console.ForegroundColor = success ? ConsoleColor.Green : ConsoleColor.Red;
            Console.WriteLine($"actual: {index}, expected: 75000 -> {(success ? "ok" : "fail")}");
            Console.ResetColor();
        }

        // Sollte empirisch ermittelt werden, mehr geht nicht auf die einfache Art und Weise
        // Könnte z.B. während der Installation durch einen Ermittlungs-Lauf auf dem Ziel-System
        // ermittelt werden, aber der Aufwand dafür steigt.
        // Der tatsächliche Wert wird vermutlich bei ein paar Millionen liegen -- ich habs nicht geprüft.
        private const int ThreshouldForParallel = 100_000;

        public static int IndexOf(byte[] source, int startIndex, byte[] searchPattern)
        {
            if (source.Length - startIndex < ThreshouldForParallel)
            {
                return IndexOf(source.AsSpan(startIndex), searchPattern) + startIndex;
            }

            int index = int.MaxValue;

            Parallel.ForEach(
                Partitioner.Create(startIndex, source.Length),
                () => int.MaxValue,
                (range, loopState, localIndex) =>
                {
                    // Die Range-Schreibweie benötigt C#8
                    int tmp = source.AsSpan(range.Item1..range.Item2).IndexOf(searchPattern);
                    if (tmp != -1)
                    {
                        // Könnte verwendet werden, wenn nicht unbedingt das erste Auftreten gewünscht ist,
                        // sondern irgendein auftreten von searchPattern in source.
                        //loopState.Stop();

                        localIndex = tmp + range.Item1;
                    }

                    return localIndex;
                },
                localIndex => InterlockedExchangeIfSmaller(ref index, localIndex);
            );

            return index == int.MaxValue ? -1 : index;
        }

        private static int IndexOf(ReadOnlySpan<byte> source, ReadOnlySpan<byte> searchPattern)
            => source.IndexOf(searchPattern);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static int InterlockedExchangeIfSmaller(ref int location, int newValue)
        {
            int snapShot;

            do
            {
                snapShot = location;
                if (snapShot < newValue) return snapShot;
            } while (snapShot != Interlocked.CompareExchange(ref location, newValue, snapShot));

            return snapShot;
        }
    }
}

mfG Gü
06.10.2019 14:05 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.594
Entwicklungsumgebung: VS 2019
Herkunft: Waidring


gfoidl ist offline

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

Hallo peterpan4711,

vllt. hab ich mich vorhin ein wenig verschätzt mit Parallel.ForEach. Wenn ich  https://benchmarkdotnet.org/ verwende, so erhalte ich

Code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
|             Method |     Size |            Mean |          Error |         StdDev |          Median |  Ratio | RatioSD |
|------------------- |--------- |-----------------|----------------|----------------|-----------------|--------|---------|
| IndexOf_Sequential |      100 |        35.25 ns |      0.2245 ns |      0.2100 ns |        35.22 ns |   1.00 |    0.00 |
|   IndexOf_Parallel |      100 |     6,990.23 ns |     91.0912 ns |     85.2068 ns |     6,980.10 ns | 198.30 |    2.89 |
|                    |          |                 |                |                |                 |        |         |
| IndexOf_Sequential |   100000 |     6,652.80 ns |    126.0095 ns |    117.8694 ns |     6,638.81 ns |   1.00 |    0.00 |
|   IndexOf_Parallel |   100000 |     9,625.42 ns |     34.2770 ns |     32.0627 ns |     9,626.04 ns |   1.45 |    0.03 |
|                    |          |                 |                |                |                 |        |         |
| IndexOf_Sequential | 10000000 | 1,800,221.32 ns | 30,094.1568 ns | 26,677.6862 ns | 1,799,309.96 ns |   1.00 |    0.00 |
|   IndexOf_Parallel | 10000000 |   447,255.93 ns |  8,889.4187 ns | 20,064.8993 ns |   438,615.09 ns |   0.26 |    0.01 |

Wie erwartet ist bei kleineren Länge der Overhead der TPL zu groß und dort ist (vektorisierte) sequentielle Verarbeitung schneller.
Irgendwo in (100000, 10000000) ist dann die TPL schneller*.
Ich hab das Projekt angehängt, damit du selbst die passende Schwelle -- die je nach System unterschiedlich sein kann -- ermitteln kannst bzw. auch die anderen Varianten einfach durch Hinzufügen von weiteren Benchmark-Methoden prüfen kannst.

* zumindest im Benchmark. Wie es im Verhalten vom Gesamtsystem aussieht geht hier nicht hervor, da auch beim Benchmark die CPU voll ausgelastet wird und somit das System insgesamt kaum mehr Reserven hat -- das sollte auch berücksichtigt werden.

mfG Gü


Dateianhang:
unknown ConsoleApp4.zip (1,46 KB, 0 mal heruntergeladen)
06.10.2019 14:30 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.321
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
Der TE braucht soweit ich dies verstanden habe, alle Treffer des Musters innerhalb der Byte Blocks.
Hier müsstest du also eine List<int> oder ein int Array mit allen Treffern liefern.
Aktuell hast du zwar eine List<int> indices aber verwendest die nicht.
Ist das nur zum testen oder ein Ansatz für den TE zum ausbauen?


T-Virus
06.10.2019 14:35 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.594
Entwicklungsumgebung: VS 2019
Herkunft: Waidring


gfoidl ist offline

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

Hallo T-Virus,

ah, danke das hatte ich überlesen und mich somit in die falsche Richtung entwickelt -- sorry (da hatte mich auch die Methoden-Signatur im ersten Post in die Irre geführt, da IReadOnlyList<int> AllIndicesOf(this byte[] source, byte[] searchPattern) passender wäre, auch der Titel sollte vom OP angepasst werden).
Ja dann einfach eine List<int> für die Suchtreffer aufbauen und den Code modifizieren dass nicht nur der erste Treffer (pro Range) zählt.
Beim Hinzufügen zur Liste bei TPL auf Races achten und entsprechend synchronisieren -- od. z.B. ConcurrentBag<int> verwenden, als threadsichere Datenstruktur.

Achtung:
Ich bemerke gerade dass die TPL-Version einen potentiellen Bug hat.
Und zwar genau dann wenn die Ranges genau so aufgeteilt werden, dass das Suchmuster auf zwei (benachbarte) Ranges aufgeteilt wird. Dann gibt es keinen Treffer.
Dieser Umstand müsste noch eingebaut werden, damit die Ergebnisse robust und sicher geliefert werden können.
(Ein Grund mehr warum Unit-Tests hilfreich wären um dieses Verhalten mit Tets abdecken zu können).



Zitat:
List<int> indices

Ist ein Artefakt vom Debuggen, hab ich mittlerweile oben rauseditiert.


mfG Gü
06.10.2019 14:52 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.321
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

Und noch eine Anmerkung.
Anstelle von List sollte ConcurrentBag verwendet werden, wenn innerhalb der Action in Parallel.For die Einträge hinzugefügt werden.

Meine Version oben ist wegen den parallelen Zugriffen bei mehreren gleichzeitigen Treffern weggeflogen, was ich anfangs wegen nur einem Treffer nicht getestet hatte :/

Nachtrag:
Den Part mit dem Partitioner.Create müsste ich mir noch mal anschauen, diesen kenne ich noch nicht.
Bei meinem Ansatz laufe ich eben byte für byte über den Index durch und haben somit keine Probleme mit benachbarten Suchmustern.
Erst bei identischen Bytes im Suchmuster und im Byte Block gibt es bei meinem Ansatz fehlerhafte Treffer.
Oder meintest du dies damit?

T-Virus

Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von T-Virus am 06.10.2019 15:59.

06.10.2019 15:53 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.594
Entwicklungsumgebung: VS 2019
Herkunft: Waidring


gfoidl ist offline

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

Hallo T-Virus,

ich bastle mal eine Variante...ganz folgen kann ich deiner Beschreibung so nicht. Schauen wir einmal...

mfG Gü
06.10.2019 16:00 Beiträge des Benutzers | zu Buddylist hinzufügen
Th69
myCSharp.de-Poweruser/ Experte

avatar-2578.jpg


Dabei seit: 01.04.2008
Beiträge: 3.362
Entwicklungsumgebung: Visual Studio 2015/17


Th69 ist offline

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

Noch als genereller Tipp:
Statt der naiven Suche könnte man hier auch andere  String-Matching-Algorithmus benutzen (ein Byte-Array verhält sich ja diesbezüglich analog zu einem String), s.a. mein Beitrag dazu:  Boyer-Moore-Horspool Suche (und weitere)
Insbesondere wenn ein Text mehrfach nach unterschiedlichen Mustern durchsucht werden soll, bietet sich jedoch die Erzeugung eines  Suffixbaums an (die dort erwähnten Autoren "Giegerich & Kurtz" waren meine Diplombetreuer ;-).
06.10.2019 16:13 E-Mail | 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.321
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

Die Idee mit der Textsuche hatte der TE auch schon anhand seines Beispiels mit dem Hex Editor eingeworfen.
Diese Idee hatte ich aber auch aus dem Grund verworfen, da er dann ein Mapping der String Positionen zu den Byte Array Positionen braucht.
Ebenfalls steigt dann durch die Umwandlung des Byte Array in einen String der Speicherverbrauch nochmals an.

Bei kleinen Blöcken ggf. sinnvoll aber bei 900MB an Bytes dürfte dies dann doch etwas zu viel Speicher werden.
Da wir die Ausführungsumgebung nicht kennen, würde ich auch nicht von beliebig viel Speicher ausgehen und die Speicherschonenste Version wählen.

T-Virus
06.10.2019 16:20 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Th69
myCSharp.de-Poweruser/ Experte

avatar-2578.jpg


Dabei seit: 01.04.2008
Beiträge: 3.362
Entwicklungsumgebung: Visual Studio 2015/17


Th69 ist offline

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

Ich meine nicht, das Byte-Array in einen String umzuwandeln, sondern den Algorithmus direkt auf dem Byte-Array durchzuführen (in C zum Beispiel ist ja ein String auch nur ein Byte-Array).

Und dein Beitrag

Zitat von T-Virus:
Dein Hexeditor hat nur den Voteil [sic], dass er die Daten als Text darstellt.
Hier ist es dann auch einfacher zu suchen, da er lediglich im Text nach den Muster suchen muss.
Ist also nicht direkt vergleichbar mit der Suche einer Elementen Kette in einem Array.

ergibt logisch keinen Sinn, denn auch ein Hexeditor sucht dann direkt auf Byte-Ebene und nicht als Text (der ja nur zur Anzeige als ASCI- bzw. Unicode-Zeichen dargestellt wird).

Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von Th69 am 06.10.2019 16:54.

06.10.2019 16:44 E-Mail | 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.321
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

Das hatte ich dann falsch verstanden.
Ich bin leider nicht mit dem Algorithmus vertraut um deinen Ansatz zu verstehen.
Müsste mich da erst einmal einlesen.

In C mag dies funktionieren, bei C# müsste man die Bytes aber in char konvertieren.
Da Strings auch als UTF-16 repräsentiert werden, bin ich nicht sicher ob der Ansatz hier funktioniert.
Aber wie geschrieben, bin ich da nicht drin um das beurteilen zu können.

T-Virus

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von T-Virus am 06.10.2019 16:49.

06.10.2019 16:48 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
peterpan4711
myCSharp.de-Mitglied

Dabei seit: 05.10.2019
Beiträge: 19

Themenstarter Thema begonnen von peterpan4711

peterpan4711 ist offline

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

Ich habe den Titel jetzt angepasst.

Also das Ziel ist es ein suchArray so oft zu finden wie es vorkommt und jeweils den Index in einer List zu speichern.
06.10.2019 16:54 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.594
Entwicklungsumgebung: VS 2019
Herkunft: Waidring


gfoidl ist offline

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

Hallo,

meine Umsetzung wäre wie folgt:

C#-Code:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;

// Könnte auch im csproj aktiviert werden
// <Nullable>enable</Nullable>
// hier aber so, da ich nur diesen Code kopiere
#nullable enable

namespace Extensions
{
    public static class ByteArrayExtensions
    {
        // Sollte empirisch ermittelt werden, mehr geht nicht auf die einfache Art und Weise
        // Könnte z.B. während der Installation durch einen Ermittlungs-Lauf auf dem Ziel-System
        // ermittelt werden, aber der Aufwand dafür steigt.
        // Der tatsächliche Wert wird vermutlich bei ein paar Millionen liegen -- ich habs nicht geprüft.
        internal const int ThreshouldForParallel = 500_000;     // internal für die Tests

        public static bool AllIndicesOf(
            this byte[]? source,
            byte[]? searchPattern,
            [NotNullWhen(true)] out IReadOnlyCollection<int>? indices)
        {
            if (source == null) throw new ArgumentNullException(nameof(source));
            if (searchPattern == null) throw new ArgumentNullException(nameof(searchPattern));

            if (searchPattern.Length == 0)
            {
                indices = default;
                return false;
            }

            if (source.Length < ThreshouldForParallel)
            {
                indices = new List<int>(AllIndicesOfSequential(source, searchPattern)).AsReadOnly();
                return indices.Count > 0;
            }

            var indicesSet = new HashSet<int>();
            var rangeIndices = new ConcurrentBag<int>();

            Parallel.ForEach(
                Partitioner.Create(0, source.Length),
                range =>
                {
                    rangeIndices.Add(range.Item2);

                    ReadOnlyMemory<byte> memorySlice = source.AsMemory(range.Item1..range.Item2);
                    foreach (int index in AllIndicesOfSequential(memorySlice, searchPattern))
                    {
                        lock (indicesSet)
                        {
                            indicesSet.Add(range.Item1 + index);
                        }
                    }
                }
            );

            // Alle oberen Grenzen der jeweiligen Ranges separat prüfen, um Splits über das Suchmuster zu berücksichtigen
            foreach (int range in rangeIndices)
            {
                int start = Math.Max(0, range - searchPattern.Length);
                int end = Math.Min(source.Length, range + searchPattern.Length);

                ReadOnlyMemory<byte> memorySlice = source.AsMemory(start..end);
                foreach (int index in AllIndicesOfSequential(memorySlice, searchPattern))
                {
                    indicesSet.Add(start + index);
                }
            }

            indices = indicesSet;
            return indices.Count > 0;
        }

        // Durch einen eigenen Enumerator könnte die Allokation des vom C# compiler generierten Enumerator
        // vermieden werden, aber ich denke das fällt hier nicht ins Gewicht und der Code ist so (viel) einfacher.
        private static IEnumerable<int> AllIndicesOfSequential(ReadOnlyMemory<byte> source, byte[] searchPattern)
        {
            int searchPatternLength = searchPattern.Length;
            int currentOffsetFromStart = 0;

            while (!source.IsEmpty)
            {
                int index = source.Span.IndexOf(searchPattern);

                if (index == -1)
                {
                    break;
                }

                yield return currentOffsetFromStart + index;

                source = source.Slice(index + searchPatternLength);
                currentOffsetFromStart += index + searchPatternLength;
            }
        }
    }
}

Das Test-Programm mit 900 MB und Suchmuster-Länge 15 das 4x vorkommt, braucht ~75ms um die Indizes zu finden.

Code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
init...
random data written
searchPattern copied 4 times
init done
measure...
time: 76 ms
count: 4
indices:
    0
    50
    500
    899999985

Angehängt das Projekt (mit Tests -- zumindest ein paar, die Projekt-Namen sind sinnfrei gewählt ;-)).

Ich kenn jetzt die Implementierung in .NET Core für IndexOf nicht im Detail, weiß aber dass diese in mehrere Iteationen verbessert wurde. Ob dort Methoden wie von Th69 vorgeschlagen umgesetzt wurden weiß ich jetzt auch nicht, kann mir das aber schon vorstellen.

mfG Gü


Dateianhang:
unknown ClassLibrary1.zip (6 KB, 0 mal heruntergeladen)
06.10.2019 17:54 Beiträge des Benutzers | zu Buddylist hinzufügen
peterpan4711
myCSharp.de-Mitglied

Dabei seit: 05.10.2019
Beiträge: 19

Themenstarter Thema begonnen von peterpan4711

peterpan4711 ist offline

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

Danke! Das scheint genau das zu sein, was ich brauche. Da C#8 komplett neu ist für mich muss ich erstmal ein bisschen mit der Syntax klar kommen.

Aber ich arbeite mich da jetzt mal durch. Nur kurz zum Verstädnis: Ich kann in C#8 doch auch <C#8 Code und Syntax nutzen, oder?

PS: Du scheinst einen echt schnellen PC zu haben:

Code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
init...
random data written
searchPattern copied 4 times
init done
measure...
time: 1400 ms
count: 4
indices:
        0
        50
        500
        899999985
07.10.2019 17:25 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


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


Abt ist offline

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

Ja, C# Versionen sind prinzipiell rückwärtskompatibel.
Das meiste ist quasi nur  Syntaktischer Zucker
07.10.2019 17:50 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.594
Entwicklungsumgebung: VS 2019
Herkunft: Waidring


gfoidl ist offline

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

Hallo peterpan4711,

Zitat:
Du scheinst einen echt schnellen PC zu haben:

Kann sein ;-)
Bedenke auch dass nur ab .NET Core 3.0 optimale Leistung erzielt wird, da
  • dort "Fast-Span" verwendet wird im gegensatz zu .NET 4.x, d.h. der JIT-Compiler (RyuJit) versteht Span-Code besser und erzeugt besseren Maschinencode

  • viele Span-Operationen vektorisiert sind auf Basis von  Hardware Intrinsics in .NET Core

  • sonst noch eine Menge an Optimierungen in der Code-Basis von .NET Core stattgefunden haben (und stattfinden) die nur teilweise, wenn überhaupt, nach .NET 4.x portiert werden (u.a. ist das ein Grund für das Zusammenlegen mit .NET 5, siehe  .NET 5 kommt 2020)
Das "Ziel" von Hex-Editoren mit den 8..10s hast du dennoch geschlagen :-)

Zitat:
Ich kann in C#8 doch auch <C#8 Code und Syntax nutzen, oder?

Wie Abt schon erwähnte sind die C#-Versionen (großteils) rückwärts/abwärtskompatibel.
Ich hab ein paar neue Features von C# 8 verwendet, um zu zeigen wie diese angewandt werden können. Konkret nullable reference types und ranges.
Beides ist kein Muss für diese Methode.

Mit  nullable gibt der C#-Compiler Warnungen (od. Fehler wenn TreatWarningsAsErrors gesetzt wurde) und das hilft bei der Vemeidung von  [FAQ] NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt

Mit  ranges lassen sich Memory-/Span-Operationen wie "Slicing" imho eleganter schreiben, wenn zwei Indizes gegeben sind, da nicht explizit die Länge ermittelt werden muss.

Wegen nullable und der praktischen Verwenden dieser Methode schaut auch die Signatur so aus:

C#-Code (Verwendung):
if (source.AllIndicesOf(searchPatter, out IReadOnlyCollection<int>? indices)
{
    // Hier kann 'indices' verwendet werden und der C# 8-Compiler weiß, dass 'indices' nicht null sein darf
}

Wäre die Signatur IReadOnlyCollection<int> AllIndicesOf(...) so müsste das Ergebnis explizit in eine lokale Variable geschrieben werden, da dann auf null geprüft wird. Mit gewählter Signatur gehts in einem.

Jetzt vorsorglich der Hinweis (auch an mich selbst) nicht zuweit ins Off-Topic C# 8 abzurutschen...

mfG Gü
08.10.2019 10:59 Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum
Antwort erstellen


© Copyright 2003-2019 myCSharp.de-Team | Impressum | Datenschutz | Alle Rechte vorbehalten. | Dieses Portal verwendet zum korrekten Betrieb Cookies. 15.10.2019 09:18