Динамическое и статическое распределение памяти в буферах для сетевых операций ввода-вывода и C ++ - PullRequest
3 голосов
/ 07 февраля 2011

Я не уверен, что тема правильная, но я постараюсь объяснить мою проблему ниже.То, что я ищу, - это своего рода «академический ответ», потому что я полагаю, что оба моих решения дают схожие результаты во время выполнения.

У меня есть программа C ++ в Linux, которая выполняет много сетевых операций ввода-вывода.операции, и мне интересно, лучше ли встроить буфер в класс клиента или распределить его динамически.Первое решение с использованием встроенных буферов:

template <size_t buffer_size> class Buffer
{
    // ...

    char buffer [buffer_size];
}

class TcpClient
{
    // ...

    Buffer<1024> input_buffer;
    Buffer<1024> output_buffer;
}

Второе с использованием динамически распределенных буферов:

class Buffer 
{
    Buffer (size_t buffer_size) :
        buffer (malloc (buffer_size))
    {
        // ...
    }

    // ...

    char* buffer; 
}

class TcpClient 
{
    // ...

    Buffer input_buffer (1024);
    Buffer output_buffer (1024); 
}

Теперь, сравнивая оба решения, я вижу, что первое требует меньше операций выделения памяти, чем второе,Следующая вещь - благодаря шаблонам компилятор знает размер класса во время компиляции.Первое решение должно дать лучшую ссылочную локальность (?), И компилятор также может выровнять размер класса по своему усмотрению.Также у нас есть прямой доступ к буферам, потому что нам не нужно выполнять дополнительную операцию разыменования указателя.

Я начал думать, как объекты TcpClient из первого решения ведут себя в кэше процессоров.Каждый раз, когда мы обращаемся к такому объекту в коде, он загружается в кэш процессоров, и его буферы тоже копируются, даже если они нам не нужны.Это может сделать кэш неэффективным, потому что мы храним там много данных, что увеличивает вероятность ошибок поиска памяти, верно?

Разве процессор не тратит время на копирование буферов в кэш все время?Каковы другие эффекты обоих решений с точки зрения процессора и операционной системы?Лучше ли держать размер класса маленьким или строить как можно больше?

Ответы [ 2 ]

4 голосов
/ 07 февраля 2011

Процессор не будет искать память по-разному, в зависимости от того, где он находится.Выделение в стеке происходит намного быстрее, гораздо менее подвержено ошибкам.Используйте динамическое распределение только в том случае, если распределение должно быть динамическое , то есть вам требуется переменная времени жизни или тип объекта.Иначе, используйте статическое распределение.

3 голосов
/ 07 февраля 2011

Я бы не был так обеспокоен эффектами кеша здесь. Сокет ввода-вывода медленен и кэширование неэффективно в любом случае, потому что это требует системных вызовов и копирования данных из буфера ядра в пространство пользователя, нет никакого способа POSIX сделать ввод-вывод сокета нулевой копии (вы можете сделать это с пользовательским оборудованием ). Лучшее, что вы можете сделать, - это минимизировать количество системных вызовов, необходимых для отправки и получения данных через сокет.

Размер вашего приемного буфера пользовательского пространства в идеале должен совпадать с размером приемного буфера сокета в ядре. Таким образом, вы можете прочитать все полученные данные в один recv/recvmsg/read() системный вызов.

Если вы не создаете клиентов много раз в секунду, может не иметь большого значения, сколько выделений требуется для построения объекта. Часто лучше определить и оптимизировать быстрый путь ввода-вывода, чтобы после создания клиентского объекта отправка и получение данных не требовали выделения памяти и копирования данных в пространстве пользователя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...