Избегайте копирования данных.
| | |
Во многих сетевых приложениях, занимающихся, прежде всего, переносом данных между машинами, большая часть времени процессора уходит на копирование данных из одного буфера в другой. В этом разделе будет рассмотрено несколько способов уменьшения объема копирования, что позволит «бесплатно» повысить производительность приложения. Предложение избегать копирования больших объемов данных в памяти оказывается не таким революционным, поскольку именно так всегда и происходит. Массивы передаются не целиком, используются только указатели на них.
Конечно, обычно данные между функциями, работающими внутри одного процесса, не копируются. Но в многопроцессных приложениях часто приходится передавать большие объемы данных от одного процесса другому с помощью того или иного механизма межпроцессного взаимодействия. И даже в рамках одного процесса часто доводится заниматься копированием, если сообщение состоит более чем из двух частей, которые нужно объединить перед отправкой другому процессу или другой машине. Типичный пример такого рода, обсуждавшийся в совете 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, как правило, копируют данные отправляющего процесса в пространство ядра, а затем из ядра в пространство принимающего процесса, то есть копирование происходит дважды. Поэтому необходимо применять хотя бы один из вышеупомянутых методов, чтобы избежать лишних операций копирования.