c# write StorageFile 50 Mal langsamer als IsolatedStorageFile



windows:: storage (1)

Dies wird eine lange Antwort sein, die Antworten auf alle meine Fragen und Empfehlungen zu den zu verwendenden Methoden enthält.

Diese Antwort ist auch noch nicht fertig, aber nachdem ich schon 5 Seiten in Word geschrieben habe, dachte ich, ich werde den ersten Teil jetzt posten.

Nachdem ich 2160 Benchmarks durchgeführt und die gesammelten Daten verglichen und analysiert habe, bin ich mir ziemlich sicher, dass ich meine eigenen Fragen beantworten kann und zusätzliche Einblicke in die bestmögliche Performance von StorageFile (und IsolatedStorageFile) liefern kann.

(für Rohergebnisse und alle Benchmark-Methoden siehe Frage)

Lass uns die erste Frage sehen:

Warum ist await StreamReader.ReadToEndAsync() in jedem Benchmark konsistent langsamer als die nicht asynchrone Methode StreamReader.ReadToEnd() ?

Neil Turner schrieb in Kommentaren: "In einer Schleife zu warten, wird eine leichte Perf. Verursachen. Hit wegen der ständigen Kontextwechsel hin und her "

Ich erwartete einen leichten Performance-Hit, aber wir dachten beide nicht, dass es einen so großen Rückgang in jedem Benchmark mit erwarten würde. Analysieren wir den Performance-Hit von erwartet in einer Schleife.

Dazu vergleichen wir zunächst die Ergebnisse der Benchmarks b1 und b5 (und b2 als nicht miteinander zusammenhängender Best-Case-Vergleich) hier die wichtigen Teile der beiden Methoden:

//b1 
for (int i = 0; i < filepaths.Count; i++)
{
    StorageFile f = await data.GetFileAsync(filepaths[i]);
    using (var stream = await f.OpenStreamForReadAsync())
    {
        using (StreamReader r = new StreamReader(stream))
        {
            filecontent = await r.ReadToEndAsync();
        }
    }
}
//b5
for (int i = 0; i < filepaths.Count; i++)
{
    StorageFile f = await data.GetFileAsync(filepaths[i]);
    using (var stream = await f.OpenStreamForReadAsync())
    {
        using (StreamReader r = new StreamReader(stream))
        {
            filecontent = r.ReadToEnd();
        }
    }
}

Benchmark-Ergebnisse:

50 Dateien, 100kb:

B1: 2651ms

B5: 1553ms

B2: 147

200 Dateien, 1kb

B1: 9984ms

B5: 6572

B2: 87

In beiden Szenarien dauert B5 ungefähr 2/3 der Zeit, die B1 benötigt, mit nur 2 erwartet in einer Schleife vs 3 erwartet in B1. Es scheint, dass das tatsächliche Laden von sowohl b1 als auch b5 ungefähr das gleiche wie in b2 sein könnte und nur die Wartezeiten verursachen den enormen Leistungsabfall (wahrscheinlich wegen der Kontextumschaltung) (Annahme 1).

Versuchen wir zu berechnen, wie lange ein Kontextwechsel dauert (mit b1) und prüfen Sie dann, ob Annahme 1 korrekt war.

Mit 50 Dateien und 3 erwartet, haben wir 150 Kontext-Schalter: (2651ms-147ms) / 150 = 16.7ms für einen Kontextwechsel. Können wir das bestätigen? :

B5, 50 Dateien: 16.7ms * 50 * 2 = 1670ms + 147ms = 1817ms vs Benchmarks Ergebnisse: 1553ms

B1, 200 Dateien: 16.7ms * 200 * 3 = 10020ms + 87ms = 10107ms vs 9984ms

B5, 200 Dateien: 16.7ms * 200 * 2 = 6680ms + 87ms = 6767ms vs 6572ms

Es scheint ziemlich vielversprechend zu sein mit nur relativ kleinen Unterschieden, die auf eine Fehlerquote bei den Benchmark-Ergebnissen zurückzuführen sind.

Benchmark (erwartet, Dateien): Berechnung vs. Benchmark-Ergebnisse

B7 (1 erwarten, 50 Dateien): 16.7ms * 50 + 147 = 982ms vs 899ms

B7 (1 erwarten, 200 Dateien): 16,7 * 200 + 87 = 3427ms vs 3354ms

B12 (1 erwartet, 50 Dateien): 982ms vs 897ms

B12 (1 erwartet, 200 Dateien): 3427ms vs 3348ms

B9 (3 erwartet, 50 Dateien): 2652ms vs 2526ms

B9 (3 erwartet, 200 Dateien): 10107ms vs 10014ms

Ich denke, mit diesen Ergebnissen ist es sicher zu sagen, dass ein Kontextwechsel etwa 16,7 ms dauert (zumindest in einer Schleife).

Mit dieser Aufklärung machen einige der Benchmark-Ergebnisse viel mehr Sinn. In Benchmarks mit 3 erwarteten Ergebnissen sehen wir meistens nur einen Unterschied von 0,1% bei den Ergebnissen verschiedener Dateigrößen (1, 20, 100). Das ist der absolute Unterschied, den wir in unserem Referenz-Benchmark b2 beobachten können.

Fazit: awaits in loops sind wirklich sehr schlecht (wenn die Schleife im ui-Thread ausgeführt wird, komme ich später dazu)

Auf Frage Nummer 2

Es scheint einen großen Overhead zu geben, wenn eine Datei mit StorageFile geöffnet wird, aber nur, wenn sie im UI-Thread geöffnet wird. (Warum?)

Betrachten wir Benchmark 10 und 19:

//b10
for (int i = 0; i < filepaths.Count; i++)
{
    using (var stream = new IsolatedStorageFileStream("/benchmarks/samplefiles/" + filepaths[i], FileMode.Open, store))
    {
        using (StreamReader r = new StreamReader(stream))
        {
            filecontent = await Task.Factory.StartNew<String>(() => { return r.ReadToEnd(); });
        }
    }
}
//b19
await await Task.Factory.StartNew(async () =>
{
    for (int i = 0; i < filepaths.Count; i++)
    {
        using (var stream = new IsolatedStorageFileStream("/benchmarks/samplefiles/" + filepaths[i], FileMode.Open, store))
        {
            using (StreamReader r = new StreamReader(stream))
            {
                filecontent = await Task.Factory.StartNew<String>(() => { return r.ReadToEnd(); });
            }
        }
    }
});

Benchmarks (1kb, 20kb, 100kb, 1mb) in ms:

10: (846, 865, 916, 1564)

19: (35, 57, 166, 1438)

In Benchmark 10 sehen wir erneut einen großen Performance-Hit beim Kontextwechsel. Wenn wir jedoch die for-Schleife in einem anderen Thread ausführen (b19), erhalten wir fast die gleiche Leistung wie bei unserem Referenzbenchmark 2 (Ui blockiert IsolatedStorageFile). Theoretisch sollte es immer noch Kontextwechsel geben (zumindest nach meinem Wissen). Ich vermute, dass der Compiler den Code in dieser Situation optimiert, dass es keine Kontextwechsel gibt.

In der Tat erhalten wir fast die gleiche Leistung wie in Benchmark 20, was im Grunde dasselbe ist wie Benchmark 10, aber mit ConfigureAwait (false):

filecontent = await Task.Factory.StartNew<String>(() => { return r.ReadToEnd(); }).ConfigureAwait(false);

20: (36, 55, 168, 1435)

Dies scheint nicht nur für neue Aufgaben der Fall zu sein, sondern für jede asynchrone Methode (zumindest für alles, was ich getestet habe)

Die Antwort auf diese Frage ist also die Kombination von Antwort 1 und dem, was wir gerade herausgefunden haben:

Der große Overhead ist wegen der Kontextwechsel, aber in einem anderen Thread treten entweder keine Kontextwechsel auf oder es gibt keinen durch sie verursachten Overhead. (Dies gilt natürlich nicht nur für das Öffnen einer Datei, wie in der Frage, sondern für jede asynchrone Methode)

Frage 3

Frage 3 kann nicht wirklich vollständig beantwortet werden, es kann immer Wege geben, die unter bestimmten Bedingungen etwas schneller sind, aber wir können zumindest sagen, dass einige Methoden niemals verwendet werden sollten und die beste Lösung für die häufigsten Fälle aus den Daten finden. Ich habe gesammelt:

StreamReader.ReadToEndAsync wir uns zuerst StreamReader.ReadToEndAsync und Alternativen an. Dafür können wir Benchmark 7 und Benchmark 10 vergleichen

Sie unterscheiden sich nur in einer Zeile:

b7:

filecontent = await r.ReadToEndAsync();

b10:

filecontent = await Task.Factory.StartNew<String>(() => { return r.ReadToEnd(); });

Sie könnten denken, dass sie ähnlich gut oder schlecht abschneiden würden und dass Sie (zumindest in einigen Fällen) falsch liegen würden.

Als ich zuerst an diesen Test dachte, ReadToEndAsync() ich, dass ReadToEndAsync() so implementiert werden würde.

Benchmarks:

b7: (848, 853, 899, 3386)

b10: (846, 865, 916, 1564)

Wir können deutlich sehen, dass in dem Fall, in dem die meiste Zeit mit dem Lesen der Datei verbracht wird, die zweite Methode viel schneller ist.

Meine Empfehlung:

Verwenden Sie nicht ReadToEndAsync() sondern schreiben Sie sich selbst eine ReadToEndAsync() Erweiterungsmethode:

public static async Task<String> ReadToEndAsyncThread(this StreamReader reader)
{
    return await Task.Factory.StartNew<String>(() => { return reader.ReadToEnd(); });
}

