Не надо недооценивать производительность TCP
| | |
TCP - это сложный протокол, обеспечивающий базовую службу доставки IP- датаграмм надежностью и управлением потоком. В то же время UDP добавляет лишь контрольную сумму. Поэтому может показаться, что UDP должен быть на порядок быстрее TCP. Исходя из этого предположения, многие программисты полагают, что с помощью UDP можно достичь максимальной производительности. Да, действительно, есть ситуации, когда UDP существенно быстрее TCP. Но иногда использование TCP оказывается эффективнее, чем применение UDP.
В сетевом программировании производительность любого протокола зависит от сети, приложения, нагрузки и других факторов, из которых не последнюю роль играет качество реализации. Единственный надежный способ узнать, какой протокол и алгоритм работают оптимально, - это протестировать их в предполагаешь условиях работы приложения. На практике это, конечно, не всегда осуществимо, но часто удается получить представление о производительности, воспользовавшись каркасами для построения простых программ, моделирующих ожидаемый сетевой трафик.
Перед созданием тестовых примеров необходимо разобраться, когда и почему производительность UDP больше, чем TCP. Прежде всего, поскольку TCP сложнее, он выполняет больше вычислений, чем UDP.
Примечание: В работе [Stevens, 1996] сообщается, что реализация TCP в системе 4.4 BSD содержит примерно 4500 строк кода на языке С в сравнении с 800 строками для UDP. Естественно, обычно выполняется намного меньше строк, но эти числа отражают сравнительную сложность кода.
Но в типичной ситуации большая часть времени процессора в обоих протоколах тратится на копирование данных и вычисление контрольных сумм (совет 26), поэтому здесь нет большой разницы. В своей работе [Partridge 1993] Джекобсон описывает экспериментальную версию TCP, в которой для выполнения всего кода обычно требуется всего 30 машинных инструкций RISC (исключая вычисление контрольных сумм и копирование данных в буфер пользовательской программы, которые производятся одновременно).
Нужно отметить, что для обеспечения надежности TCP должен посылать подтверждения (АСК-сегменты) на каждый принятый пакет. Это несколько увеличивает объем обработки на обоих концах. Во-первых, принимающая сторона может включить АСК в состав данных, которые она посылает отправителю. В действительности во многих реализациях TCP отправка АСК задерживается на несколько миллисекунд: предполагается, что приложение-получатель вскоре сгенерирует ответ на пришедший сегмент. Во-вторых, TCP не обязан подтверждать каждый сегмент. В большинстве реализаций при нормальных условиях АСК посылает только на каждый второй сегмент.
Примечание: RFC 1122 [Braden 1989] рекомендует откладывать посылку ACK до 0,5 с при подтверждении каждого второго сегмента.
Еще одно принципиальное отличие между TCP и UDP в том, что TCP требует логического соединения (совет 1) и, значит, необходимо заботиться об его установлении и разрыве. Для установления соединения обычно требуется обмен тремя сегментами. Для разрыва соединения нужно четыре сегмента, которые, кроме последнего, часто можно скомбинировать с сегментами, содержащими данные.
Предположим, что время, необходимое для разрыва соединения в большинстве случаев не расходуется зря, поскольку одновременно передаются данные. Следует выяснить, что же происходит во время установления соединения. Как показано на рис. 2.16, клиент начинает процедуру установления соединения, посылая серверу сегмент SYN (синхронизация). В этом сегменте указывается порядковый номер, который клиент присвоит первому посланному байту, а также другие параметры соединения. В частности, максимальный размер сегмента (MSS), который клиент готов принять, и начальный размер окна приема, Сервер в ответ посылает свой сегмент SYN, который также содержит подтверждение АСК на сегмент SYN клиента. И, наконец, клиент отсылает АСК на сегмент SYN сервера. На этом процедура установления соединения завершается. Теперь клиент может послать свой первый сегмент данных.
Рис. 2.16. Установление соединения
На рис. 2.16 RTT (round-trip time) - это период кругового обращения, то есть время, необходимое пакету для прохождения с одного хоста на другой и обратно. Для установления соединения нужно полтора таких периода.
При длительном соединении между клиентом и сервером (например, клиент и сервер обмениваются большим объемом данных) указанный период «размазывается» между всеми передачами данных, так что существенного влияния на производительность это не оказывает. Однако если речь идет о простой транзакции, в течение которой клиент посылает запрос, получает ответ и разрывает соединение, то время инициализации составляет заметную часть от времени всей транзакции. Таким образом, следует ожидать, что UDP намного превосходит TCP по производительности именно тогда, когда приложение организует короткие сеансы связи. И, наоборот, TСР работает быстрее, когда соединение поддерживается в течении длительного времени при передаче больших объемов данных.
Чтобы протестировать сравнительную производительность TCP и UDP, а заодно продемонстрировать, как пишутся небольшие тестовые программки, создадим несколько простых серверов и клиентов. Здесь имеется в виду не полнофункциональная контрольная задача, а лишь определение производительности двух протоколов при передаче большого объема данных. Примером такого рода приложения служит протокол FTP.