Показаны сообщения с ярлыком копирование файлов. Показать все сообщения
Показаны сообщения с ярлыком копирование файлов. Показать все сообщения

19.10.2022

Возвращаясь к копированию файлов

Сделав первую версию программки для копирования файлов и даже получив какой-то положительный результат, правда, всего лишь в одном из множества экспериментов, я захотел вывести формулу, которая показывает, во сколько раз может быть ускорено копирование файлов в многопоточном варианте.

И вот в этом месте я застрял. Потому что по всем моим рассуждениям выходило, что существенного ускорения быть не должно. И в большинстве экспериментов выходило именно так, за исключением всего лишь одного.
В общем, долго я в фоновом режиме думал на эту тему, пока меня не осенило. Так что сейчас поделюсь результатами.

Начнем с последовательного копирования при отключенном кэшировании со стороны ОС. В этом случае время копирования файлов (замечу, что пока речь про огромные файлы) будет наибольшим и составит


 
 


где D – объем копируемых данных, Vr
– скорость чтения, Vw – скорость записи данных на соответствующих дисковых устройствах.

При параллельном копировании с использованием многопоточной архитектуры время копирования составит



 


где Vmin
минимальная из скоростей чтения или записи. Я считаю, что в этом случае время на переключение потоков можно не учитывать в расчетах, так как оно пренебрежимо мало из-за гораздо более высокой производительности процессора и ОЗУ по сравнению с дисковыми устройствами.
Из приведенной формулы понятно, что скорость параллельного копирования существенно превышает скорость последовательного.

Но это происходит ровно до того момента, пока в работу не вступает системный файловый кэш на уровне ОС. Такой кэш реализован в виде отдельного системного процесса и работает всегда независимо и параллельно пользовательскому процессу.
Фактически это приводит к тому, что последовательное копирование при наличии файлового кэша по факту превращается в параллельное и примерно соответствует ему по скорости.
Но всегда ли?

Давайте рассмотрим процесс копирования подробнее с разных сторон.
В том случае, когда скорость чтения с диска-источника меньше скорости записи, то именно она будет лимитирующим фактором и будет однозначно определять скорость копирования как при параллельном, так и при последовательном копировании с кэшем. В этом случае никакого преимущества у параллельного копирования нет (точнее, почти нет).

В случае, когда лимитирующим фактором является запись на диск назначения и размер буфера записи в системе кэширования достаточен, то мы опять же получаем (примерный) паритет по скорости копирования у обоих вариантов.

А вот когда буфера записи становится недостаточно, то есть когда объем копируемых данных во много раз превышает размер кэша, то псевдопараллельный режим переходит в чисто последовательный и скорость записи существенно падает.
Это связано с тем, что при заполнении буфера записи при запросе приложения на запись очередной порции данных оно вынуждено ждать, пока буфер записи не скинет самую старую порцию на диск и не освободит место в ОЗУ под новую.

Таким образом, один из двух случай, когда параллельное копирование может быть существенно быстрее, чем последовательное с использованием кэширования, возникает тогда, когда объем копируемых данных существенно превышает объем кэша и копирование производится с более быстрого устройства на более медленное. Точнее, объем копируемых данных должен превышать половину кэша, так как из всего доступного объема половина будет использоваться под чтение.
Впрочем, это ограничение на половину объема можно снизить, правильно указав флаги использования копируемых файлов.
Если отбросить простые алгебраические манипуляции, то в этом случае время копирования файлов составит



 

 

где B – размер буфера записи, k – коэффициент > 1, показывающий, во сколько раз скорость чтения больше скорости записи, то есть Vr = k*Vw.

Отсюда легко получаем ускорение параллельного копирования по отношения к последовательному с кэшем



 

 

Остался еще один небольшой нюанс, связанный с переходом от копирования очередного файла к следующему. Если в случае параллельного копирования никаких пауз в этот момент нет, то вот в случае последовательного, даже с использованием кэша, она все же возникает, потому что пока запись очередного файла не закончится до конца, то есть все данные не будут сброшены на устройство, последовательное приложение не может начать копировать следующий файл. 

Но насколько велика такая задержка? Приблизительно её можно оценить так



 


где С
– размер блока read-ahead в системе файлового кэширования.
Насколько же она велика? Это зависит от размера блока и может сильно отличаться в разных версиях и вариантах системы кэширования. В простейших случаях она имеет фиксированный размер и, например, для какой-то (уже не помню номер) версии Microsoft Windows Server составляет 256 КиБ.
В любом случае можно констатировать, что она не очень велика, что подтверждается и моим тестами. Например, когда скорость дисков источника и назначения сравнима, то выигрыш параллельного копирования очень невелик и примерно соответствует приведенной величине задержке, умноженной на количество копируемых файлов за вычетом единицы.
Поэтому в том случае, когда мы копируем небольшое количество значительных по размеру файлов, такую задержку можно не учитывать, как и было в расчета времени копирования выше.

