StorageFile比IsolatedStorageFile慢50倍

2024-05-01

当我发现在 Lumia 920 上运行的应用程序的 WP7 版本加载数据的速度是在同一设备上运行的 WP8 版本的 2 倍时,我正在对多种算法进行基准测试,以找到加载应用程序中所有数据的最快方法。

然后,我编写了以下独立代码来测试 WP8 中的 StorageFile 和 WP7 中的isolatedStorageFile 的性能。

为了澄清标题,这里是我读取 50 个 20kb 和 100kb 文件的初步基准测试结果:

代码见下文

Update

今天进行了几个小时的基准测试并获得了一些有趣的结果后,让我重新表述一下我的问题:

  1. Why is await StreamReader.ReadToEndAsync()在每个基准测试中始终比非异步方法慢StreamReader.ReadToEnd()? (尼尔·特纳的评论可能已经回答了这个问题)

  2. 使用StorageFile打开文件时似乎有很大的开销,但仅限于在UI线程中打开它时。 (参见方法 1 和 3 之间或 5 和 6 之间加载时间的差异,其中 3 和 6 比等效的 UI 线程方法快大约 10 倍)

  3. 有没有其他方法可以更快地读取文件?

Update 3

好吧,现在通过此更新,我添加了 10 个以上的算法,并使用以前使用的每个文件大小和使用的文件数量重新运行每个算法。这次每个算法运行 10 次。因此 Excel 文件中的原始数据是这些运行的平均值。由于现在有 18 种算法,每种算法都使用 4 种文件大小(1kb、20kb、100kb、1mb)进行测试,每种文件大小为 50、100 和 200 个文件(18*4*3 = 216),因此总共运行了 2160 次基准测试,总共花费 95 分钟(原始运行时间)。

Update 5

添加了基准 25、26、27 和ReadStorageFile方法。不得不删除一些文本,因为该帖子有超过 30000 个字符,这显然是最大值。使用新数据、新结构、比较和新图表更新了 Excel 文件。

代码:

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;
        });
}

这些基准测试是如何运行的:

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;
    }

基准测试结果

这里是原始基准数据的链接:http://www.dehodev.com/windowsphonebenchmarks.xlsx http://www.dehodev.com/windowsphonebenchmarks.xlsx

现在的图表(每张图表显示通过每种方法加载 50 次的数据,结果均以毫秒为单位)

下一个 1mb 的基准测试并不能真正代表应用程序。我将它们放在这里是为了更好地概述这些方法如何扩展。

总而言之:用于读取文件的标准方法 (1.) 总是最差的(除非您想读取 50 个 10mb 文件,但即使如此,也有更好的方法)。


我也链接这个:等待 AsyncMethod() 与等待等待 Task.Factory.StartNew(AsyncMethod) https://stackoverflow.com/questions/11531934/await-asyncmethod-versus-await-await-task-factory-startnewtresultasyncmetho,其中有人认为通常添加新任务是没有用的。然而,我在这里看到的结果是,您不能假设这一点,并且应该始终检查添加任务是否可以提高性能。

最后:我想在官方 Windows Phone 开发者论坛中发布此内容,但每次尝试时,我都会收到“意外错误”消息......

Update 2

结论:

查看数据后,您可以清楚地看到,无论文件大小如何,每种算法都与文件数量呈线性关系。因此,为了简化一切,我们可以忽略文件数量(在以后的比较中我们将仅使用 50 个文件的数据)。

现在讨论文件大小:文件大小很重要。我们可以看到,当我们增加文件大小时,算法开始收敛。在 10MB 文件大小时,之前最慢的算法发生了 8 个中的 4 个。但是,由于这个问题主要涉及手机,因此应用程序很少会读取包含这么多数据的多个文件,即使是 1MB 文件对于大多数应用程序来说也很少见。我的猜测是,即使读取 50 个 20kb 文件也不常见。大多数应用程序可能读取 10 到 30 个文件范围内的数据,每个文件大小为 0.5kb 到 3kb。 (这只是猜测,但我认为可能是准确的)


这将是一个很长的答案,其中包括我所有问题的答案以及有关使用方法的建议。

这个答案也还没有完成,但是在word已经有5页之后,我想我现在就发布第一部分。


在运行超过 2160 个基准测试、比较和分析收集的数据后,我非常确定我可以回答自己的问题,并提供有关如何获得 StorageFile(和isolatedStorageFile)最佳性能的更多见解

(有关原始结果和所有基准方法,请参阅问题)

我们看第一个问题:

