Один большой байтовый буфер или несколько маленьких? - PullRequest
2 голосов
/ 24 февраля 2009

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

Однако я видел два разных метода создания пула байтовых массивов: один использовал простую систему очередей и просто добавлял / удалял их из очереди по мере необходимости. Если он был запрошен и в очереди больше не осталось, создается новый байтовый массив.

Другой метод, который я видел, использует один большой байтовый массив для всей программы. Идея очереди все еще применима, но вместо этого это очередь целых чисел, которые определяют срез (смещение) байтового массива для использования. Если один запросили, а в очереди больше не осталось, размер массива должен быть изменен.

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

Ответы [ 5 ]

5 голосов
/ 24 февраля 2009

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

Решение с одним массивом также намного сложнее в реализации. Хорошая вещь заключается в том, что решение с одним массивом позволяет вам выдавать куски переменного размера, но это происходит за счет существенного переопределения malloc: работы с фрагментацией и т. Д., И т. Д., В которые вы не должны входить.

Решение с несколькими массивами позволяет вам инициализировать пул с количеством буферов N и легко управлять им простым способом. Определенно подход, который я бы рекомендовал.

2 голосов
/ 24 февраля 2009

Еще один голос за несколько буферов, но с добавлением, что, поскольку вы делаете вещи асинхронно, вы должны убедиться, что ваша очередь является поточно-ориентированной. По умолчанию Queue<T> коллекция определенно не поточно-безопасная.

ТАК пользователь и сотрудник MS JaredPar имеет хорошую реализацию очереди с поддержкой потоков:
http://blogs.msdn.com/jaredpar/archive/2009/02/16/a-more-usable-thread-safe-collection.aspx

2 голосов
/ 24 февраля 2009

Я бы не предложил вариант изменения размера. Начните с простого и продолжайте свой путь. Хорошим началом была бы очередь байтовых буферов, которая добавляет новый в конец, когда он исчерпан. Вам, вероятно, придется обратить внимание на проблемы с многопоточностью, поэтому я бы посоветовал использовать чью-либо еще реализацию с безопасным потоком очереди.

Далее вы можете взглянуть на более сложные «указатели» на кусок большого байтового массива, за исключением того, что я бы посоветовал иметь в очереди блоки размером 4k / 16k (что в два раза больше размера страницы) индексировать в, и когда он заполнится, вы добавляете еще один большой кусок в очередь. На самом деле, я не рекомендую это вообще из-за сложности и сомнительного увеличения производительности.

Начни с простого, продолжай свой путь. Пул буферов, сделайте его потокобезопасным, посмотрите, нужно ли вам что-нибудь еще.

1 голос
/ 24 февраля 2009

При использовании кучи для сбора мусора вы всегда должны отдавать предпочтение небольшим буферам правильного размера с коротким сроком службы. Распределитель кучи .NET работает очень быстро, коллекции № 0 очень дешевы.

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

1 голос
/ 24 февраля 2009

Если вы используете один буфер, вам нужна стратегия того, как быстро он должен расти при необходимости. Если вы увеличиваете его небольшими приращениями, вам, возможно, придется делать это часто и часто копировать все данные. Если вы увеличиваете его большими приращениями (например, следующий размер в 1,5 раза превышает предыдущий), вы рискуете столкнуться с ситуацией, когда вы получаете «Недостаточно памяти», просто пытаясь увеличить буфер. Это выбор без потерь для масштабируемой системы. Вот почему повторное использование небольших буферов предпочтительнее.

...