Но только не тогда, когда мы копируем очень много мелких файлов. И это второй случай, когда параллельное копирование будет значительно опережать последовательное с кэшем.

К сожалению, пока моя текущая версия программы во втором случае не может особо помочь. Это связано с особенностями работы с файлам в режиме с отключенным кэшем: порции данных, которыми ведется такая работа, должна быть кратна размеру сектора устройства.
Для оптимизации работы с большими файлами я сейчас использую достаточно большой размер такой порции.
При записи концовки большого файла или всего крохотного файла сначала записывается полная порция данных, гораздо больше, чем нужно, а файл потом усекается до требуемого размера.
В принципе, для оптимизации нужно внести совсем немного исправлений, в следующей версии, я думаю, они уже появятся.

Ну а пока, в текущей версии, сделал немного более информативный интерфейс, а то он был совсем уже слепой, да пару косячков исправил.

Кстати, для копирования файлов в параллельном режиме без использования кэширования вполне достаточно очень небольшого буфера. Его размер примерно можно определить по скорости работы накопителей. Точнее это можно сделать по IOPS, но сути это не меняет.
Сейчас я, например, исхожу из того, что размер буфера должен быть равен сумме объемов данных всех устройств, задействованных в копировании, которые они могут передать за один цикл переключения потоков. В Windows он скорее всего равен 55 мс, но может быть и меньше.
Этого хватает с большим запасом для поддержания стабильной скорости, при этом объём памяти, занимаемой программой, не превышает 25 МиБ. Потенциально может иметь важное значение в тяжело нагруженных системах, интенсивно работающих с ОЗУ.

29.05.2022

Нюансы копирования

Как у меня возникла идея попробовать написать свою программку копирования? Из наблюдения за процессом копирования менеджеров файлов, в первую очередь Total Commander и Far. Особенно у Total Commander это заметно в режиме копирования файлов в очереди.
Когда копирования очередного файла в очереди завершено, то есть файл полностью скопирован, программа замирает на продолжительное время (при копировании файла большого размера). Поразмыслив, я пришел к выводу, что причина в том, что запись файла кэширована, и при закрытии только что скопированного файла операционная система не отдает управление программе копирования, пока не будет
полностью сброшен кэш записи на диск.

Но в этом момент работает только диск назначения (если диски источника и назначения различны). Поэтому почему бы не начать заранее считывать следующий файл в буфер, пока текущий сбрасывает кэш на диск?
Собственно, на этой идеи и была основана моя многопоточная программка. И к моему разочарованию, она не показала особого ускорения по сравнению со стандартными способами: хоть при использовании xcopy, хоть Total Commander.
Я пробовал копировать со одного HDD на другой, с USB HDD на встроенный HDD и с HDD на сетевой диск. Результат был плюс-минус одинаковым.

А потом я решил ее проверить на более новом компе. И вот тут все сработало как надо: ускорение при копировании по сравнению с copy 29% с диска f: на диск g:, и 32% в обратном направлении.
Собственно, замерил в двух направлениях только из-за того, что это были два почти одинаковых HDD Seagate, отличия только в буковках названия модели, при этом один из диском, судя по мониторингу, был существенно меньше загружен, чем другой (f: > g:).

С чем же связано такое различие в поведении программы при исполнении на двух разных компьютерах? У меня несколько предположений.

1. Необходимым условием для ускорения копирования таким методом является возможность работы различных устройств хранения данных параллельно друг с другом. Если это условие не выполняется, то ускорения не будет.
Поэтому мне кажется это основной причиной различия в результатах.
Тем более, что когда я в прошлый раз менял сгоревшую "мать" (к сожалению, вместе с процессором), то заметил существенное снижение производительности дисковой подсистемы. Но так как на старой материнке я не тестил скорость дисковых операций, то всё осталось на уровне ощущений. Старая материнка была средне-высокой ценовой категории, а текущая – из дешевых.
Судя по тестам, мне кажется, что моя текущая плата не умеет вообще в параллельный ввод-вывод.

2. На компьютерах стоят разные операционные системы: Windows 7 на моем рабочем и Window 10 на более новом и быстром.

3. Я сравнивал быстродействие моей программы и стандартной copy. Возможно, xcopy будет работать быстрее. Проверю при ближайшей возможности.