Why is await StreamReader.ReadToEndAsync()始终较慢 每个基准都比非异步方法StreamReader.ReadToEnd()?

Neil Turner 在评论中写道:“循环等待会导致轻微的 性能。由于上下文不断来回切换而受到打击”

我预计性能会受到轻微影响,但我们都认为这不会导致每个等待基准测试都有如此大的下降。 让我们分析一下循环中的等待对性能的影响。

为此,我们首先比较基准 b1 和 b5 的结果(b2 作为不相关的最佳情况比较),这里是两种方法的重要部分:

//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();
        }
    }
}

基准测试结果:

50 个文件,100kb:

B1:2651毫秒

B5:1553毫秒

B2: 147

200 个文件,1kb

B1:9984ms

B5: 6572

B2: 87

在这两种情况下,B5 大约花费 B1 所花费时间的 2/3 左右,循环中只有 2 个等待,而 B1 中有 3 个等待。看起来 b1 和 b5 的实际加载可能与 b2 大致相同,只有等待导致性能大幅下降(可能是因为上下文切换)(假设 1)。

让我们尝试计算一次上下文切换需要多长时间(使用 b1),然后检查假设 1 是否正确。

有 50 个文件和 3 个等待,我们有 150 次上下文切换:一次上下文切换需要 (2651ms-147ms)/150 = 16.7ms。我们可以证实这一点吗? :

B5,50 个文件:16.7ms * 50 * 2 = 1670ms + 147ms = 1817ms 与基准测试结果:1553ms

B1、200个文件:16.7ms * 200 * 3 = 10020ms + 87ms = 10107ms vs 9984ms

B5,200个文件:16.7ms * 200 * 2 = 6680ms + 87ms = 6767ms vs 6572ms

看起来很有希望,只有相对较小的差异,这可能归因于基准结果中的误差幅度。

基准(等待、文件):计算与基准结果

B7(1 个等待,50 个文件):16.7ms*50 + 147= 982ms 与 899ms

B7(1 个等待,200 个文件):16.7*200+87 = 3427ms 与 3354ms

B12(1 个等待,50 个文件):982 毫秒 vs 897 毫秒

B12(1 个等待,200 个文件):3427 毫秒 vs 3348 毫秒

B9(3 个等待,50 个文件):2652 毫秒 vs 2526 毫秒

B9(3 个等待,200 个文件):10107ms 与 10014ms

我认为根据这个结果可以肯定地说,一次上下文切换大约需要 16.7 毫秒(至少在一个循环中)。

弄清楚这一点后,一些基准测试结果就更有意义了。在具有 3 个等待的基准测试中,我们大多数情况下看到不同文件大小(1、20、100)的结果仅存在 0.1% 的差异。这大约是我们在参考基准 b2 中可以观察到的绝对差异。

结论:循环中的等待真的很糟糕(如果循环是在 ui 线程中执行的,但我稍后会谈到)

关于问题 2

使用StorageFile打开文件时似乎有很大的开销, 但仅当它在 UI 线程中打开时。 (为什么?)

让我们看看基准 10 和 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(); });
            }
        }
    }
});

基准(1kb、20kb、100kb、1mb)(以毫秒为单位):

10:(846、865、916、1564)

19:(35、57、166、1438)

在基准测试 10 中,我们再次看到上下文切换对性能造成了巨大影响。然而,当我们在不同的线程中执行 for 循环 (b19) 时,我们获得的性能几乎与参考基准 2(Ui 阻塞isolatedStorageFile)相同。理论上仍然应该存在上下文切换(至少据我所知)。我怀疑编译器在没有上下文切换的情况下优化了代码。

事实上,我们获得了与基准测试 20 几乎相同的性能,它与基准测试 10 基本相同,但使用了ConfigureAwait(false):

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

20:(36、55、168、1435)

这似乎不仅适用于新任务,而且适用于每个异步方法(至少对于我测试的所有方法)

所以这个问题的答案是答案一和我们刚刚发现的内容的结合:

很大的开销是由于上下文切换造成的,但是在不同的线程中,要么不发生上下文切换,要么不存在由它们引起的开销。 (当然,这不仅适用于问题中所要求的打开文件,而且适用于每个异步方法)

问题3

问题 3 无法真正得到完全回答,在特定条件下总是有可能更快一点的方法,但我们至少可以告诉我们永远不应该使用某些方法,并从数据中找到最常见情况的最佳解决方案我收集:

我们先来看看StreamReader.ReadToEndAsync和替代方案。为此,我们可以比较基准 7 和基准 10

它们只有一行不同:

b7:

