Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Как определить оптимальное количество потоков и размер буфера при копировании файла?

62K
13 апреля 2013 года
LWhisper
33 / / 27.11.2012
Всем привет!

Набросал небольшой тестик на предмет параллельного чтения \ записи из \ в файл.
Файл весит 640 мб. Использую размер буфера объемом 200 мб. Код и результаты выполнения примера приведены ниже.

Четыре потока одновременно читают и пишут: 6.2527444 секунд
Один поток читает и пишет: 26.7359741 секунд
Функция File.Copy: 21.6532898 секунд

Как видно из примера, при копировании больших объемов информации такой подход более чем обоснован. Но возникает вопрос - как определить оптимальное количество потоков? Возможно ли это вообще без сбора статистических данных на основании доступных сведений о хранилище? Размер буфера в этом случае, будет равен некоторому проценту (например 80%) от всего доступного свободного места в оперативке, деленному на количество потоков.

Кто-нибудь что-нибудь ещё может подсказать?

P.S. Если сюда случайно заглянет какой-нибудь .NET'овец: - подскажите с чего Dispose для последнего выходного потока вызывается до того, как закончится запись?

Код:
static void Main(string[] args)
{
    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();
}
252
13 апреля 2013 года
koderAlex
1.4K / / 07.09.2005
"Как видно из примера, при копировании больших объемов информации такой подход более чем обоснован." - это просто доказывает , что .NET делали индо-китайцы по принципу "как-то работает ? ну и ладно.." . )
"подскажите с чего Dispose для последнего выходного потока вызывается до того, как закончится запись?" - это вам подскажет не .NET'овец , а любой системный программист : .NET - надстройка над апи . и когда вы пишете блок в файл на самом деле он просто передаётся кеширующему драйверу файловой системы .
"Но возникает вопрос - как определить оптимальное количество потоков?" - большое количество потоков выполняется псевдопараллельно . физически одновременно может выполнятся не более потоков чем имеется полноценных процессорных ядер .
62K
14 апреля 2013 года
LWhisper
33 / / 27.11.2012
Цитата: koderAlex
"Как видно из примера, при копировании больших объемов информации такой подход более чем обоснован." - это просто доказывает , что .NET делали индо-китайцы по принципу "как-то работает ? ну и ладно.." . )
"подскажите с чего Dispose для последнего выходного потока вызывается до того, как закончится запись?" - это вам подскажет не .NET'овец , а любой системный программист : .NET - надстройка над апи . и когда вы пишете блок в файл на самом деле он просто передаётся кеширующему драйверу файловой системы .
"Но возникает вопрос - как определить оптимальное количество потоков?" - большое количество потоков выполняется псевдопараллельно . физически одновременно может выполнятся не более потоков чем имеется полноценных процессорных ядер .


Хм. Я видел много твоих постов на форуме и это самый бесполезный из них, извини. :)

1) Да я в общем-то уверен, что и в Java и в C++ std::, и в Qt копирование реализовано точно также, а всем желающим предлагается написать собственную более эффективную реализацию. В конце концов, если у меня есть 1000 потоков по 256 байт и 1000 файлов по 1 кб, я совершенно точно не захочу писать каждый из них в 8 потоков, не говоря уже о том, что некоторые потоки (а Stream - асбтрактный класс) могут не реализовывать перемещение в произвольную позицию и совместную запись в один и тот же репозиторий. У File.Copy, конечно, могли бы сделать соответствующий флажок, но не сделали и ладно. Но всё равно как-то мимо кассы. В .NET есть многое, но это не значит, что там должно быть всё.

2) Ты безусловно совершенно прав. Но .NET - это умная надстройка над API и её асинхронные вызовы не исчезают в неизвестности, а всегда возвращаются с некоторым результатом, свидетельствующем о выполнении операции. Затем следует волшебная строчка:

 
Код:
Task.WaitAll(task1, task2, task3, task4);
Которая ожидает завершения всех запущенных на выполнение задач. И она действительно ждёт их завершения, потому что в выходной файл попадает всё, кроме последнего кусочка. И если после неё добавит ожидание в 1 милисекнду:
 
Код:
Thread.Sleep(1);
То всё завершеается корректно. А если нет, то потоки начинают освобождаться чуть-чуть раньше, чем должны бы были. Именно поэтому я и спрашивал совета .NET'овца. Сдаётся мне, что это руки тех самых индо-китайских программистов и в Task.WaitAll (.NET 4.5) закрался какой-то бажик.

3) Безусловно всё так. Но при копировании содержимого файла с диска на диск, узким местом является явно не процессор, а жесткий диск, который способен отдавать и получать данные существенно медленнее чем оперативная память, и в ожидании этого процессор спокойно может обслуживать и вдвое и втрое больше потоков, безболезненно переключаясь между ними. Поэтому интересует именно определение возможностей жесткого диска. (Если я конечно верно рассуждаю и одновременное копирование двух файлов размером 100Гб каждый в пределах одного и того же SSD размером 256Гб на одноядерном процессоре будет выполнено быстрее, чем их последовательное копирование).
252
14 апреля 2013 года
koderAlex
1.4K / / 07.09.2005
бесполезный ? другие значит тоже бесполезны ? :)
я вам дал "волшебного пендаля" в нужном направлении по всем вашим вопросам .
так что за бесполезностью умываю руки . )
62K
14 апреля 2013 года
LWhisper
33 / / 27.11.2012
Цитата: koderAlex
бесполезный ? другие значит тоже бесполезны ? :)
я вам дал "волшебного пендаля" в нужном направлении по всем вашим вопросам .
так что за бесполезностью умываю руки . )