Verwenden Sie dieses immer anstelle von ReadToEndAsync() .

Das sieht man noch besser beim Vergleich von Benchmark 8 und 19 (Benchmark 7 und 10, wobei die for-Schleife in einem anderen Thread ausgeführt wird:

b8: (55, 103, 360, 3252)

b19: (35, 57, 166, 1438)

b6: (35, 55, 163, 1374)

In beiden Fällen gibt es keinen Overhead durch Kontextwechsel und Sie können deutlich sehen, dass die Leistung von ReadToEndAsync() absolut schrecklich ist. (Benchmark 6 ist auch fast identisch mit 8 und 19, aber mit filecontent = r.ReadToEnd(); Skalieren auf 10 Dateien mit 10mb)

Wenn wir dies mit unserer Referenz-ui-Blockierungsmethode vergleichen:

b2: (21, 44, 147, 1365)

Wir können sehen, dass sowohl Benchmark 6 als auch 19 sehr nahe an die gleiche Leistung herankommen, ohne den UI-Thread zu blockieren. Können wir die Leistung noch verbessern? Ja, aber nur marginal bei paralleler Beladung:

b14: ​​(36, 45, 133, 1074)

b16: (31, 52, 141, 1086)

Wenn Sie sich diese Methoden ansehen, sind sie nicht sehr hübsch und schreiben, dass überall, wo Sie etwas laden müssen, schlechtes Design wäre. Dafür habe ich die Methode ReadFile(string filepath) , die für einzelne Dateien verwendet werden kann, in normalen Schleifen mit 1 await und in loops mit parallelem Laden. Dies sollte eine wirklich gute Leistung liefern und zu leicht wiederverwendbarem und wartbarem Code führen:

public async Task<String> ReadFile(String filepath)
{
    return await await Task.Factory.StartNew<Task<String>>(async () =>
        {
            String filec = "";
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                using (var stream = new IsolatedStorageFileStream(filepath, FileMode.Open, store))
                {
                    using (StreamReader r = new StreamReader(stream))
                    {
                        filec = await r.ReadToEndAsyncThread();
                    }
                }
            }
            return filec;
        });
}

Hier sind einige Benchmarks (verglichen mit Benchmark 16) (für diesen Benchmark hatte ich einen separaten Benchmark-Lauf, bei dem ich die MEDIAN-Zeit (nicht den Durchschnitt) von 100 Läufen jeder Methode genommen habe):

b16: (16, 32, 122, 1197)

B22: (59, 81, 219, 1516)

b23: (50, 48, 160, 1015)

b24: (34, 50, 87, 1002)

(Der Median in all diesen Methoden liegt sehr nahe am Durchschnitt, wobei der Durchschnitt manchmal etwas langsamer, manchmal schneller ist. Die Daten sollten vergleichbar sein.)

(Bitte beachten Sie, dass obwohl die Werte den Median von 100 Läufen haben, die Daten im Bereich von 0-100ms nicht wirklich vergleichbar sind. ZB in den ersten 100 Läufen hatte der Benchmark 24 einen Median von 1002ms, in den zweiten 100 Läufen , 899ms.)

Benchmark 22 ist vergleichbar mit Benchmark 19. Benchmark 23 und 24 sind vergleichbar mit Benchmark 14 und 16.

Ok, jetzt sollte dies einer der besten Wege sein, um die Dateien zu lesen, wenn IsolatedStorageFile verfügbar ist.

Ich werde eine ähnliche Analyse für StorageFile für Situationen hinzufügen, in denen Sie nur StorageFile verfügbar haben (Freigabe von Code mit Windows 8 Apps).

Und weil ich daran interessiert bin, wie StorageFile unter Windows 8 funktioniert, werde ich wahrscheinlich auch alle StorageFile-Methoden auf meinem Windows 8-Rechner testen. (obwohl ich dafür wahrscheinlich keine Analyse schreiben werde)

Ich habe nur mehrere Algorithmen verglichen, um den schnellsten Weg zu finden, alle Daten in meine App zu laden, als ich herausfand, dass die WP7-Version meiner App auf meinem Lumia 920 die Daten 2 Mal so schnell lädt wie die WP8-Version auf demselben Gerät.

Ich schrieb dann den folgenden unabhängigen Code, um die Leistung der StorageFile von WP8 und der IsolatedStorageFile von WP7 zu testen.

Um den Titel zu verdeutlichen, hier meine vorläufigen Benchmark-Ergebnisse, die ich gelesen habe: 50 Dateien mit 20 kb und 100 kb gelesen:

Für den Code siehe unten

Aktualisieren

Nachdem ich heute einige Stunden lang einige Benchmarks und einige interessante Ergebnisse gemacht habe, lassen Sie mich meine Fragen formulieren:

  1. Warum ist await StreamReader.ReadToEndAsync() in jedem Benchmark konsistent langsamer als die nicht asynchrone Methode StreamReader.ReadToEnd() ? (Dies könnte bereits in einem Kommentar von Neil Turner beantwortet werden)

  2. Es scheint einen großen Overhead zu geben, wenn eine Datei mit StorageFile geöffnet wird, aber nur, wenn sie im UI-Thread geöffnet wird. (Siehe Ladezeitunterschiede zwischen Methode 1 und 3 oder zwischen 5 und 6, wobei 3 und 6 etwa 10-mal schneller sind als die äquivalente UI-Thread-Methode)

  3. Gibt es andere Möglichkeiten, die möglicherweise schnelleren Dateien zu lesen?

Update 3

Nun, mit diesem Update habe ich 10 weitere Algorithmen hinzugefügt und jeden Algorithmus mit jeder zuvor verwendeten Dateigröße und Anzahl der verwendeten Dateien neu gestartet. Diesmal wurde jeder Algorithmus 10 Mal ausgeführt. Die Rohdaten in der Excel-Datei sind also ein Durchschnitt dieser Läufe. Da es nun 18 Algorithmen gibt, die jeweils mit 4 Dateigrößen (1kb, 20kb, 100kb, 1mb) für jeweils 50, 100 und 200 Dateien (18 * 4 * 3 = 216) getestet wurden, gab es insgesamt 2160 Benchmarkläufe, eine Gesamtzeit von 95 Minuten (rohe Laufzeit).

Update 5

Die Benchmarks 25, 26, 27 und die ReadStorageFile Methode wurden ReadStorageFile . Musste etwas Text entfernen, da der Beitrag über 30000 Zeichen hatte, was anscheinend das Maximum ist. Die Excel-Datei wurde mit neuen Daten, neuer Struktur, Vergleichen und neuen Diagrammen aktualisiert.

Der Code:

public async Task b1LoadDataStorageFileAsync()
{
    StorageFolder data = await ApplicationData.Current.LocalFolder.GetFolderAsync("benchmarks");
    data = await data.GetFolderAsync("samplefiles");
    //b1 
    for (int i = 0; i < filepaths.Count; i++)
    {
        StorageFile f = await data.GetFileAsync(filepaths[i]);
        using (var stream = await f.OpenStreamForReadAsync())
        {
            using (StreamReader r = new StreamReader(stream))
            {
                filecontent = await r.ReadToEndAsync();
            }
        }
    }
}
public async Task b2LoadDataIsolatedStorage()
{
    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
    {
        for (int i = 0; i < filepaths.Count; i++)
        {
            using (var stream = new IsolatedStorageFileStream("/benchmarks/samplefiles/" + filepaths[i], FileMode.Open, store))
            {
                using (StreamReader r = new StreamReader(stream))
                {
                    filecontent = r.ReadToEnd();
                }
            }
        }
    }
    await TaskEx.Delay(0);
}

public async Task b3LoadDataStorageFileAsyncThread()
{
    StorageFolder data = await ApplicationData.Current.LocalFolder.GetFolderAsync("benchmarks");
    data = await data.GetFolderAsync("samplefiles");

    await await Task.Factory.StartNew(async () =>
    {
        for (int i = 0; i < filepaths.Count; i++)
        {

            StorageFile f = await data.GetFileAsync(filepaths[i]);
            using (var stream = await f.OpenStreamForReadAsync())
            {
                using (StreamReader r = new StreamReader(stream))
                {
                    filecontent = await r.ReadToEndAsync();
                }
            }
        }
    });
}
public async Task b4LoadDataStorageFileThread()
{
    StorageFolder data = await ApplicationData.Current.LocalFolder.GetFolderAsync("benchmarks");
    data = await data.GetFolderAsync("samplefiles");

    await await Task.Factory.StartNew(async () =>
    {
        for (int i = 0; i < filepaths.Count; i++)
        {

            StorageFile f = await data.GetFileAsync(filepaths[i]);
            using (var stream = await f.OpenStreamForReadAsync())
            {
                using (StreamReader r = new StreamReader(stream))
                {
                    filecontent = r.ReadToEnd();
                }
            }
        }
    });
}
public async Task b5LoadDataStorageFile()
{
    StorageFolder data = await ApplicationData.Current.LocalFolder.GetFolderAsync("benchmarks");
    data = await data.GetFolderAsync("samplefiles");
    //b5
    for (int i = 0; i < filepaths.Count; i++)
    {
        StorageFile f = await data.GetFileAsync(filepaths[i]);
        using (var stream = await f.OpenStreamForReadAsync())
        {
            using (StreamReader r = new StreamReader(stream))
            {
                filecontent = r.ReadToEnd();
            }
        }
    }
}
public async Task b6LoadDataIsolatedStorageThread()
{
    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
    {
        await Task.Factory.StartNew(() =>
            {
                for (int i = 0; i < filepaths.Count; i++)
                {
                    using (var stream = new IsolatedStorageFileStream("/benchmarks/samplefiles/" + filepaths[i], FileMode.Open, store))
                    {
                        using (StreamReader r = new StreamReader(stream))
                        {
                            filecontent = r.ReadToEnd();
                        }
                    }
                }
            });
    }
}
public async Task b7LoadDataIsolatedStorageAsync()
{
    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
    {
        for (int i = 0; i < filepaths.Count; i++)
        {
            using (var stream = new IsolatedStorageFileStream("/benchmarks/samplefiles/" + filepaths[i], FileMode.Open, store))
            {
                using (StreamReader r = new StreamReader(stream))
                {
                    filecontent = await r.ReadToEndAsync();
                }
            }
        }
    }
}
public async Task b8LoadDataIsolatedStorageAsyncThread()
{
    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
    {
        await await Task.Factory.StartNew(async () =>
        {
            for (int i = 0; i < filepaths.Count; i++)
            {
                using (var stream = new IsolatedStorageFileStream("/benchmarks/samplefiles/" + filepaths[i], FileMode.Open, store))
                {
                    using (StreamReader r = new StreamReader(stream))
                    {
                        filecontent = await r.ReadToEndAsync();
                    }
                }
            }
        });
    }
}