filecontent = await r.ReadToEndAsync();

b10:

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

你可能认为他们的表现会相似好坏,但你错了(至少在某些情况下)。

当我第一次想到做这个测试时,我想ReadToEndAsync()将以这种方式实施。

基准:

b7:(848、853、899、3386)

b10:(846、865、916、1564)

我们可以清楚地看到,在大部分时间都花在读取文件上的情况下,第二种方法要快得多。

我的建议:

不要使用ReadToEndAsync()但给自己写一个像这样的扩展方法:

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

始终使用这个而不是ReadToEndAsync().

在比较基准测试 8 和 19 时(即基准测试 7 和 10,for 循环在不同的线程中执行),您可以更清楚地看到这一点:

b8:(55、103、360、3252)

b19:(35、57、166、1438)

b6: (35, 55, 163, 1374)

在这两种情况下,上下文切换都没有开销,您可以清楚地看到,性能ReadToEndAsync()绝对是可怕的。 (基准 6 也几乎与 8 和 19 相同,但filecontent = r.ReadToEnd();。还可以扩展到 10 个 10mb 的文件)

如果我们将其与我们的参考 ui 阻塞方法进行比较:

b2:(21、44、147、1365)

我们可以看到,基准测试 6 和 19 都非常接近相同的性能,且不会阻塞 ui 线程。我们可以进一步提高性能吗?是的,但只有少量并行加载:

b14:(36、45、133、1074)

b16:(31、52、141、1086)

但是,如果您查看这些方法,您会发现它们不是很漂亮,并且在必须加载某些内容的任何地方编写都是糟糕的设计。为此我写了这个方法ReadFile(string filepath)它可用于单个文件、具有 1 个等待的正常循环和具有并行加载的循环。这应该能提供非常好的性能,并产生易于重用和维护的代码:

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;
        });
}

以下是一些基准测试(与基准测试 16 相比)(对于此基准测试,我进行了单独的基准测试运行,其中我从每种方法 100 次运行中获取了中值(而非平均)时间):

b16:(16、32、122、1197)

b22:(59、81、219、1516)

b23:(50、48、160、1015)

b24:(34、50、87、1002)

(所有这些方法的中位数非常接近平均值,平均值有时慢一点,有时快一点。数据应该具有可比性)

(请注意,尽管这些值是 100 次运行的中位数,但 0-100 毫秒范围内的数据实际上并不具有可比性。例如,在前 100 次运行中,基准测试 24 的中位数为 1002 毫秒,在后 100 次运行中,899 毫秒。)

基准 22 与基准 19 具有可比性。基准 23 和 24 与基准 14 和 16 具有可比性。

好的,现在这应该是当isolatedStorageFile可用时读取文件的最佳方法之一。

对于只有 StorageFile 可用的情况(与 Windows 8 应用程序共享代码),我将为 StorageFile 添加类似的分析。

因为我对 StorageFile 在 Windows 8 上的执行方式感兴趣,所以我可能也会在我的 Windows 8 计算机上测试所有 StorageFile 方法。 (尽管我可能不会写分析)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

StorageFile比IsolatedStorageFile慢50倍 的相关文章

