StorageFile в 50 раз медленнее, чем IsolatedStorageFile
Я просто тестировал несколько алгоритмов, чтобы найти самый быстрый способ загрузки всех данных в мое приложение, когда обнаружил, что версия моего приложения WP7, работающая на Lumia 920, загружает данные в 2 раза быстрее, чем версия WP8, работающая на том же устройстве.
Затем я написал следующий независимый код для тестирования производительности StorageFile из WP8 и IsolatedStorageFile из WP7.
Чтобы уточнить заголовок, вот мои предварительные результаты тестов, которые я сделал, прочитав 50 файлов по 20 КБ и 100 КБ:
Код см. Ниже
ОбновитьПосле нескольких часов тестов и интересных результатов позвольте мне перефразировать мои вопросы:
Почемуawait StreamReader.ReadToEndAsync()
последовательно медленнее в каждом тесте, чем не асинхронный методStreamReader.ReadToEnd()
? (На это уже может быть ответ в комментарии Нила Тернера)
Кажется, что при открытии файла с StorageFile возникают большие издержки, но только когда он открывается в потоке пользовательского интерфейса. (См. Разницу во времени загрузки между методом 1 и 3 или между 5 и 6, где 3 и 6 примерно в 10 раз быстрее, чем эквивалентный метод потока пользовательского интерфейса)
Есть ли другие способы чтения файлов, которые могут быть быстрее?
Обновление 3Что ж, теперь с этим обновлением я добавил еще 10 алгоритмов, переназначая каждый алгоритм с каждым ранее использованным размером файла и количеством используемых файлов. На этот раз каждый алгоритм был запущен 10 раз. Таким образом, необработанные данные в файле Excel являются средним значением этих прогонов. Поскольку теперь существует 18 алгоритмов, каждый из которых протестирован с 4 размерами файлов (1 КБ, 20 КБ, 100 КБ, 1 МБ) для 50, 100 и 200 файлов каждый (18 * 4 * 3 = 216), было проведено 2160 тестов производительности, занимает общее время 95 минут (сырое время работы).
Обновление 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
Теперь графики (каждый график показывает данные для загрузки 50 с помощью каждого метода, результаты все в миллисекундах)
Следующие тесты с 1 МБ не очень характерны для приложений. Я включил их здесь, чтобы дать лучший обзор того, как масштабируются эти методы.
Итак, подведем итоги: стандартный метод, используемый для чтения файлов (1.), всегда худший (за исключением случая, когда вы хотите прочитать 50 файлов по 10 Мб, но даже тогда существуют более эффективные методы).
Я также связываю это:await AsyncMethod () и await await Task.Factory.StartNew <TResult> (AsyncMethod)где утверждается, что обычно не полезно добавлять новое задание. Однако результаты, которые я вижу здесь, таковы, что вы просто не можете это предположить и всегда должны проверять, повышает ли добавление задачи производительность.
И последнее: я хотел опубликовать это на официальном форуме разработчиков Windows Phone, но каждый раз, когда я пытаюсь, я получаю сообщение «Неожиданная ошибка» ...
Обновление 2Выводы:После просмотра данных вы можете ясно видеть, что независимо от размера файла каждый алгоритм масштабируется линейно по отношению к количеству файлов. Таким образом, чтобы упростить все, мы можем игнорировать количество файлов (мы будем просто использовать данные для 50 файлов в будущих сравнениях).
Теперь о размере файла: Размер файла важен. Мы видим, что когда мы увеличиваем размер файла, алгоритмы начинают сходиться. При размере файла 10 МБ предыдущий самый медленный алгоритм имеет место 4 из 8. Однако, поскольку этот вопрос в основном относится к телефонам, невероятно редко, когда приложения будут читать несколько файлов с таким большим количеством данных, даже файлы 1 МБ будут редкими для большинства приложений. Я предполагаю, что даже чтение 50 файлов размером 20 КБ является редкостью. Большинство приложений, вероятно, считывают данные в диапазоне от 10 до 30 файлов, каждый размером от 0,5 до 3 КБ. (Это только предположение, но я думаю, что это может быть точным)