public async Task b9LoadDataStorageFileAsyncMy9()
{
    StorageFolder data = await ApplicationData.Current.LocalFolder.GetFolderAsync("benchmarks");
    data = await data.GetFolderAsync("samplefiles");

    for (int i = 0; i < filepaths.Count; i++)
    {
        StorageFile f = await data.GetFileAsync(filepaths[i]);
        using (var stream = await f.OpenStreamForReadAsync())
        {
            using (StreamReader r = new StreamReader(stream))
            {
                filecontent = await Task.Factory.StartNew<String>(() => { return r.ReadToEnd(); });
            }
        }
    }
}

public async Task b10LoadDataIsolatedStorageAsyncMy10()
{
    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
    {
        //b10
        for (int i = 0; i < filepaths.Count; i++)
        {
            using (var stream = new IsolatedStorageFileStream("/benchmarks/samplefiles/" + filepaths[i], FileMode.Open, store))
            {
                using (StreamReader r = new StreamReader(stream))
                {
                    filecontent = await Task.Factory.StartNew<String>(() => { return r.ReadToEnd(); });
                }
            }
        }
    }
}
public async Task b11LoadDataStorageFileAsyncMy11()
{
    StorageFolder data = await ApplicationData.Current.LocalFolder.GetFolderAsync("benchmarks");
    data = await data.GetFolderAsync("samplefiles");

    for (int i = 0; i < filepaths.Count; i++)
    {
        await await Task.Factory.StartNew(async () =>
            {
                StorageFile f = await data.GetFileAsync(filepaths[i]);
                using (var stream = await f.OpenStreamForReadAsync())
                {
                    using (StreamReader r = new StreamReader(stream))
                    {
                        filecontent = r.ReadToEnd();
                    }
                }
            });
    }
}

public async Task b12LoadDataIsolatedStorageMy12()
{
    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
    {
        for (int i = 0; i < filepaths.Count; i++)
        {
            await Task.Factory.StartNew(() =>
                {
                    using (var stream = new IsolatedStorageFileStream("/benchmarks/samplefiles/" + filepaths[i], FileMode.Open, store))
                    {
                        using (StreamReader r = new StreamReader(stream))
                        {
                            filecontent = r.ReadToEnd();
                        }
                    }
                });
        }
    }
}

public async Task b13LoadDataStorageFileParallel13()
{
    StorageFolder data = await ApplicationData.Current.LocalFolder.GetFolderAsync("benchmarks");
    data = await data.GetFolderAsync("samplefiles");
    List<Task> tasks = new List<Task>();
    for (int i = 0; i < filepaths.Count; i++)
    {
        int index = i;
        var task = await Task.Factory.StartNew(async () =>
        {
            StorageFile f = await data.GetFileAsync(filepaths[index]);
            using (var stream = await f.OpenStreamForReadAsync())
            {
                using (StreamReader r = new StreamReader(stream))
                {
                    String content = r.ReadToEnd();
                    if (content.Length == 0)
                    {
                        //just some code to ensure this is not removed by optimization from the compiler
                        //because "content" is not used otherwise
                        //should never be called
                        ShowNotificationText(content);
                    }
                }
            }
        });
        tasks.Add(task);
    }
    await TaskEx.WhenAll(tasks);
}

public async Task b14LoadDataIsolatedStorageParallel14()
{
    List<Task> tasks = new List<Task>();
    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
    {
        for (int i = 0; i < filepaths.Count; i++)
        {
            int index = i;
            var t = Task.Factory.StartNew(() =>
            {
                using (var stream = new IsolatedStorageFileStream("/benchmarks/samplefiles/" + filepaths[index], FileMode.Open, store))
                {
                    using (StreamReader r = new StreamReader(stream))
                    {
                        String content = r.ReadToEnd();
                        if (content.Length == 0)
                        {
                            //just some code to ensure this is not removed by optimization from the compiler
                            //because "content" is not used otherwise
                            //should never be called
                            ShowNotificationText(content);
                        }
                    }
                }
            });
            tasks.Add(t);
        }
        await TaskEx.WhenAll(tasks);
    }
}

