Тонкая настройка параметров TCP/IP под толстые каналы
Крис Касперски
Пропускная способность локальных сетей и Интернет-каналов неуклонно растет, однако вместе с ней растут и потребности, вызывающие естественное желание выжать из стека TCP/IP максимум возможного, чем мы сейчас, собственно, и займемся, главным образом акцентируя внимание на Windows Server 2003, хотя описанные технология оптимизации справедливы и для рабочих станций, собранных на базе W2K/XP.
Содержание:
- Введение
- Прежде чем приступать к оптимизации
- MTU + MSS = ???
- TCP Receive Window
- Медленный старт и выборочное подтверждение
- Время, работающее против нас
По поводу кручения настроек TCP/IP существуют два диаметрально противоположных мнения: многие администраторы (а вместе с ними и авторы популярных книг) считают, что разработчики уже сделали все, что нужно, и любое вмешательство в этот четко отлаженный механизм может только навредить. В то же самое время в Интернете валяется множество руководств, обещающих радикальное увеличение производительности ценою изменения пары-тройки ключей в системном реестре.
Истина, как водится, где-то по середине. Операционные системы уже давно научились автоматически распознавать тип подключения, выбирая соответствующий ему набор настроек по умолчанию. Адаптивные алгоритмы динамически подстраиваются под характеристики канала, и неквалифицированные «указания» пользователя, действительно, только мешают. Однако адаптивным алгоритмам свойственно ошибаться, а настройки по умолчанию далеко не всегда соответствуют характеристикам конкретно взятых каналов связи, разброс которых просто колоссален.
Какой прирост производительности может дать оптимизация параметров TCP/IP при условии, что она выполнена правильно? Зависит от того, насколько настройки по умолчанию близки к свойствам используемого канала. В среднем, следует ожидать 20%-30% выигрыша, однако в «клинических» случаях скорость может увеличиться в несколько раз!
Прежде чем приступать к оптимизации
Вместо того чтобы, засучив рукава, с первых же строк бросаться в бой, лучше сперва покурить и подумать. Допустим, мы имеем 10 мегабитный канал и скачиваем/раздаем файлы с превалирующей скоростью порядка мегабайта в секунду. Понятно, что никакими ухищрениями нам не удастся поднять производительность на сколь-нибудь заметную величину. Так стоит ли возиться? К тому же, достаточно большое количество администраторов умышленно ущемляет отдачу в районе 50-100 Кбайт/с, предотвращая перегрузку сети. Какая уж тут оптимизация…
Другое дело, если наблюдаемая пропускная способность составляет менее 2/3 от заявленной аплинком. Тут уже без оптимизации никак не обойтись! Однако, помимо стека TCP/IP, за производительность отвечают и другие системные компоненты, например, процессор. При большом количестве одновременно установленных соединений загрузка ЦП может достигать 100%.
Еще одна виновница — видеокарта, надолго захватывающая шину безо всяких видимых причин, в результате чего все остальные периферийные устройства садятся на голодный паек, и скорость ввода/вывода (в том числе и сетевого) многократно снижается. Обновление драйверов или отключение всех «агрессивных» настроек видеокарты в этой ситуации может помочь.
Так же не стоит забывать и о том, что чрезмерная фрагментация дискового пространства существенно замедляет скорость отдачи/приема файлов, что является одной из основных причин замедления загрузок web-страничек у конечных пользователей.
В общем, прежде чем лезть в стек TCP/IP, следует убедиться, что все остальные возможные причины устранены, и узким местом являются именно настройки сетевых протоколов, а не что-то иное (внимание: «убедиться» это совсем не то же самое, что «убедить себя»).
MTU (Maximum Transmission Unit – максимальный размер передаваемого пакета), вероятно, самый известный параметр TCP/IP, рекомендации по настройке которого можно встретить практически в любой статье по оптимизации TCP/IP. Сотни утилит предлагают свои услуги по определению предельно точного значения, но, увы, обещанного увеличения производительности как-то не достигается.
MTU задает наибольший возможный размер отправляемого IP-пакета (вместе с заголовком), нарезая отправляемые данные на порции фиксированного размера. Чем больше MTU, тем ниже накладные расходы на передачу служебной информации, а, значит, выше КПД канала. С другой стороны, маршрутизаторы сваливают пакеты, поступающие от разных узлов, в общую очередь, и потому гораздо выгоднее отправить один большой пакет, чем два маленьких, причем, чем сильнее загружен маршрутизатор, тем больший выигрыш мы получим.
Так в чем же дело?! Выкручиваем MTU до предела и… скорость падает до нуля. Почему? Причина в том, что с ростом размера пакетов увеличивается и время, необходимое для их повторной передачи, в том случае если пакет потерян или искажен. К тому же, промежуточные узлы имеют свои собственные настройки, и если размер передаваемого пакета превышает текущий MTU, пакет разрезается на два или более пакетов (т.е. фрагментируется), и эти фрагменты собираются воедино только на узле-приемнике, в результате чего пропускная способность уменьшается. Причем, если MTU узла отправителя лишь чуть-чуть превышает MTU промежуточного узла, то второй пакет состоит практически из одного заголовка.
Запускам утилиту «Редактора Реестра» и открываем в ней следующий раздел: HKLMSYSTEMCurrentControlSetServicesTcpipParametersInterfacesinterfaceGUID. Видим параметр MTU типа DWORD (а если не видим, то создаем) и вводим требуемый размер в байтах (например, 0xFFFFFFFF означает «использовать значение MTU по умолчанию»). Интерфейсы заданы GUID-идентификаторами, и обычно их бывает намного больше одного. Как среди них найти интерфейс кабельного модема или конкретной сетевой карты? Да очень просто — по IP-адресу!
Существует возможность автоматического определения маршрута, по которому пакеты с заданным MTU проходят без фрагментации (параметр EnablePMTUDiscovery типа DWORD, находящийся в той же ветви реестра, что и MTU; значение «1″ — включает данную функцию, «0″ – выключает). Однако многие администраторы промежуточных узлов по соображениям «безопасности» блокируют отправку ICMP-сообщений, и узел-отправитель остается в полном неведении относительно факта фрагментации. Специально для обнаружения таких вот «неправильных» маршрутизаторов (прозванных «черными дырами»), Windows поддерживает специальный алгоритм, управляемый параметром EnablePMTUDiscovery (во всем аналогичный EnablePMTUDiscovery). В подавляющем большинстве случаев использование опций EnablePMTUDiscovery и EnablePMTUDiscovery приводит к снижению производительности.
Еще один параметр — MSS (Maximum Segment Size – Максимальный Размер Сегмента) отвечает за максимальный размер передаваемых данных за вычетом длины заголовка IP-пакета. Трогать его не следует, да и Windows все равно это не позволяет. В общем случае, MSS = MTU — 40 байт.
Размер TCP-окна — малоизвестный, но чрезвычайно важный (в плане производительности) параметр, способный увеличить пропускную способность в несколько раз. Рассмотрим два узла A и B и заставим узел A передавать узлу B данные, разбитые на сегменты, размер которых (как уже говорилось) определяется параметром MSS. Протокол TCP работает с установкой соединения, что обязывает его отправлять уведомления об успешно принятых сегментах. Неподтвержденные сегменты спустя некоторое время передаются узлом A вновь.
Промежуток времени между отправкой пакета и его получением называется задержкой (latency), и эта латентность, в зависимости от типа и загруженности сети, варьируется от 20 ms (и менее) до 100 ms (и более). Легко посчитать, что если бы подтверждался каждый сегмент, то даже в низколатентной сети реальная скорость передачи заметно отставала бы от ее реальных возможностей и была равна MTU/(2*latency), что образует предел в 6 Мбит/сек, *независящий* от пропускной способности. Кошмар!
Вот поэтому создатели TCP/IP и разрешили узлу A отправлять более одного сегмента, не дожидаясь подтверждения. Максимальное количество сегментов, которое можно передать до прихода подтверждения, и называется размером TCP-окна. Почему этот параметр так важен для достижения наибольшей производительности?
Допустим, мы имеем канал 10 Мбит и передаем 7 сегментов по 1460 байт каждый, потратив на это 8 ms. Если латентность составляет 100 ms, то 100 ms + 92 ms = 192 ms. Мы, как идиоты, ждем подтверждения целых 192 ms, и 96% времени узел А проводит в бездействии, используя лишь 4% пропускной способности канала. Это, конечно, крайний случай, но все-таки не настолько далекий от истины, как можно было бы подумать.
В процессе установки соединения узел A предлагает узлу B установить размер окна, равный 16 Кб (значение по умолчанию, прописанное в параметре реестре ТсрWindowSize, которое при желании можно изменить). Если размер окна превышает 64 Кб, система активирует алгоритм автоматического масштабирования, который, впрочем, работает только в том случае, если узел B также поддерживает этот механизм. Однако следует помнить, что слишком большое окно забивает канал пакетами, вызывая перегрузку сети, препятствующую пересылке уведомлений, в результате чего производительность падает.
Две рекомендации: если клиенты локальной сети работают через Proxy-сервер, то для достижения максимальной производительности достаточно изменить размер TCP-окна непосредственно на самом сервере; при работе через NAT необходимо настроить TCP-окно на каждой рабочей станции, подключенной к локальной сети.
Медленный старт и выборочное подтверждение
Для предотвращения перегрузок сети в протокол TCP был введен так называемый «медленный старт» («slow start»), подробно описанный в RFC 1122 и RFC 2581. При создании нового TCP/IP соединения система устанавливает размер окна, равный одному сегменту. После получения подтверждения размер окна увеличивается вдвое, и так продолжается вплоть до достижения максимально возможного размера.
Экспоненциальный рост ширины окна «съедает» совсем немного времени при передачи огромных файлов, но вот при установке множества TCP/IP соединений (характерных, например, для браузеров), обменивающихся крошечными порциями данных, медленный старт заметно снижает эффективность широких каналов, кроме того, даже при кратковременной перегрузке сети система сбрасывает размер окна в единицу.
Нельзя забывать и про специальный параметр Slow Start Threshold Size (пороговый размер окна медленного старта), по умолчанию равный 65636. После распознавания ситуации «перегрузка сети» он может вызвать драматическое падение производительности.
Непосредственно отключить «медленный старт» штатными средствами Windows (не прибегая к патчу ядра) нельзя, однако, если задействовать SACK-алгоритм (Selective Acknowledgement — выборочное подтверждение, одно из расширений TCP-протокола, описанное в RFC 2018), «медленный старт» вырубается сам собой, становясь при этом никому не нужным избыточным пережитком старины.
Выборочное подтверждение передачи позволяет осуществлять повторную передачу неподтвержденных сегментов в одном окне (при неактивном SACK’е потерянные сегменты передаются один за другим в индивидуальном порядке). Другими словами, узел А повторно передает узлу B только реально потерянные сегменты, а не весь блок, в состав которого входят и успешно принятые пакеты. Очевидно, что максимальный прирост производительности будет наблюдаться на нестабильных каналах связи, регулярно теряющих пакеты. Для активации алгоритма SACK достаточно установить параметр реестра SackOpts в значение «1″.
С подтвержденными сегментами все ясно. Если подтверждение пришло, сегмент можно считать успешно доставленным. Весь вопрос в том, сколько это самое подтверждение ждать, и когда начинать повторную пересылку.
По умолчанию Win2003 ждет три секунды (при желании это значение можно изменить редактированием параметра TcpInitialRTT), после чего осуществляется повторная пересылка неподтвержденных пакетов, а сам интервал ожидания увеличивается в соответствии с алгоритмом SRTT (Smoothed Round Trip Time — сглаженное оцененное время обращения). Максимальное количество повторных передач хранится в параметре TcpMaxDataRetransmissions (по умолчанию равен пяти), при достижении которого соединение разрывается.
Очевидно, что на нестабильных каналах, страдающих хроническими задержками, количество разрывов соединений можно сократить путем увеличения параметра TcpMaxDataRetransmissions до любой разумной величины (но не больше FFFFFFFFh). С другой стороны, для повышения производительности и «нейтрализации» пагубного влияния потерянных пакетов на быстрых каналах с малым временем задержки значение TcpInitialRTT рекомендуется уменьшить до одной секунды.
Главный недостаток статического таймера в его неспособности реагировать на кратковременные изменения характеристик канала связи. Выбранное системой время ожидания подтверждения оказывается то мало, то велико. Производительность падает, пользователь рвет и мечет, а пропускная способность «плавает» в очень широких пределах, заметно отставая от ожидаемой.
Задержанное подтверждение (Delayed Acknowledgement) — еще одно расширение протокола TCP/IP, описанное в RFC 1122 и впервые реализованное в NT 4.0 SP4 и W2K. Вместо того чтобы подтверждать каждый полученный сегмент, узел B теперь отправляет подтверждение только в том случае, если в течение определенного промежутка времени (хранящегося в параметре TcpDelAckTicks и по умолчанию равном 200 ms) от узла A не было получено ни одного сегмента. Другими словами, если сегменты идут дружными косяками, и все работает нормально, подтверждения не отправляются до тех пор, пока в сети не возникнет «затор». Немного подождав, узел B высылает подтверждение обо всех полученных сегментах, давая узлу A возможность самостоятельно разобраться — какие сегменты потерялись в дороге, и передать их повторно с минимальными накладными расходами.
К сожалению, задержка, выбранная компанией Microsoft по умолчанию, близка к латентности сетей с большими задержками, что сводит на нет все достоинства данного алгоритма, и для повышения производительности значение TcpDelAckTicks рекомендуется увеличить в несколько раз. Соответственно, на низколатентных сетях его лучше уменьшить, ликвидируя никому не нужные простои. Значения данного параметра могут варьироваться в диапазоне от 0 до 6, выражаемом в десятых долях секунды, т.е. единица соответствует 100 ms, а нуль трактуется как запрет на использование задержанных подтверждений.
При использовании TCP-окон большого размера рекомендуется задействовать алгоритм временных меток (TCP-Timestamps), описанный в RFC 1323 и автоматически адаптирующий значение таймера повторной передачи даже в условиях быстро меняющихся характеристик канала связи. За это отвечает параметр Tcp1323Opts, который, будучи установленным в значение 3, разрешает использование всех расширений RFC 1323.
Статья опубликована в августовском номере журнала «Xakep» за 2007 год.