Из любопытного: старый комп, которому уже наверное больше пяти лет, показал скорость копирования HDDHDD около 30 МиБ/с, а вот новый – около 180. Я думал, что в области жестких дисков уже давно нет никакого прогресса в скорости работы, только емкость потихоньку растет. Но, похоже, это не так?
Впрочем, на старом компе оба диска достаточно сильно забиты данными, так что, возможно, разница объясняется сильной фрагментацией данных.

Не знаю, надо ли кому, но выложил программку в общий доступ. Интерфейс у нее несколько кривоват и неудобен, а также несколько слеповат. Может быть, когда-нибудь и сделаю получше.

26.04.2022

Копирование файлов

Наблюдая за копированием больших файлов, заметил некоторую неоптимальность. Например, после завершения копирования файла возникает достаточно длинная пауза, вызванная дозаписью остатков файла из кэша. В это время можно было бы уже начинать считывать другой файл, если чтение и запись осуществляются на разные физические устройства.

Вторая неоптимальность связана с возможностью параллельного копирования нескольких файлов, если источник и назначение у них полностью разные или если одно из устройств гораздо быстрее другого/других.

Речь тут идет об использовании каких-либо приложений, поддерживающих постановку задач копирования файлов в очередь. Я пользуюсь Total Commander.

Возможность подобной оптимизации конечно зависит от аппаратной части компьютера: позволяет ли подсистема ввода-вывода параллельные операции на физически разные накопители.

В принципе, вторую неоптимальность можно легко решить в командной строке, запустив несколько процессов copy/xcopy для разных пар физических устройств. Правда, хотелось бы все же иметь для этого более человекоориентированный интерфейс.
А вот с первой неоптимальностью сложнее.

Лирическое отступление про кэш. В идеальной системе, где все приложения написаны максимально эффективно, видимо, нет особого места для файлового кэша на уровне операционной системы. Ну, по крайне мере я не вижу такого варианта использования, когда бы он пригодился.
Точнее, придумал тут один: когда вы открыли большой вордовский файл, закрыли его, и тут же вспомнили, что чего-то в нем не доделали. Вот тут кэш пригодится, да.

В реальной, не идеальной же системе кэш очень даже пригождается, потому что далеко не всегда программист имеет желание и возможность достаточно внимания уделить буферизации дисковых операций.
С кэшем же особо голову можно не греть. И без оптимизации все работает относительно быстро. Если про антивирусы на секунду забыть, да. Потери производительности, конечно, будут, но не сильно заметные.

Издержки же на файловый кэш не очень значительны. Во-первых, это память. Но при нынешних ценах на ОЗУ в компьютерах её обычно с избытком, так что это не проблема.
Во-вторых, двойные пересылки память-память. Первый раз при чтении с устройства в буфер системы кэширования, затем второй раз при копировании из кэша в буфер приложения.
При записи те же два этапа сохраняются, только идут в обратной последовательности.
Так как современное ОЗУ весьма производительно, то пересылки память-память не сильно снижают производительность.

Все это была преамбула. Теперь же перейду к амбуле. Решил я посмотреть и проверить, а нельзя ли ускорить процесс копирования файлов?

Для начала определился со стратегией кэширования. Вариантов тут три:
ничего не трогать, что бы все работало по умолчанию;
сообщить операционной системе, что операции с файлами будут происходить строго последовательно, а запись в файлы – сразу на диск;
вообще не использовать системы кэширования, читая/записывая непосредственно в/из буфера приложения.
Выбрал самый хардкорный, последний вариант, хотя там немножко более сложно выполнять и чтение и запись.

А для самого приложения решил реализовать следующую идею: есть набор потоков-"читателей", по одному на каждое физическое устройство хранения данных, и симметричный набор потоков-"писателей". Еще есть управляющий поток, который формирует задание "читателям", "читатели" в свою очередь читают данные из файла в буфер, это буфер передают соответствующим "писателям".
Идея здесь в том, что пока одно физическое устройство ждет завершения операции ввода/вывода, другое может в параллель тоже что-нибудь прочитать/записать.

В общем, слегка помаявшись с отладкой многопоточного приложения, в конце концов оттестировал его.

Как ни обидно, особого выигрыша по сравнению с несколькими копиями copy/xcopy достичь не удалось. 😂 Как, впрочем и проигрыша. У xcopy, кстати, тоже есть опция копирования без кэширования, как раз для больших файлов.
Есть подозрение, что причиной всему небольшие задержки в переключении потоков между "читателям" и "писателями".

Поэтому, если будет не лень, попробую другую идею: каждый файл копирует отдельный поток, но с разными приоритетами – первый файл на определенную пару физических устройств копируется с максимальным приоритетом, а все последующие – со всё более меньшим.