public async Task b15LoadDataStorageFileParallelThread15()
{
    StorageFolder data = await ApplicationData.Current.LocalFolder.GetFolderAsync("benchmarks");
    data = await data.GetFolderAsync("samplefiles");

    await await Task.Factory.StartNew(async () =>
        {
            List<Task> tasks = new List<Task>();
            for (int i = 0; i < filepaths.Count; i++)
            {
                int index = i;
                var task = await Task.Factory.StartNew(async () =>
                {
                    StorageFile f = await data.GetFileAsync(filepaths[index]);
                    using (var stream = await f.OpenStreamForReadAsync())
                    {
                        using (StreamReader r = new StreamReader(stream))
                        {
                            String content = r.ReadToEnd();
                            if (content.Length == 0)
                            {
                                //just some code to ensure this is not removed by optimization from the compiler
                                //because "content" is not used otherwise
                                //should never be called
                                ShowNotificationText(content);
                            }
                        }
                    }
                });
                tasks.Add(task);
            }
            await TaskEx.WhenAll(tasks);
        });
}

public async Task b16LoadDataIsolatedStorageParallelThread16()
{
    await await Task.Factory.StartNew(async () =>
        {
            List<Task> tasks = new List<Task>();
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                for (int i = 0; i < filepaths.Count; i++)
                {
                    int index = i;
                    var t = Task.Factory.StartNew(() =>
                    {
                        using (var stream = new IsolatedStorageFileStream("/benchmarks/samplefiles/" + filepaths[index], FileMode.Open, store))
                        {
                            using (StreamReader r = new StreamReader(stream))
                            {
                                String content = r.ReadToEnd();
                                if (content.Length == 0)
                                {
                                    //just some code to ensure this is not removed by optimization from the compiler
                                    //because "content" is not used otherwise
                                    //should never be called
                                    ShowNotificationText(content);
                                }
                            }
                        }
                    });
                    tasks.Add(t);
                }
                await TaskEx.WhenAll(tasks);
            }
        });
}
public async Task b17LoadDataStorageFileParallel17()
{
    StorageFolder data = await ApplicationData.Current.LocalFolder.GetFolderAsync("benchmarks");
    data = await data.GetFolderAsync("samplefiles");
    List<Task<Task>> tasks = new List<Task<Task>>();
    for (int i = 0; i < filepaths.Count; i++)
    {
        int index = i;
        var task = Task.Factory.StartNew<Task>(async () =>
        {
            StorageFile f = await data.GetFileAsync(filepaths[index]);
            using (var stream = await f.OpenStreamForReadAsync())
            {
                using (StreamReader r = new StreamReader(stream))
                {
                    String content = r.ReadToEnd();
                    if (content.Length == 0)
                    {
                        //just some code to ensure this is not removed by optimization from the compiler
                        //because "content" is not used otherwise
                        //should never be called
                        ShowNotificationText(content);
                    }
                }
            }
        });
        tasks.Add(task);
    }
    await TaskEx.WhenAll(tasks);
    List<Task> tasks2 = new List<Task>();
    foreach (var item in tasks)
    {
        tasks2.Add(item.Result);
    }
    await TaskEx.WhenAll(tasks2);
}

public async Task b18LoadDataStorageFileParallelThread18()
{
    StorageFolder data = await ApplicationData.Current.LocalFolder.GetFolderAsync("benchmarks");
    data = await data.GetFolderAsync("samplefiles");

    await await Task.Factory.StartNew(async () =>
    {
        List<Task<Task>> tasks = new List<Task<Task>>();
        for (int i = 0; i < filepaths.Count; i++)
        {
            int index = i;
            var task = Task.Factory.StartNew<Task>(async () =>
            {
                StorageFile f = await data.GetFileAsync(filepaths[index]);
                using (var stream = await f.OpenStreamForReadAsync())
                {
                    using (StreamReader r = new StreamReader(stream))
                    {
                        String content = r.ReadToEnd();
                        if (content.Length == 0)
                        {
                            //just some code to ensure this is not removed by optimization from the compiler
                            //because "content" is not used otherwise
                            //should never be called
                            ShowNotificationText(content);
                        }
                    }
                }
            });
            tasks.Add(task);
        }
        await TaskEx.WhenAll(tasks);
        List<Task> tasks2 = new List<Task>();
        foreach (var item in tasks)
        {
            tasks2.Add(item.Result);
        }
        await TaskEx.WhenAll(tasks2);
    });
}
public async Task b19LoadDataIsolatedStorageAsyncMyThread()
{
    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
    {
        //b19
        await await Task.Factory.StartNew(async () =>
        {
            for (int i = 0; i < filepaths.Count; i++)
            {
                using (var stream = new IsolatedStorageFileStream("/benchmarks/samplefiles/" + filepaths[i], FileMode.Open, store))
                {
                    using (StreamReader r = new StreamReader(stream))
                    {
                        filecontent = await Task.Factory.StartNew<String>(() => { return r.ReadToEnd(); });
                    }
                }
            }
        });
    }
}

