Frage

Dies ist nur ein persönliches Projekt habe ich in gegraben. Im Grunde habe ich eine Textdatei zu analysieren (sagen wir von 20MB bis zu etwa 1 GB) mit Stream. Die Leistung ist ziemlich solide, aber immer noch ... Ich habe Juckreiz worden, um zu sehen, was passieren würde, wenn ich es in binären analysieren. Versteht mich nicht falsch, ich bin der Optimierung nicht vorzeitig. Ich bin auf jeden Fall Mikro-Optimierung absichtlich nur „zu sehen“.

Also, ich lese in der Textdatei Byte-Arrays verwenden. Kommen Sie, um herauszufinden, können neue Linien der (Windows) Standard CR / LF oder CR oder LF ... ziemlich chaotisch. Ich hatte in der Lage sein zu verwenden Array.indexOf auf CR gehofft und dann vorbei an der LF überspringen. Stattdessen finde ich mich Code sehr ähnlich IndexOf schreiben, aber entweder für die Überprüfung und die Rückkehr ein Array nach Bedarf.

So der Kern: mit sehr ähnlichen Code zu IndexOf, meinen Code endet noch irrsinnig langsamer sein werden. Um es in Perspektive mit einer 800MB-Datei:

  • Mit IndexOf und suchen nach CR: ~ 320 MB / s
  • Mit Stream und Readline: ~ 180 MB / s
  • für Schleife replizierende IndexOf: ~ 150 MB / s

Hier ist der Code mit dem for-Schleife (~ 150 MB / s):

IEnumerator<byte[]> IEnumerable<byte[]>.GetEnumerator() {
    using(FileStream fs = new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, _bufferSize)) {
        byte[] buffer = new byte[_bufferSize];
        int bytesRead;
        int overflowCount = 0;
        while((bytesRead = fs.Read(buffer, overflowCount, buffer.Length - overflowCount)) > 0) {
            int bufferLength = bytesRead + overflowCount;
            int lastPos = 0;
            for(int i = 0; i < bufferLength; i++) {
                if(buffer[i] == 13 || buffer[i] == 10) {
                    int length = i - lastPos;
                    if(length > 0) {
                        byte[] line = new byte[length];
                        Array.Copy(buffer, lastPos, line, 0, length);
                        yield return line;
                    }
                    lastPos = i + 1;
                }
            }
            if(lastPos > 0) {
                overflowCount = bufferLength - lastPos;
                Array.Copy(buffer, lastPos, buffer, 0, overflowCount);
            }
        }
    }
}

Dies ist der schnellere Codeblock (~ 320 MB / s):

while((bytesRead = fs.Read(buffer, overflowCount, buffer.Length - overflowCount)) > 0) {
    int bufferLength = bytesRead + overflowCount;
    int pos = 0;
    int lastPos = 0;
    while(pos < bufferLength && (pos = Array.IndexOf<byte>(buffer, 13, pos)) != -1) {
        int length = pos - lastPos;
        if(length > 0) {
            byte[] line = new byte[length];
            Array.Copy(buffer, lastPos, line, 0, length);
            yield return line;
        }
        if(pos < bufferLength - 1 && buffer[pos + 1] == 10)
            pos++;
        lastPos = ++pos;

    }
    if(lastPos > 0) {
        overflowCount = bufferLength - lastPos;
        Array.Copy(buffer, lastPos, buffer, 0, overflowCount);
    }
}

(Nein, es ist nicht die Produktion bereit, bestimmte Fälle werden es machen sprengen;. Ich benutze einen 128kb Puffergröße der meisten von denen zu ignorieren)

So ist meine große Frage ist, warum ... Array.indexOf so viel schneller funktioniert? Es ist im Wesentlichen der gleiche, ein for-Schleife ein Array Fuß. Gibt es etwas über die Art und Weise mscorlib Code ausgeführt? Auch die Änderung der oben genannten Code, um wirklich IndexOf und suchen nach nur CR zu replizieren und dann LF Überspringen wie ich würde, wenn IndexOf verwenden hilft nicht. Errr ... Ich habe vielleicht verschiedene Permutationen worden durchlaufen und es ist spät genug, dass es einige eklatante Fehler ist mir fehlt?

BTW, schaute ich in readline- und bemerkte, es wird ein Blockschalt eher als ein, wenn Block ... wenn ich etwas Ähnliches tun, bizarr genug tut es die Leistung erhöhen, indem sie über 15mb / s. Das ist eine andere Fragen für eine andere Zeit (warum wechseln schneller, als wenn?), Aber ich dachte, ich würde darauf hinweisen, dass ich es sah.

Auch teste ich eine Veröffentlichung außerhalb von VS bauen, damit es keine debuggery ist los.

War es hilfreich?

Lösung

Das ist eine gute Frage. Die kurze Version ist, dass es läuft alles auf die Umsetzung des IEqualityComparer gelegt, dass IndexOf verwenden. Lassen Sie sehen das folgende Stück Code:

using System;
using System.Collections.Generic;
using System.Diagnostics;

class Program {

    static int [] buffer = new int [1024];
    const byte mark = 42;
    const int iterations = 10000;

    static void Main ()
    {
        buffer [buffer.Length -1] = mark;

        Console.WriteLine (EqualityComparer<int>.Default.GetType ());

        Console.WriteLine ("Custom:  {0}", Time (CustomIndexOf));
        Console.WriteLine ("Builtin: {0}", Time (ArrayIndexOf));
    }

    static TimeSpan Time (Action action)
    {
        var watch = new Stopwatch ();
        watch.Start ();
        for (int i = 0; i < iterations; i++)
            action ();
        watch.Stop ();
        return watch.Elapsed;
    }

    static void CustomIndexOf ()
    {
        for (int i = 0; i < buffer.Length; i++)
            if (buffer [i] == mark)
                break;
    }

    static void ArrayIndexOf ()
    {
        Array.IndexOf (buffer, mark);
    }
}

Sie werden kompilieren müssen, um es mit csc / optimize + .

Hier ist das Ergebnis habe ich:

C:\Tmp>test
System.Collections.Generic.GenericEqualityComparer`1[System.Int32]
Custom:  00:00:00.0386403
Builtin: 00:00:00.0427903

Nun ändern Sie den Typ des Arrays und des EqualityComparer auf Byte, und hier ist das Ergebnis habe ich:

C:\Tmp>test
System.Collections.Generic.ByteEqualityComparer
Custom:  00:00:00.0387158
Builtin: 00:00:00.0165881

Wie Sie sehen können, ist der Byte-Array spezieller verrohrt, was wahrscheinlich einen Byte in einer Byte-Array finden optimiert ist. Da ich nicht das .net Framework dekompilieren kann, hielt ich das hier analysieren, aber ich denke, dass ein ziemlich guter Anhaltspunkt ist.

Andere Tipps

Die mscorlib Dateien werden während der Installation ngen'd. Versuchen Sie ngen'ing Ihre Datei mit Ngen.exe Dienstprogramm (vorausgesetzt, zusammen mit .NET framwork nehme ich an) ... und dann die Benchmarks überprüfen. Könnte etwas schneller sein.

Um Ihre .NET-Code laufen bei nahezu nativer Geschwindigkeit zu machen, empfiehlt Microsoft, dass Sie "Ngen" Code während der App-Installation ...

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top