Windows Vista SP1 по многим параметрам улучшает оригинальную Vista, в том числе в поддержке новых устройств, управлении питанием, безопасности и стабильности работы. Первый сервис пак должен выйти уже сегодня, так что до его полномасштабного внедрения остались буквально часы. Одним из улучшений станет ускоренное копирование файлов в разных обстоятельствах – в локальном копировании на одном диске, в копировании с удаленных не-Vista систем, копировании между SP1 компьютерами. Как же добились таких улучшений? Ответ достаточно сложен и лежит в изменениях, которые претерпел механизм копирования при переходе от Windows XP к Vista и в дальнейших изменениях которым подвергся в SP1. Каждый копирует файлы и я думаю будет очень полезным рассмотреть развитие процесса копирования что бы в конце концов понять как же он улучшен в первом сервис-паке.
Копирование выглядит довольно простой операцией: открыть исходный файл, создать целевой и прочитанное из исходного записать в целевой. В действительности же, быстродействие копирования зависит от множества факторов, таких как загруженность процессора, использовании памяти и передаваемых через систему данных. И улучшения в одной область неизбежно приводят к упадку в другой. В дополнении к этому, семантическая информация не доступна для процесса копирования и не может помочь в создании оптимального процесса копирования. Например, если бы он знал что вы не планируете использовать целевой файл, то можно было бы избежать кэширования его в памяти, но в случае если бы файл был немедленно использован другим приложением, или (как в случае файл-сервера) клиентская система расшарила файл, то механизм копирования немедленно бы и приоритетно закэшировал данные на целевой системе.
Копирование в предыдущих Windows
В свете всех компромиссов и недостаточной информации, механизм копирования в Windows пытается предусмотреть все сценарии разом. До Windows Vista в копировании прямо открывались исходный и конечный файлы в режиме кэширования и последовательно из исходного файла данные читались блоками по 64 Кб (60 Кб в случае сетевого копирования из-за ограничений, накладываемых протоколом SMB1.0) за раз и записывались в целевой по мере прихода. В случае когда доступ к файлу проходит через кэшированные операции чтения/записи (в противоположность memory-mapped операциям или операциям с флагом отказа от буферизации), данные чтения или записи сохраняются в памяти, по крайней мере до тех пор пока Memory Manager не решит перераспределить память для других нужд, например на кэширование других файлов.
Движок копирования полагается на Windows Cache Manager для осуществления асинхронного опережающего чтения, которое позволяет читать исходный файл в фоновом режиме, пока Explorer занят записью данных на другой диск или удаленную систему. Он так же использует опережающую запись Cache Manager для своевременного сброса содержания файлов из памяти обратно на диск так что бы память могла бы быть быстро освобождена в случае необходимости и потери данных при системном сбое диска были бы минимальны. Посмотреть механизм за работой вы можете в Process Monitor, в данном случае приведена трассировка копирования 256 Кб файла в Windows XP из одной директории в другую (с фильтром на чтение и запись данных):
Первую операцию чтения Explorer (событие 0) проводит с данными не присутствующими в памяти, что приводит к тому что Cache Manager выполняет некэшированное чтение/запись (в нем данные пишутся или читаются прямо с диска, без кэширования в памяти), для извлечения данных с диска мы видим событие 1, что видно по трассировке стека:
Видно, что обращение Explorer-а к ReadFile приводит к чтению файла на 22 шаге в функции BaseCopyStream и Cache Manager косвенно задействует некэширумеое чтение пытаясь использовать меппинг файла в памяти, что и вызывает сбой в 8 шаге.
Так как Explorer открывает файл с последовательным доступом (что не видно в трассировке), поток опережающего чтения Cache Manager-а, запущенный в процессе System, начинает активно читать файл в пользу Explorer-а в событии 2 и 3. Функцию опережающего чтения вы можете видеть в стеке для второго события:
Возможно вы заметили, что опережающее чтение идет не по тому порядку, который был инициирован первым не кэшированным чтением. В теории это может замедлить работу так как головкам винчестера надо будет осуществлять больше движений, однако Explorer прекращает некэшированное чтение в том момент когда доходит до данных уже прочтенных Cache Manager и его чтение совпадает с данными, уже находящимися в памяти. Обычно Cache Manager опережает Explorer на 128 Кб в процессе копирования.
В событии 4 в данном трейсе Explorer встречается с первой записью и затем мы можем видеть последовательность перемежающегося чтения и записи. В конце концов поток опережающей записи Cache Manager-а, так же запущенный в процессе System, сбрасывает последние данные целевого файла из памяти на диск при помощи некэшированной записи.
Улучшения в копировании в Vista
В процессе разработки Windows Vista команда пересмотрела движок копирования для улучшения его работы в нескольких ситуациях. Одна из самых больших проблем состояла в том, что при копировании больших объемов данных поток опережающей записи Cache Manager на целевой системе часто не мог справиться с со скоростью с которой данные записываются и кэшируются в памяти. Это забивало память данными, вытесняя другой необходимый код и информацию, а системная память на целевой машине становилась неким туннелем, через который проходил весь объем копированных данных со скоростью, которую ограничивал диск.
Другой проблемой стало копирование с удаленной системы, при котором содержание файла кэшировалось дважды – сначала при чтении исходного файла, а потом при записи целевого. Помимо пустой траты памяти на файлы, которые скорее всего никогда не будут использованы, работа Cache Manager перегружала центральный процессор, который необходимо было использовать для управления исходным и конечным файлами.
Улучшения в копировании в Vista
Ограничение, связанное с относительно маленькими и перемежающимися файловыми операциями, заключается в SMB драйвере файловой системы, протоколе, отвечающем за сетевое разделение файлов. Он не способен эффективно организовывать передачу данных по широким канал, по WLAN сетям с высокими задержками. Каждый раз когда локальная система ждет от удаленной получения данных, передача останавливается и задержки происходят сразу на двух системах, ожидающих друг от друга ответа о получении и очередной порции данных.
После изучения нескольких альтернатив команда разработчиков решила остановиться на механизме, оперирующем большими асинхронными некэшированными запросами на чтение/запись и таким образом решить все проблемы. С помощью некэшированного механизма данные копируемого файла не потребляют память на локальной системе, отсюда решение с перерасходом памяти. Асинхронность в чтении и записи больших файлов позволит организовать канал передачи данных в сетях с большими задержками, уменьшить занятость центрального процессора так как Cache Manager-у уже не надо будет перераспределять память. Неэффективность оригинального Windows Vista Cache Manager-а в обработке больших запросов так же склонила разработчиков использовать некэшируемые I/O. Они не могли сделать блоки данных произвольно большими, так как движок копирования должен читать данные перед их записью, и выполнять чтение и запись желательно одновременно, особенно в случае в деле копирования на разные диски или системы. Большие блоки так же усложняют задачу оценки точного времени операции копирования для пользователя, в такой ситуации меньше контрольных точек для определения прогресса и обновления этого показателя. Но команда отметила значительный спад в производительности некэшируемых запросов чтения/записи – в процессе копирования многих маленьких файлов дисковой головке приходиться непрерывно перемещаться по диску, сначала к источнику, потому к целевому файлу, потому обратно к источнику и так далее.
После проведенного анализа, тестов и настройки, программисты решили применить алгоритм, который использует кэшируемое копирование для файлов меньше 256 Кб. Для файлов больше 256 Кб алгоритм полагается на внутреннюю матрицу для определения количества и размера некэшируемых операций чтения/записи которые придется выполнить за раз. Количество это варьируется от 2 для файлов меньше 2 Мб до 8 для файлов больше 8 Мб. Размер блока чтения/записи равен размеру файла для файла меньше 1 Мб, 1 Мб для файла до 2 Мб и 2 Мб для файла большего размера.
При копировании 16 Мб файла, например, движок осуществляет восемь 2 Мб асинхронных некэшируемых чтений исходного файла, ожидает завершения операции, выполняет восемь 2 Мб асинхронных некэшируемых операций записи целевого файла, снова ожидает окончания записи и повторяет цикл. Вы можете увидеть такой механизм в трассировке Process Monitor копирования 16 Мб файла с локальной системы на удаленную:
В то время как механизм улучшен по сравнению с предыдущим по многим позициям, есть у него и недостатки. Один из них эпизодически случается при сетевом копировании, из-за не упорядоченных операций записи, одна из которых видна видна в трассировке на принимающем копию компьютере:
Обратите внимание как смещение операции записи скачет с 327,680 до 458,752, пропуская блок со смещением 393,216. Такой пропуск вызывает лишнее перемещение головки диска и заставляет NTFS выполнить ненужную операцию записи, записывая в пропущенный блок нули, так что в файле со смещением 393,216 будет две операции чтения. Ниже вы можете видеть как NTFS вызывает функцию Cache Manager-а CcZeroData для записи нулей в пропущенный блок:
Большей проблемой в использовании некэшируемых запросов чтения/записи является то, что быстродействие может страдать в сценариях публикации файлов. Если вы копируете группу файлов, например, на файловую шару веб-сайта, то веб-сервер естественно должен прочитать их с диска при первом обращении к ним. Это безусловно правильно для сервера, но большинство операций копирования в сценарии публикации осуществляется на клиентских машинах, так как появление новых файлов требует обновления поискового индекса, срабатывания антивируса, создания иконок для отображения файлов в папке Проводника.
(Продолжение следует)
Источник: http://blogs.technet.com/markrussinovich/