public async Task b20LoadDataIsolatedStorageAsyncMyConfigure()
{
    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
    {
        for (int i = 0; i < filepaths.Count; i++)
        {
            using (var stream = new IsolatedStorageFileStream("/benchmarks/samplefiles/" + filepaths[i], FileMode.Open, store))
            {
                using (StreamReader r = new StreamReader(stream))
                {
                    filecontent = await Task.Factory.StartNew<String>(() => { return r.ReadToEnd(); }).ConfigureAwait(false);
                }
            }
        }
    }
}
public async Task b21LoadDataIsolatedStorageAsyncMyThreadConfigure()
{
    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
    {
        await await Task.Factory.StartNew(async () =>
        {
            for (int i = 0; i < filepaths.Count; i++)
            {
                using (var stream = new IsolatedStorageFileStream("/benchmarks/samplefiles/" + filepaths[i], FileMode.Open, store))
                {
                    using (StreamReader r = new StreamReader(stream))
                    {
                        filecontent = await Task.Factory.StartNew<String>(() => { return r.ReadToEnd(); }).ConfigureAwait(false);
                    }
                }
            }
        });
    }
}
public async Task b22LoadDataOwnReadFileMethod()
{
    await await Task.Factory.StartNew(async () =>
    {
        for (int i = 0; i < filepaths.Count; i++)
        {
            filecontent = await ReadFile("/benchmarks/samplefiles/" + filepaths[i]);

        }
    });

}
public async Task b23LoadDataOwnReadFileMethodParallel()
{
    List<Task> tasks = new List<Task>();

    for (int i = 0; i < filepaths.Count; i++)
    {
        int index = i;
        var t = ReadFile("/benchmarks/samplefiles/" + filepaths[i]);
        tasks.Add(t);
    }
    await TaskEx.WhenAll(tasks);

}
public async Task b24LoadDataOwnReadFileMethodParallelThread()
{
    await await Task.Factory.StartNew(async () =>
        {
            List<Task> tasks = new List<Task>();

            for (int i = 0; i < filepaths.Count; i++)
            {
                int index = i;
                var t = ReadFile("/benchmarks/samplefiles/" + filepaths[i]);
                tasks.Add(t);
            }
            await TaskEx.WhenAll(tasks);

        });
}


public async Task b25LoadDataOwnReadFileMethodStorageFile()
{
    StorageFolder data = await ApplicationData.Current.LocalFolder.GetFolderAsync("benchmarks");
    data = await data.GetFolderAsync("samplefiles");
    await await Task.Factory.StartNew(async () =>
    {
        for (int i = 0; i < filepaths.Count; i++)
        {
            filecontent = await ReadStorageFile(data, filepaths[i]);

        }
    });

}
public async Task b26LoadDataOwnReadFileMethodParallelStorageFile()
{
    StorageFolder data = await ApplicationData.Current.LocalFolder.GetFolderAsync("benchmarks");
    data = await data.GetFolderAsync("samplefiles");
    List<Task> tasks = new List<Task>();

    for (int i = 0; i < filepaths.Count; i++)
    {
        int index = i;
        var t = ReadStorageFile(data, filepaths[i]);
        tasks.Add(t);
    }
    await TaskEx.WhenAll(tasks);

}
public async Task b27LoadDataOwnReadFileMethodParallelThreadStorageFile()
{
    StorageFolder data = await ApplicationData.Current.LocalFolder.GetFolderAsync("benchmarks");
    data = await data.GetFolderAsync("samplefiles");
    await await Task.Factory.StartNew(async () =>
    {
        List<Task> tasks = new List<Task>();

        for (int i = 0; i < filepaths.Count; i++)
        {
            int index = i;
            var t = ReadStorageFile(data, filepaths[i]);
            tasks.Add(t);
        }
        await TaskEx.WhenAll(tasks);

    });
}