随机推荐

  • 仅隐藏输入字段的内容

    我有一个与输入字段重叠的跨度 当您在输入字段中键入内容时 它会更新其内容 尽管我将跨度完美地定位在输入文本上 但您仍然可以看到文本更加粗体并且字母更粗 field nr 1 with span nr 2 without 我尝试隐藏整个输入字
  • 如何在 git log 中查看提交的 git bisect 状态?

    我正在做 git bisect 到目前为止我发现了一些好的和坏的提交 我可以通过运行来确认git bisect log 但是 如果我为我的分支运行 git loggit log graph decorate full origin mast
  • Windows API 代码包 - ShellFile 不生成 PDF 位图

    使用之前堆栈溢出问题中的代码 System Drawing Bitmap image ShellFile f ShellFile FromFilePath fileLocation image f Thumbnail ExtraLargeB
  • 如何在 Postgres 中对包含语义版本的列进行排序

    有没有办法高效存储https semver org https semver orgPostgres 中的版本字符串 然后执行最新查询 例如假设列的值为 1 1 0 1 0 0 1 2 0 1 0 1 rc 1 B001 我想排序并获取最新
  • Socket IO聊天例子很慢

    我是 Node js 和 Socket IO 的新手 我想尝试一下解释的示例 https socket io get started chat https socket io get started chat 我做了我必须做的一切 它起作用
  • 不同 Perl 安装造成的混乱

    我尝试升级 Perl 却把我的电脑搞得一团糟 我目前运行的是 RHEL6 5 64 位 事情是这样的 我安装了 perl 5 10 1 并且运行良好 这是安装好的 我可以从百胜看到它 我想安装 Padre 一个 Perl IDE 但这至少需
  • 删除查询字符串参数

    Is there any way to remove query string parameters from url in azure api management Thanks in advance 是的 您可以通过在发布者门户的操作屏
  • 角度锚标记单击的元素 ID 未显示

    我正在尝试获取单击的锚元素的 id 这是我的 HTML div class list group col md 4 a href class list group item img src book image alt p strong P
  • 如何在android文本视图的任意一端插入文本?

    是否可以有一个文本视图 其中在文本视图的最左侧包含一些文本 在文本视图的最右侧包含一些文本 例如 我想要一个应该像这样显示的文本视图 文字 a 可以使用单个 TextView 只需将文本放入 HTML 格式即可 例如
  • Firebase 动态链接无法获取参数

    我正在我的应用程序中处理邀请 我希望用户推荐其他人 推荐人将在他们推荐的人注册后获得奖励 我正在关注这个firebase 邀请教程 https firebase google com docs dynamic links use cases
  • 有没有办法运行查询并检查聚合数据而无需再次重新运行查询?

    在决定枚举 LINQ 查询的结果集之前 我想查看查询结果计数 可能还有其他聚合数据 几乎每个 LINQ 示例都会立即枚举 foreach 循环中的结果 但我想检查计数以确定是否需要循环每个结果 有没有办法运行查询并检查聚合数据而无需再次重新
  • jQuery 绑定 ajax:成功无法在 Rails 3 应用程序中为新创建的(ajax)项目工作

    编辑这篇文章是因为我发现问题实际上在于rails无法绑定到ajax success函数 使用导轨3 2 3 感谢您花时间阅读并尝试提供帮助 我在 ajax success 删除项目上添加一个简单的淡出功能 如下所示 document rea
  • 在所有设备上使用 jquery 垂直对齐?

    我发现在不更改其他元素布局的情况下垂直对齐页面上的某些元素是有问题的 这个问题在移动设备上更加严重 如此多的屏幕尺寸 分辨率 纵向 横向 var x window innerHeight myDiv height myDiv css mar
  • Android ArrayList 的 IndexOutOfBoundsException [重复]

    这个问题在这里已经有答案了 我遇到了一个非常烦人的问题 一些代码抛出 IndexOutOfBoundsException 我真的不明白为什么 logcat 指向以下代码的 addTimetableItem 我们将对此进行更多解释 if so
  • 测试涉及打开文件夹/工作区的 VSCode 扩展

    我正在开发一个 VSCode 扩展 它将当前在工作区中打开的文件的路径考虑在内 因此 为了进行可重现的测试 我尝试在 VSCode 中打开测试文件夹本身 然后打开其中的测试文件 如下所示 import as vscode from vsco
  • 学说和大量数据

    我有一个返回约 50k 行的查询 似乎学说将整个结果放入内存中超出了内存限制 128M 我发现节省一些内存的唯一解决方案是 result gt execute array Doctrine Core HYDRATE NONE 但还是超出了极
  • 在运行时拖动窗体上的控件

    我刚刚开始使用 WPF 但我正在尝试添加我的代码 来自 Winforms 使用户能够在运行时将任何控件拖动到他们想要的任何位置 但我似乎无法获取鼠标当前的位置 呃 没有鼠标位置 在 Mouse 事件中 您可以使用 e GetPosition
  • 如何让 CompletionService 知道项目中的其他文档?

    我正在构建一个允许用户定义 编辑和执行 C 脚本的应用程序 定义由方法名称 参数名称数组和方法的内部代码组成 例如 名称 脚本1 参数名称 arg1 arg2 代码 return Arg1 arg1 Arg2 arg2 基于这个定义 可以生
  • 安装 mariadb 包时找不到 mariadb_config [重复]

    这个问题在这里已经有答案了 我尝试在 ubuntu 20 04 上安装 mariadb 软件包 并收到以下消息 Collecting mariadb Using cached mariadb 1 1 3 tar gz 80 kB Prepa
  • StorageFile比IsolatedStorageFile慢50倍

    当我发现在 Lumia 920 上运行的应用程序的 WP7 版本加载数据的速度是在同一设备上运行的 WP8 版本的 2 倍时 我正在对多种算法进行基准测试 以找到加载应用程序中所有数据的最快方法 然后 我编写了以下独立代码来测试 WP8 中