Эффективное программирование TCP-IP

       

Избегайте копирования данных.


| | |

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

Конечно, обычно данные между функциями, работающими внутри одного процесса, не копируются. Но в многопроцессных приложениях часто приходится передавать большие объемы данных от одного процесса другому с помощью того или иного механизма межпроцессного взаимодействия. И даже в рамках одного про­цесса часто доводится заниматься копированием, если сообщение состоит более чем из двух частей, которые нужно объединить перед отправкой другому процессу или другой машине. Типичный пример такого рода, обсуждавшийся в совете 24, -это добавление заголовка в начало сообщения. Сначала копируется в буфер заголовок, а вслед за ним - само сообщение.

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

rc = read( fd, buf + sizeof( struct hdr ) ),

sizeoft ( buf ) - sizeof( struct hdr );

Пример применения такой техники содержится в листинге 3.6.

Еще один прием - определить пакет сообщения в виде структуры, одним из элементов которой является заголовок. Тогда можно просто прочитать заголовок в одном из полей:

struct {

 struct hdr header; /* Структура определена  в другом месте.*/

 char  data[ DATASZ ];

} packet ;

rc = read( fd, packet, data. sizeof( packet data ) );

Пример использования этого способа был продемонстрирован в листинге 2.15. Там же говорилось, что при определении такой структуры следует проявлять осмотрительность.

Третий, очень гибкий, прием заключается в применении операции записи со сбором - листинги 3.23 (UNIX) и 3.24 (Winsock). Он позволяет объединять части сообщения с различными размерами.

Избежать копирования данных намного труднее, когда есть несколько процессов. Эта проблема часто возникает в системе UNIX, где многопроцессные приложения-распространенная парадигма (рис. 3.4). Обычно в этой ситуации проблема даже острее, так как механизмы IPC, как правило, копируют данные отправляющего процесса в пространство ядра, а затем из ядра в пространство принимающего процесса, то есть копирование происходит дважды. Поэтому необходимо применять хотя бы один из вышеупомянутых методов, чтобы избежать лишних операций копирования.



Содержание раздела