public async Task b28LoadDataOwnReadFileMethodStorageFile()
{
    //StorageFolder data = await ApplicationData.Current.LocalFolder.GetFolderAsync("benchmarks");
    //data = await data.GetFolderAsync("samplefiles");
    await await Task.Factory.StartNew(async () =>
    {
        for (int i = 0; i < filepaths.Count; i++)
        {
            filecontent = await ReadStorageFile(ApplicationData.Current.LocalFolder, @"benchmarks\samplefiles\" + filepaths[i]);

        }
    });

}

public async Task<String> ReadStorageFile(StorageFolder folder, String filename)
{
    return await await Task.Factory.StartNew<Task<String>>(async () =>
    {
        String filec = "";
        StorageFile f = await folder.GetFileAsync(filename);
        using (var stream = await f.OpenStreamForReadAsync())
        {
            using (StreamReader r = new StreamReader(stream))
            {
                filec = await r.ReadToEndAsyncThread();
            }
        }
        return filec;
    });
}

public async Task<String> ReadFile(String filepath)
{
    return await await Task.Factory.StartNew<Task<String>>(async () =>
        {
            String filec = "";
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                using (var stream = new IsolatedStorageFileStream(filepath, FileMode.Open, store))
                {
                    using (StreamReader r = new StreamReader(stream))
                    {
                        filec = await r.ReadToEndAsyncThread();
                    }
                }
            }
            return filec;
        });
}

Wie diese Benchmarks ausgeführt werden:

public async Task RunBenchmark(String message, Func<Task> benchmarkmethod)
    {
        SystemTray.ProgressIndicator.IsVisible = true;
        SystemTray.ProgressIndicator.Text = message;
        SystemTray.ProgressIndicator.Value = 0;
        long milliseconds = 0;

        Stopwatch w = new Stopwatch();
        List<long> results = new List<long>(benchmarkruns);
        for (int i = 0; i < benchmarkruns; i++)
        {
            w.Reset();
            w.Start();
            await benchmarkmethod();
            w.Stop();
            milliseconds += w.ElapsedMilliseconds;
            results.Add(w.ElapsedMilliseconds);
            SystemTray.ProgressIndicator.Value += (double)1 / (double)benchmarkruns;
        }

        Log.Write("Fastest: " + results.Min(), "Slowest: " + results.Max(), "Average: " + results.Average(), "Median: " + results[results.Count / 2], "Maxdifference: " + (results.Max() - results.Min()),
                  "All results: " + results);


        ShowNotificationText((message + ":").PadRight(24) + (milliseconds / ((double)benchmarkruns)).ToString());
        SystemTray.ProgressIndicator.IsVisible = false;
    }

Benchmark-Ergebnisse

Hier ein Link zu den Rohbenchmarkdaten: http://www.dehodev.com/windowsphonebenchmarks.xlsx

Jetzt die Diagramme (jedes Diagramm zeigt die Daten für das Laden von 50 über jede Methode, Ergebnisse sind alle in Millisekunden)

Die nächsten Benchmarks mit 1 MB sind nicht wirklich repräsentativ für Apps. Ich füge sie hier ein, um einen besseren Überblick darüber zu geben, wie diese Methoden skalieren.

Um es zusammenzufassen: Die Standardmethode zum Lesen von Dateien (1.) ist immer die schlechteste (außer wenn Sie 50 10mb Dateien lesen wollen, aber selbst dann gibt es bessere Methoden).

Ich verbinde auch dieses: erwarten Sie AsyncMethod () gegen erwarten Sie warten Task.Factory.StartNew <TResult> (AsyncMethod) , in dem es argumentiert wird, dass es normalerweise nicht nützlich ist, eine neue Aufgabe hinzuzufügen. Die Ergebnisse, die ich hier sehe, sind jedoch, dass Sie das einfach nicht akzeptieren können und immer überprüfen sollten, ob das Hinzufügen einer Aufgabe die Leistung verbessert.

Und zum Schluss: Ich wollte dies im offiziellen Windows Phone-Entwicklerforum posten, aber jedes Mal, wenn ich es versuche, erhalte ich die Fehlermeldung "Unerwarteter Fehler" ...

Update 2

Schlussfolgerungen:

Nach der Überprüfung der Daten können Sie deutlich sehen, dass unabhängig von der Dateigröße jeder Algorithmus linear auf die Anzahl der Dateien skaliert. Um alles zu vereinfachen, können wir die Anzahl der Dateien ignorieren (wir verwenden nur die Daten für 50 Dateien in zukünftigen Vergleichen).

Jetzt zur Dateigröße: Dateigröße ist wichtig. Wir können sehen, dass die Algorithmen konvergieren, wenn wir die Dateigröße erhöhen. Bei einer Dateigröße von 10 MB findet der bisher langsamste Algorithmus 4 von 8 statt. Da sich diese Frage jedoch hauptsächlich mit Telefonen befasst, ist es unglaublich selten, dass Apps mehrere Dateien mit so vielen Daten lesen, selbst 1 MB-Dateien werden für die meisten Apps selten sein. Meine Vermutung ist, dass selbst das Lesen von 50 20kb Dateien ungewöhnlich ist. Die meisten Apps lesen wahrscheinlich Daten im Bereich von 10 bis 30 Dateien mit einer Größe von 0,5 KB bis 3 KB. (Dies ist nur eine Vermutung, aber ich denke, es könnte genau sein)





isolatedstoragefile