Как определить оптимальное количество потоков и размер буфера при копировании файла?
Набросал небольшой тестик на предмет параллельного чтения \ записи из \ в файл.
Файл весит 640 мб. Использую размер буфера объемом 200 мб. Код и результаты выполнения примера приведены ниже.
Четыре потока одновременно читают и пишут: 6.2527444 секунд
Один поток читает и пишет: 26.7359741 секунд
Функция File.Copy: 21.6532898 секунд
Как видно из примера, при копировании больших объемов информации такой подход более чем обоснован. Но возникает вопрос - как определить оптимальное количество потоков? Возможно ли это вообще без сбора статистических данных на основании доступных сведений о хранилище? Размер буфера в этом случае, будет равен некоторому проценту (например 80%) от всего доступного свободного места в оперативке, деленному на количество потоков.
Кто-нибудь что-нибудь ещё может подсказать?
P.S. Если сюда случайно заглянет какой-нибудь .NET'овец: - подскажите с чего Dispose для последнего выходного потока вызывается до того, как закончится запись?
{
const string FilePath1 = @"S:\Input.iso";
const string FilePath2 = @"S:\1.iso";
const string FilePath3 = @"S:\2.iso";
const string FilePath4 = @"S:\3.iso";
const int BufferSize = 200 * 1024 * 1024;
Stopwatch sw1 = new Stopwatch();
sw1.Start();
using (FileStream input1 = new FileStream(FilePath1, FileMode.Open, FileAccess.Read, FileShare.Read))
using (FileStream input2 = new FileStream(FilePath1, FileMode.Open, FileAccess.Read, FileShare.Read))
using (FileStream input3 = new FileStream(FilePath1, FileMode.Open, FileAccess.Read, FileShare.Read))
using (FileStream input4 = new FileStream(FilePath1, FileMode.Open, FileAccess.Read, FileShare.Read))
using (FileStream output1 = new FileStream(FilePath2, FileMode.Create, FileAccess.Write, FileShare.Write))
using (FileStream output2 = new FileStream(FilePath2, FileMode.Open, FileAccess.Write, FileShare.Write))
using (FileStream output3 = new FileStream(FilePath2, FileMode.Open, FileAccess.Write, FileShare.Write))
using (FileStream output4 = new FileStream(FilePath2, FileMode.Open, FileAccess.Write, FileShare.Write))
{
input1.Position = output1.Position = 000 * 1024 * 1024;
input2.Position = output2.Position = 200 * 1024 * 1024;
input3.Position = output3.Position = 400 * 1024 * 1024;
input4.Position = output4.Position = 600 * 1024 * 1024;
byte[] buffer1 = new byte[BufferSize];
byte[] buffer2 = new byte[BufferSize];
byte[] buffer3 = new byte[BufferSize];
byte[] buffer4 = new byte[BufferSize];
Task<int> task1 = input1.ReadAsync(buffer1, 0, BufferSize);
Task<int> task2 = input1.ReadAsync(buffer2, 0, BufferSize);
Task<int> task3 = input1.ReadAsync(buffer3, 0, BufferSize);
Task<int> task4 = input1.ReadAsync(buffer4, 0, BufferSize);
task1.ContinueWith(task => output1.WriteAsync(buffer1, 0, task.Result));
task2.ContinueWith(task => output2.WriteAsync(buffer2, 0, task.Result));
task3.ContinueWith(task => output3.WriteAsync(buffer3, 0, task.Result));
task4.ContinueWith(task => output4.WriteAsync(buffer4, 0, task.Result));
Task.WaitAll(task1, task2, task3, task4);
Thread.Sleep(1); // Почему-то output4 закрывается до окончания записи, и ReSharper об этом предупреждает
}
sw1.Stop();
Console.WriteLine("Четыре потока одновременно читают и пишут: {0} секунд", sw1.Elapsed.TotalSeconds);
Stopwatch sw2 = new Stopwatch();
sw2.Start();
using (FileStream input = new FileStream(FilePath1, FileMode.Open, FileAccess.Read, FileShare.Read))
using (FileStream output = new FileStream(FilePath3, FileMode.Create, FileAccess.Write, FileShare.Write))
input.CopyTo(output, BufferSize);
sw2.Stop();
Console.WriteLine("Один поток читает и пишет: {0} секунд", sw2.Elapsed.TotalSeconds);
Stopwatch sw3 = new Stopwatch();
sw3.Start();
File.Copy(FilePath1, FilePath4);
sw3.Stop();
Console.WriteLine("Функция File.Copy: {0} секунд", sw3.Elapsed.TotalSeconds);
Console.ReadLine();
}
"подскажите с чего Dispose для последнего выходного потока вызывается до того, как закончится запись?" - это вам подскажет не .NET'овец , а любой системный программист : .NET - надстройка над апи . и когда вы пишете блок в файл на самом деле он просто передаётся кеширующему драйверу файловой системы .
"Но возникает вопрос - как определить оптимальное количество потоков?" - большое количество потоков выполняется псевдопараллельно . физически одновременно может выполнятся не более потоков чем имеется полноценных процессорных ядер .
"подскажите с чего Dispose для последнего выходного потока вызывается до того, как закончится запись?" - это вам подскажет не .NET'овец , а любой системный программист : .NET - надстройка над апи . и когда вы пишете блок в файл на самом деле он просто передаётся кеширующему драйверу файловой системы .
"Но возникает вопрос - как определить оптимальное количество потоков?" - большое количество потоков выполняется псевдопараллельно . физически одновременно может выполнятся не более потоков чем имеется полноценных процессорных ядер .
Хм. Я видел много твоих постов на форуме и это самый бесполезный из них, извини. :)
1) Да я в общем-то уверен, что и в Java и в C++ std::, и в Qt копирование реализовано точно также, а всем желающим предлагается написать собственную более эффективную реализацию. В конце концов, если у меня есть 1000 потоков по 256 байт и 1000 файлов по 1 кб, я совершенно точно не захочу писать каждый из них в 8 потоков, не говоря уже о том, что некоторые потоки (а Stream - асбтрактный класс) могут не реализовывать перемещение в произвольную позицию и совместную запись в один и тот же репозиторий. У File.Copy, конечно, могли бы сделать соответствующий флажок, но не сделали и ладно. Но всё равно как-то мимо кассы. В .NET есть многое, но это не значит, что там должно быть всё.
2) Ты безусловно совершенно прав. Но .NET - это умная надстройка над API и её асинхронные вызовы не исчезают в неизвестности, а всегда возвращаются с некоторым результатом, свидетельствующем о выполнении операции. Затем следует волшебная строчка:
3) Безусловно всё так. Но при копировании содержимого файла с диска на диск, узким местом является явно не процессор, а жесткий диск, который способен отдавать и получать данные существенно медленнее чем оперативная память, и в ожидании этого процессор спокойно может обслуживать и вдвое и втрое больше потоков, безболезненно переключаясь между ними. Поэтому интересует именно определение возможностей жесткого диска. (Если я конечно верно рассуждаю и одновременное копирование двух файлов размером 100Гб каждый в пределах одного и того же SSD размером 256Гб на одноядерном процессоре будет выполнено быстрее, чем их последовательное копирование).
я вам дал "волшебного пендаля" в нужном направлении по всем вашим вопросам .
так что за бесполезностью умываю руки . )
я вам дал "волшебного пендаля" в нужном направлении по всем вашим вопросам .
так что за бесполезностью умываю руки . )
Странная логика. Странные выводы. Обычно у тебя очень ценные ответы. Этот был наихудьшим, что я видел. Уверен, что ты отвечал, просто чтобы ответить, особенно не вчитываясь, и интерпретируя каждый вопрос по-своему. "Волшебный пендаль" был, но как-то сразу во все стороны. Он подошёл бы юному падавану, далёкому от небесных сфер, но никак не опытному программисту, которым мне хочется себя считать. :) Не обижайся. Если я не прав - аргументируй. Я свою критику аргументировал.
Копирование с использованием сопоставления с памятью: 11.3635306 секунд
Быть может, я чего-то не знаю о MMF? Пока я вижу только менее эффективное использование памяти и более медленное копирование.
P.S. И я всё ещё не понимаю, как это относится к моему вопросу, потому что любое чтение с диска остаётся чтением, а любая запись - записью, и возможности диска не увеличиваются.
Копирование с использованием сопоставления с памятью: 11.3635306 секунд
Быть может, я чего-то не знаю о MMF? Пока я вижу только менее эффективное использование памяти и более медленное копирование.
P.S. И я всё ещё не понимаю, как это относится к моему вопросу, потому что любое чтение с диска остаётся чтением, а любая запись - записью, и возможности диска не увеличиваются.
Быть может, вы осознаете разницу между вашими буферами и MMF, нэ?
PS: А на вопрос с кол-вом потоков вам уже ответили по большей части.
А что не нравится-то? Лучше этого — только 4 потока
А что не нравится-то? Лучше этого — только 4 потока
Мне всё нравится. Я не понимаю зачем мне предложили использовать MMF, который работает медленнее обычного 4х поточного копирования. Возможно, я его неправильно дёргаю.
PS: А на вопрос с кол-вом потоков вам уже ответили по большей части.
Ответа я не видел (только насчёт процессорных ядер, которые априори быстрее винта).
И нет, я не осознаю её, пока меня не ткнут носом в какой-нибудь внятный мануал, или не расскажут - чем же отличается считывание страницы файла с диска в память от чтения его буферами тех же размеров. Что -данные каким-то волшебным образом оказываются в памяти, не считываясь с диска? Магия существует? :D
Да данная тема с .NET вообще связана только P.S., на который всё равно никто ответить не смог. :) Просто код приведён на C#. Могу переписать на С++. Суть от этого не изменится.
Господа, очень хочется ответа по существу:
Либо такого: koderAlex прав, от хъарактеристик винта ничего не зависит и решает все только количество ядер.
Либо такого: дёрнув метод *** можно получить характиристики привода, и на основе *** и *** вычислить оптимальное число потоков копирования.
И, раз уж зашла речь про MMF, с которым я на практике действительно не имел дела (а потому, возможно, чего-то не понимаю и что-то делаю неверно) - то и разъяснений или вкусной ссылочки по нему.
http://msdn.microsoft.com/en-us/library/aa366556
Вкуснее некуда