Как у меня возникла идея попробовать написать свою программку копирования? Из наблюдения за процессом копирования менеджеров файлов, в первую очередь 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 будет работать быстрее. Проверю при ближайшей возможности.
Из любопытного: старый комп, которому уже наверное больше пяти лет, показал скорость копирования HDD–HDD около 30 МиБ/с, а вот новый – около 180. Я думал, что в области жестких дисков уже давно нет никакого прогресса в скорости работы, только емкость потихоньку растет. Но, похоже, это не так?
Впрочем, на старом компе оба диска достаточно сильно забиты данными, так что, возможно, разница объясняется сильной фрагментацией данных.
Не знаю, надо ли кому, но выложил программку в общий доступ. Интерфейс у нее несколько кривоват и неудобен, а также несколько слеповат. Может быть, когда-нибудь и сделаю получше.
Протестировал самый обычный HDD у себя, получил те же ~180, при этом упирается именно в диск, а не в подсистему ввода/вывода. 30 МиБ/с как-то действительно совсем мало, сомневаюсь что в HDD произошел такой прогресс за несколько лет. Похоже что-то не так с материнкой или может с какими-нибудь настройками в BIOS или еще где-нибудь?
ОтветитьУдалитьТестировал с помощью https://crystalmark.info
Скачал CrystalMark, протестировал. Не 180, конечно, около 100 показывает. Вполне нормально, с учетом того, что диски старенькие. Но это синтетический тест, реальное копирование может существенно отличаться по скорости.
УдалитьНу и может быть влияет то, что диски сильно забиты, на 94%, тут фрагментация может сильно влиять.
Тем не менее, даже в таких условиях, мне кажется, должно быть ускорение при разделении копирования на два потока - чтения и записи. Пока не знаю, в чем дело.
А на каких размерах файлов Вы тестируете? CrystalMark вроде 1ГБ 5 раз читает и пишет по умолчанию, но там можно размер подкрутить. По сути ведь этот тест измеряет тоже чтение из файл в память и запись из памяти в файл, не понятно как он так хитро показывает большие цифры? Если фрагментация виновата, то она ведь должна и тесту палки в колеса вставлять?
УдалитьКак я понял из исходников (https://github.com/hiyohiyo/CrystalDiskMark) тест - это красивая обертка над https://github.com/microsoft/diskspd, а там вроде используется стандартный API для работы с файлами: ReadFile, WriteFile, а еще есть ссылки на Async IO и CompletionPort, но я не особо вникал когда и что используется и почему, не все там тривиально как-то :)))
Еще я попробовал запустить тесты для 2-х дисков сразу (HDD и SSD) и, ожидаемо, не увидел совсем никакого влияния их друг на друга. У Вас также?
Я на разных пробовал тестировать. Кстати, с размером в районе 1 ГБ на старом компе моя программка лучше работает. Чем больше размер файла, тем меньше преимущество перед стандартными приложениями для копирования.
УдалитьПоследние тесты проводил на файлах размером около 2 ГиБ.
По поводу теста: не очень понятно, как он работает. Возможно, он просто создает один файл и потом пять раз туда пишет/читает. Это немного другая ситуация, по сравнению с копированием 16 разных файлов.
Сейчас попробую сразу два теста параллельно запустить на разные HDD, посмотрю, что получится.
Кстати, может быть проблема есть с одним диском. Он года через два после покупки начал дурить, поэтому я купил еще один, а этот типа в качестве резервного, для хранения не очень важной информации использую. Вполне вероятно, что он уже не очень хорошо себя чувствует и дело в этом.
Сейчас запустил параллельный тест. Тесты показали независимы результат. Любопытно это всё.
Удалить> не очень понятно, как он работает. Возможно, он просто создает один файл и потом пять раз туда пишет/читает.
УдалитьПокопался в исходниках, действительно он именно так и работает :) Создает файл нужного размера и при этом не фрагментированный, а далее каждый тест или читает его или заполняет случайными числами с выключенным системным кэшем. Похоже действительно все замедляет фрагментация, как Вы и предполагали.
> Чем больше размер файла, тем меньше преимущество
Ну это вроде объяснимо: Ваш алгоритм оптимизирует копирование только для границ файлов, интересно было бы посмотреть на график разницы между стандартным и Вашем копированием в зависимости от размера файла. Моя теория что после определенного размера, разница перестанет изменяться.
А Вы не сравнивали размер внутреннего кэша дисков на старой и новой машине? Я тут обнаружил что у новых дисков он теперь существенно больше. В идеальной системе, когда ОС не вносит задержки, Ваш алгоритм, как я понимаю, именно операции с внутренним кэшем параллелизует на границах файлов, а если кэш маленький то и разница не значительна.
>...интересно было бы посмотреть на график разницы между стандартным и Вашем копированием
ОтветитьУдалитьДа, постараюсь как-нибудь сделать такой тест.
>А Вы не сравнивали размер внутреннего кэша дисков на старой и новой машине?
Не сравнивал, но точно знаю, что на новой машине он существенно больше. В несколько раз.
>...Ваш алгоритм, как я понимаю, именно операции с внутренним кэшем параллелизует на границах файлов
Вот тут не очень понял, про что ты... Вроде размер кэша самый большой, что я видел у настольных HDD - 256 МиБ, я же кэширую в ОЗУ несколько ГиБ, точнее, в описанном случае ровно 1 ГиБ. Кроме того, насколько я понимаю, этот кэш в HDD - только на чтение. По крайней мере, у современных жестких дисков нет возможности включить/выключить внутренний кэш на запись. Но я помню стародавние времена XP, когда для устройств с кэшем на запись это можно было сделать.
Также кэш HDD ничего не знает о файлах, поэтому там нет упреждающего кэширования, как в кэше ОС.
Кстати, любопытный момент - еще я оттестировал вариант программы, когда каждый поток копирует отдельный файл. К сожалению, она оказалась, на мой взгляд совсем не перспективной. Если одновременно начать копировать два файла в двух потоках одного приложения, то это только снизит производительность.
А любопытный момент вот в чем: если не запускать параллельно несколько копирований, а только одно, то производительность получается такая же, как и у всех программ копирования, только кэша на такие операции нужно очень немного. Мне хватило буфера в 128 КиБ, может быть и с меньшим скорость была бы аналогична. То есть именно для копирования использования кэша ОС не очень эффективно - доступная память отъедается на кэш, но эффекта от этого никакого нет.
Маленький кэш я не проверял на такой программе, а вот на многопоточной программе пробовал большой кэш, разбитый на маленькие страницы, по 4 КиБ (по размеру кластера файловой системы). И тут был странный эффект, кроме снижения производительности - высокая загруженность одного ядра процессора. При размере страницы больше 32 КиБ процессор вообще не загружается. Честно говоря, тут я не понимаю, в чем дело. На мой вкус, разницы не должно быть ни в загрузке процессора, ни в скорости работы. Ну, по крайней мере, заметной разницы.
> Вот тут не очень понял, про что ты
УдалитьКопируем файл, где-то в середине файла все оптимально: прочитали блок, отдали на запись, читаем следующий блок оба диска заняты, красота, и по идеи что у Вашей что у стандартной скорости должны быть плюс/минус одинаковыми, все упирается в диски. Но вот когда один файл полностью прочитался, но еще сохраняется, то Ваша программа сразу начинает читать следующий файл, а в стандартной сначала один диск простаивает, а потом другой. Вроде именно на этом достигается ускорение. Оно как мне кажется пропорционально: sum(min(file-size, some-constant) for each file) и интересно было бы узнать эту самую some-constant.
Но возможно я не совсем понял как у Вас реализовано копирование :) Я думал Вы читаете какими-то относительно небольшими блоками в одном потоке и отдаете на запись другому потоку, от куда 1 ГиБ в памяти берется? Или Вы читаете/пишите блоками по 1 ГиБ? А есть смысл в таких больших блоках? Ведь пока самый 1-й блок не прочитается, писать нечего и 2-й диск простаивает. Можете исходник выложить куда-нибудь?
> Кроме того, насколько я понимаю, этот кэш в HDD - только на чтение.
Вот вроде нет, он используется как на чтение так и на запись, по крайней мере судя по той инфе что я нашел.
> Также кэш HDD ничего не знает о файлах
Если данные не фрагментированы, то думаю упреждающее чтение возможно, но не факт что реализовано.
> то производительность получается такая же
Хм... но ведь если копируем с одного диска на другой, то распараллеливание чтения и записи или отложенная запись должны ведь ускорить, по крайней мере в теории, может быть тут как раз упреждающее чтение во внутренний буфер пока другой диск пишет как-раз и неявно реализует распараллеливание?
> доступная память отъедается на кэш, но эффекта от этого никакого нет.
Какие-то разные у нас сценарии :) Я практически всегда копирую или только-что только-что открыл, а значит он уже в кэше или наоборот - скопировал потом сразу открыл, а значит опять же копия в кэше - пригождается. Такого что скопировал и не трогаю практически не бывает. Есть конечно еще бэкапы, это действительно лучше без загрузки в кэш копировать, но обычно это не тупое стандартное копирование всего, а через софт какой-нибудь.
> И тут был странный эффект
А чтение блока требует синхронизации потоков? Может все в это упирается?
> откуда 1 ГиБ в памяти берется?
УдалитьНу да, именно так, я читаю небольшими блоками (страницами пусть будет, в данном случае по 64 КиБ), и общий объем этих страниц составляет 1 ГиБ.
> Можете исходник выложить куда-нибудь?
Подумаю, куда, но там очень замороченная система реализации в нескольких файлах.
> Вот вроде нет, он используется как на чтение так и на запись, по крайней мере судя по той инфе что я нашел.
Со встроенным кэшем HDD на запись есть одна беда - в случае сбоя по питанию данные теряются 100%. Поэтому надо городить резервное питание внутри устройства, а с учетом потребления HDD в пике - это та еще задачка. Думаю, что в бюджетных точно резервирования по питанию нет. Не уверен, что емкости небольшого суперконденсатора хватит на эти цели. Именно поэтому кэш на запись либо отсутствует на физическом уровне, либо, даже если и есть, то средствами Windows его включить нельзя сейчас.
> Хм... но ведь если копируем с одного диска на другой,
Да, но копируем одним поток синхронными вызовами. Поэтому распараллеливания там никакого нет, пишу мимо кэша, соответственно, никак упреждающее чтение не поможет. Особенно с учетом сильной дефрагментации дисков - тут внутренний кэш HDD тоже не помощник особо, даже если допустить, что он так умеет.
> Какие-то разные у нас сценарии :)
Нет, одинаковые. Просто иногда приходится копировать большие файлы, в моем случае видео. А вот использовать их если и приходится, то существенно позже. Ну и волей-неволей приходится наблюдать за процессом копирования, а так как он длинный, то возьмешь да и задумаешься.
Ну и насчет бэкапов. Удивительное дело, но многие небольшие конторы, которые не покупают себе суперсерверов и прочего, в качестве бэкапов опять же используются стандартные функции копирования. Так просто дешевле получается. При этом держат большие базы, по несколько десятков гигов.
> А чтение блока требует синхронизации потоков?
Сами операции чтения и записи - нет, не требует. Но сразу после чтения, и перед записью такая операция есть. Но вроде как она не должна настолько сильно грузить процессор сама по себе. У меня есть идея, в чем тут дело, но она так себе. Как-нибудь напишу.
> то средствами Windows его включить нельзя сейчас.
УдалитьА есть где почитать про это подробно? Я мало что найти смог, https://devblogs.microsoft.com/oldnewthing/20210729-00 статья одна из самых подробных и вроде как кэш на запись используется.
> Да, но копируем одним поток синхронными вызовами. Поэтому распараллеливания там никакого нет
Синхронные вызовы в теории могут скрывать чтение параллельно с записью, возможно некоторые чтения происходят очень быстро, так как данные берутся из внутреннего кэша диска? Хоть он и фрагментирован, но поди не на столько все плохо что там каждый блок в новом месте. Параллелизм думаю можно проверить отключив запись и оставив только чтение, если все совсем последовательно, то по идеи должно ускориться в 2 раза. Как то не верится что стандартное копирование полностью последовательно для 2-х дисков:)
> многие небольшие конторы
У нас собственные сервера, да и дата центры остались только у совсем больших компаний, да и те переезжают в клауд. Дорого здесь менедждить собственное железо, да и смысла особого нет.
> Но вроде как она не должна настолько сильно грузить процессор сама по себе
При 30 МиБ/с и 4 КиБ блоке получам 30К / 4 * 2 (чтение и запись) = 15K блоков/с или 0.066 мс/блок, я не знаю какой конкретно примитив Вы использововали, но в https://stackoverflow.com/questions/9997473/stdmutex-performance-compared-to-win32-critical-section тесте получили 0.140 мс/критическую секцию при 2-х потоках, так что вроде легко может оказаться узким местом.
> А есть где почитать про это подробно?
УдалитьЧестно говоря, я не знаю. Но в той статье, что ты цитируешь, автор упоминает "Enable advanced performance check box", которая собственно и управляет поведением кэша устройства. Я всегда включал максимальную производительность, так как люблю, что бы все работало очень быстро, медленная работа меня раздражает.
Так вот, на новых дисках, этих галочек нет. Кэш есть, а галочек - нет. Исходя из этого, я делаю вывод, что либо сами производители жестких дисков отказались от кэширования записи, например, из-за исков потребителей в случаях потери данных, либо Микрософт последними обновлениями просто галочку убрала по аналогичной причине. В последнем случае, можно, видимо, поправить дело в реестре, но чисто лень копать эту деревянную кучу. Кстати, галочки нет ни на одном диске с кэшем, ни в семерке, ни в десятке. Информации по этой теме реально особо нет, видимо, потому что особо никому и не надо?
> Как то не верится что стандартное копирование полностью последовательно для 2-х дисков:)
Опять не очень понимаю, про что ты. Я в обсуждаемом абзаце всего лишь утверждал, что тупое последовательное копирование через один маленький буфер по скорости точно соответствует стандартной процедуре копирования с использованием всего доступного кэша ОС. Понятно, что внутренний кэш HDD используется и в том, и в другом случае. Но с программной точки зрения операции строго последовательны.
> Дорого здесь менедждить собственное железо
В России все с точностью до наоборот. Абсолютная надежность ценится пока не очень, а услуги админов еще не настолько дороги, как в штатах. Ну и плюс душу греет то, что твои данные хранятся именно у тебя, а не где-то за тридевять земель.
Опять же, в случае сбоя сети или дата-центра данные становятся полностью не доступны, в то время, когда сервера у тебя, ты с ними можешь работать хотя бы локально.
> ...получили 0.140 мс/критическую секцию при 2-х потоках, так что вроде легко может оказаться узким местом.
Может, и так... Хотя Микрософты утверждают, что Critical Section - самое быстрое средство синхронизации между потоками. Видимо, надо мутить своё )
> на новых дисках, этих галочек нет.
УдалитьПокопался в настройках диска, у компьютера сына (единственная винда) есть 2 галочки: одна управляет системным кэшем с предупреждением что возможно потеря данных при пропадании питания (включена), другая выключает принудительный сброс write-through буфера, про нее написано что если включить, то вероятна потеря данных при незапланированном выключении (выключена). Как я понимаю по дефолту буфер диска используется при записи, и он принудительно сбрасывается, но вот как часто я не нашел.
> из-за исков потребителей в случаях потери данных
Ох сомневаюсь что засудить их возможно, принимая лицензию Микрософт да и почти наверняка любую другую, ты соглашаешься что они не ответственны за потерю данных. Там где надежность действительно нужна есть и аппаратный рейд и с батарейкой и бесперебойники.
> соответствует стандартной процедуре копирования с использованием всего доступного кэша ОС
Да, и так как стандартная процедура использует кэш ОС для записи, то запись происходит отложено: ОС в фоне сбрасывает буфер на диск, фактически чтение и запись происходят одновременно. В Вашем варианте, системного кэша нет, но скорость та же, вот я и пытаюсь понять как так происходит. И кроме как крохотным буфером диска объяснить пока не могу.
> Critical Section - самое быстрое средство синхронизации между потоками.
Есть еще Lock-Free структуры данных, но если нужна именно синхронизация, то не думаю что есть что-то быстрее, так как Critical Section - она самая простая по функционалу. Я именно про эту проблему говорил, когда рассуждал про асинхронный ввод/вывод используя единственный поток для диспетчеризации как один из вариантов.
> Покопался в настройках диска, у компьютера сына (единственная винда) есть 2 галочки...
УдалитьВот эти две галочки - это про кэш ОС. Ниже есть место еще для двух галочек - вот они про кэш устройства. С некоторого момента они пропали вообще. То есть раньше они сначала были "серыми", когда у устройства не было кэша, затем они просто перестали их показывать, если кэша нет. А теперь, такое ощущение, они вообще никогда их не показывают. То ли потому что кэша нет, то ли из-за "заботы" о сохранности данных пользователей.
> ОС в фоне сбрасывает буфер на диск, фактически чтение и запись происходят одновременно.
Ну чисто теоретически, в идеальном случае. Практически же считав первую порцию файла (ее в кэше не было, поэтому потребовалось время), мы тут же ее пишем в файл назначения. Так как операция записи кэширована, то функция возвращает управление практически сразу, и мы тут же начинаем читать следующую порцию источника, система кэширования, скорее всего, не успеет даже выполнить упреждающее чтение. То есть опять ждем завершения дисковой операции.
Со временем кэш, выделенный на файл назначения, кончится, и операции записи станут медленными. Но зато начнет успевать работать упреждающее чтение. То есть до заполнения кэша это часть копирования аналогична тому, что мы используем один очень большой буфер в синхронном режиме.
Таким образом получается, что в случае кэширования мы сначала ждем чтения, потом записи. А потом кэши, выделенные на оба файла, заполняются и мы переходим полностью в синхронный режим.
В случае, если я пишу мимо кэша сразу, разницы в скорости нет, потому что я всегда в синхронном режиме. С кэшированием, получается, тоже.
> ...не думаю что есть что-то быстрее, так как Critical Section - она самая простая по функционалу.
Можно самому сделать для синхронизации в рамках процесса. Атомарная операция с блокировкой шины данных. Тут самый затык в ожидании заблокированного ресурса. Делать sleep не вариант - очень длинная пауза с потерей производительности. Обходятся обычно циклами, но это напрасно грузит ядро. Впрочем, если все операции, требующие синхронизации, очень короткие, то может быть годным вариантом. Я даже где-то видел стандартный какой-то класс на Delphi, который что-то подобное реализует.
Почитал, как в Windows критическая секция организована. Вроде, даже в режим ядра может не переключаться, если ожидание ресурса короткое. Поэтому она должна быть очень оптимальна для моего случая.
УдалитьЕсли операция под защитой критической секции длинная, то тогда для ожидание происходит переключение в режим ядра с некоторой потерей производительности.
Исходники:
УдалитьИнтерфейс - https://disk.yandex.ru/d/AkSPlm-DA9HYKQ
Генератор заданий - https://disk.yandex.ru/d/g7iBLjOtNNe9ew
Читатель - https://disk.yandex.ru/d/2Ekt-eW5n6HI7Q
Писатель - https://disk.yandex.ru/d/1T5Q-97OM5sILw
Общие типы - https://disk.yandex.ru/d/NhRp53qU5PTjLA
Это скорее черновые наброски, которые надо бы доработать.
Удалить> Вот эти две галочки - это про кэш ОС
УдалитьНе уверен что вторая про кэш, если 1-я явно использует термин Cache, то 2-я - Write Buffer, в https://devblogs.microsoft.com/oldnewthing/20100909-00 также описывается что это про внутренний буфер, но на сколько ей можно доверять не знаю.
> и мы переходим полностью в синхронный режим.
По моему Вы не учитываете, что ОС в фоне записывает грязные страницы на диск. https://docs.microsoft.com/en-us/windows/win32/fileio/file-caching упоминает что "lazy writer process" пишет на диск 1/8 грязных страниц каждую секунду. А значит если скорости читателя и писателя равны, то в кэше будет данных прочитанных за последнии 8 секунд и размер кэша стабилизируется.
Спасибо за исходники, как вариант можно попробовать разделить TWriteTaskBuffer чтобы уменьшить вероятность блокировки:
1. Одна глобальная очередь из пустых буферов не привязанных к дискам.
2. У каждого читателя своя очередь из файлов на чтения (как у вас сейчас), он берет пустой буфер, заполняет его, находит соответствующего писателя и добавляет буфер ему в очередь на запись.
3. У каждого писателя своя очередь из буферов на запись, он пишет в файл и перекладывает буфер в очередь из пустых.
Еще как вариант объединить писателя и читателя в одном потоке с 2-мя очередями, если есть что писать - то пишем, если есть что читать - читаем, ну и на крайняк начинаем читать новый файл.
На сколько это поможет я не знаю, так мысли в слух :)))
А вдруг в системе 3 диска и нам нужно с 2-х скопировать на 3-й, я правильно понимаю что сейчас третий диск будет одновременно сохранять 2 файла что возможно не очень оптимально, или это как-то решается у Вас?
> Не уверен что вторая про кэш, если 1-я явно использует термин Cache, то 2-я - Write Buffer...
УдалитьХм... Да, похоже на то... Вот что бывает, когда реконструируешь давние воспоминания ) Получается, что lazy write не отключается у кэша ОС?
> А значит если скорости читателя и писателя равны, то в кэше будет данных прочитанных за последнии 8 секунд и размер кэша стабилизируется.
И это же значит, что мы перейдем в полностью синхронный режим работы вроде?
> Спасибо за исходники, как вариант можно попробовать разделить TWriteTaskBuffer...
Мне буквально вчера пришла в голову та же мысль. Почему изначально так не сделал? Решил, что операции ввода/вывода гораздо медленнее, чем поиск по буферу, поэтому вроде как поиск не должен особо ограничить производительность.
В общем, тут надо как следует подумать, приведет ли такой подход к улучшению или нет.
Я тут немного другое обнаружил. Как я вчера писал, захват критической секции в Windows умеет немного ожидать перед тем, как отправить поток в спячку в случае, если критическая секция занята. Управляется это параметром SpinCount. Так вот, класс в Delphi для обертки критической секции не использует эту возможность. Попробую добавить вручную, возможно это избавит от проблемы с загруженностью ядра.
> А вдруг в системе 3 диска и нам нужно с 2-х скопировать на 3-й...
Тестил такой вариант. Как не удивительно, не мешает. Получается быстрее, чем при последовательном копировании файлов стандартными средствами, и аналогичная скорость, если запустить два стандартных копирования: одно копирует с одного исходного диска, а другое - со второго.
Видимо, так получается, потому что оба потока-читателя успевают накопировать в буфер достаточное количество страниц, поэтому писатель сначала пишет несколько страниц с одного диска, а потом несколько - с другого.
Два параллельных процесса xcopy тоже работают интересно: один явно имеет больший приоритет, поэтому в основном копирует он, а второй, пока не первый не закончится, работает очень медленно, поэтому они друг другу особо не мешают.
> Получается, что lazy write не отключается у кэша ОС?
УдалитьПолагаю что нет, но она его очень часто сбрасывает, чтобы минимизировать ущерб, но не исключить :)
> И это же значит, что мы перейдем в полностью синхронный режим работы вроде?
Клиентский процесс будет блокироваться на чтении, а запись в кэш будет происходить мгновенно, а системный процесс в это время будет записывать данные из кэша на диск, получается что в аккурат тоже состояние что и в вашей программе в середине копирования, только у вас два пользовательских потока, а тут пользовательский и системный. Но вот в конце файла будут отличия: стандартная утилита как только все прочитает и отправит на запись, вызовет закрытие файла назначения и заблокируется пока остатки файла не запишутся на диск, а в начале файла писатель простаивает, так как еще ничего не прочиталось. При Вашем же подходе, ни читатель, ни писатель не должны простаивать, этим и достигается оптимизация, как мне кажется.