Странная логика. Странные выводы. Обычно у тебя очень ценные ответы. Этот был наихудьшим, что я видел. Уверен, что ты отвечал, просто чтобы ответить, особенно не вчитываясь, и интерпретируя каждый вопрос по-своему. "Волшебный пендаль" был, но как-то сразу во все стороны. Он подошёл бы юному падавану, далёкому от небесных сфер, но никак не опытному программисту, которым мне хочется себя считать. :) Не обижайся. Если я не прав - аргументируй. Я свою критику аргументировал.

260
14 апреля 2013 года
Ramon
1.1K / / 16.08.2003
Для начала, откройте для себя memory mapped files, нэ.
62K
14 апреля 2013 года
LWhisper
33 / / 27.11.2012
Цитата: Ramon
Для начала, откройте для себя memory mapped files, нэ.


Копирование с использованием сопоставления с памятью: 11.3635306 секунд

Быть может, я чего-то не знаю о MMF? Пока я вижу только менее эффективное использование памяти и более медленное копирование.
P.S. И я всё ещё не понимаю, как это относится к моему вопросу, потому что любое чтение с диска остаётся чтением, а любая запись - записью, и возможности диска не увеличиваются.

260
14 апреля 2013 года
Ramon
1.1K / / 16.08.2003
Цитата: LWhisper
Цитата: Ramon
Для начала, откройте для себя memory mapped files, нэ.


Копирование с использованием сопоставления с памятью: 11.3635306 секунд

Быть может, я чего-то не знаю о MMF? Пока я вижу только менее эффективное использование памяти и более медленное копирование.
P.S. И я всё ещё не понимаю, как это относится к моему вопросу, потому что любое чтение с диска остаётся чтением, а любая запись - записью, и возможности диска не увеличиваются.



Быть может, вы осознаете разницу между вашими буферами и MMF, нэ?
PS: А на вопрос с кол-вом потоков вам уже ответили по большей части.

326
14 апреля 2013 года
sadovoya
757 / / 19.11.2005
Извините за офтоп. Лучше бы автор задал свой вопрос в .Net-овском разделе, а не в общих вопросах. Иначе о потоках тут пойдет такая война, что мама не горюй :) Думаю, автор правильно пытается решить задачу в рамках .Net, что не значит, что нет лучше методов.
7
14 апреля 2013 года
@pixo $oft
3.4K / / 20.09.2006
Цитата: LWhisper
Копирование с использованием сопоставления с памятью: 11.3635306 секунд

А что не нравится-то? Лучше этого — только 4 потока

62K
14 апреля 2013 года
LWhisper
33 / / 27.11.2012
Цитата: @pixo $oft
Цитата: LWhisper
Копирование с использованием сопоставления с памятью: 11.3635306 секунд

А что не нравится-то? Лучше этого — только 4 потока


Мне всё нравится. Я не понимаю зачем мне предложили использовать MMF, который работает медленнее обычного 4х поточного копирования. Возможно, я его неправильно дёргаю.

Цитата: Ramon
Быть может, вы осознаете разницу между вашими буферами и MMF, нэ?
PS: А на вопрос с кол-вом потоков вам уже ответили по большей части.


Ответа я не видел (только насчёт процессорных ядер, которые априори быстрее винта).
И нет, я не осознаю её, пока меня не ткнут носом в какой-нибудь внятный мануал, или не расскажут - чем же отличается считывание страницы файла с диска в память от чтения его буферами тех же размеров. Что -данные каким-то волшебным образом оказываются в памяти, не считываясь с диска? Магия существует? :D

Цитата: sadovoya
Извините за офтоп. Лучше бы автор задал свой вопрос в .Net-овском разделе, а не в общих вопросах. Иначе о потоках тут пойдет такая война, что мама не горюй :) Думаю, автор правильно пытается решить задачу в рамках .Net, что не значит, что нет лучше методов.


Да данная тема с .NET вообще связана только P.S., на который всё равно никто ответить не смог. :) Просто код приведён на C#. Могу переписать на С++. Суть от этого не изменится.


Господа, очень хочется ответа по существу:
Либо такого: koderAlex прав, от хъарактеристик винта ничего не зависит и решает все только количество ядер.
Либо такого: дёрнув метод *** можно получить характиристики привода, и на основе *** и *** вычислить оптимальное число потоков копирования.

И, раз уж зашла речь про MMF, с которым я на практике действительно не имел дела (а потому, возможно, чего-то не понимаю и что-то делаю неверно) - то и разъяснений или вкусной ссылочки по нему.

7
14 апреля 2013 года
@pixo $oft
3.4K / / 20.09.2006
http://msdn.microsoft.com/en-us/library/ms810613
http://msdn.microsoft.com/en-us/library/aa366556
Вкуснее некуда
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог