Отказ от ответственности: пожалуйста, не беспокойтесь о прочтении этого длинного поста, если вы не являетесь программистом-встраиваемым разработчиком, разработчиком ядра или высокопроизводительным системным программистом. Речь идет о распределителях памяти и может не представлять для вас никакого интереса.
Я создаю библиотеку для высокопроизводительного высокопроизводительного IPC на том же компьютере, используя специальные каналы ОС (именованные каналы в Windows и доменные сокеты UNIX в UNIX). Библиотека получает сообщения и вставляет их в обработчик, и этот механизм может быть в том же процессе в C ++ или что-то вроде Erlang или Lua, или, возможно, перенесен на центральную машину.
Сообщения в данный момент поступают из веб-сервера и являются частью конвейера разрешения HTTP-запроса и должны масштабироваться вместе с веб-сервером, т. Е. Я не могу написать неэффективную систему, потому что веб-серверы рабочие потоки зависят от скорости, с которой они могут разгрузить эти сообщения. Если веб-сервер способен обрабатывать 10 тыс. Запросов статических страниц, этот механизм IPC не должен мешать ему.
Данные поступают в виде печатных сообщений, и я полагаю, что они будут иметь собственную статистику размера, которую можно было бы использовать. Распределитель Дуга Ли создает связанные списки сегментов по размерам, если я правильно помню, возможно, именно так я и должен поступить - это стандарт общего распределителя.
Я хочу семантику нулевого копирования, поэтому пока сообщение не будет обработано, его не следует копировать. С другой стороны: это, возможно, удаляет буферы протокола Googles из картинки (на данный момент не может быть уверенным), так как формат проводника отличается от формата объекта в памяти. Я буду исследовать дальше - моя текущая реализация использует мой собственный формат IPC; возможно, у кого-то есть мнение о том, как протокольные буферы работают, использует ли он нулевое копирование внутри?
Я хочу использовать этот распределитель во всех областях приложения. Мое исследование программного обеспечения сетевого стека побуждает меня к распределителю на основе выровненного по страницам процессора; мое приложение, безусловно, имеет такую же семантику доступа. Я скопировал бы все сообщения на страницу и перезапустил бы страницы только тогда, когда все ссылки на элементы - которые выделены для пула - неактивны.
Чтобы немного формализовать и помочь вам понять, о чем я думаю, рассмотрите следующую структуру данных:
typedef struct _page
{
int active_elements; // page is returned to empty pool at zero
int free_bytes; // used to sort the pages
void * next_free_contigious; // pointer to where next allocation may occur
_page * next_page; // linked list next page pointer
} page;
struct pageAllocator
{
page * ll_empty; // completely empty pages
page * ll_active; // sorted by highest_size_available, could be binary tree
// continue reading for rationale.
};
Некоторые пункты:
- Сообщения не будут задерживаться. Так что страницы скоро станут свободными.
- Мне нужно написать пуленепробиваемый код, чтобы освободить страницы.
- Я не знаю, должно ли page-> next_free_contigious быть совмещенным со словом.
- Я предполагаю, что под давлением количество выделенных страниц будет стабильным.
- Должен ли я возвращать страницы в ОС или я должен просто сохранять их? Максимальное давление будет выделяться столько, сколько ему нужно, только если будет установлено максимальное давление.
- Должен ли я выделять сообщения первому элементу в отсортированном по размеру списке (сначала самое высокое) или найти страницу с наиболее подходящим соответствием. В этом случае мне пришлось бы использовать какое-то двоичное дерево. Я повторно реализую алгоритм Дуга здесь?
Я хочу, чтобы предложения от людей, которые лично имели дело с такими вещами, приветствуются альтернативные предложения распределителя. Было бы неплохо иметь готовые библиотеки (они должны быть реализацией мета-кода препроцессора c-ANSI-C). APR - это популярная библиотека со стратегией, основанной на иерархическом пуле, но я не занимаюсь сессиями на этом низком уровне, поэтому мне не нужна иерархия.
Не думаю, что я предварительно оптимизирую, потому что убежден, что мне это нужно. Моя текущая реализация этой системы переходит на 30% ЦП во время насыщения ЦП веб-сервера, и я знаю, что это из-за моего наивного способа программирования приложения с использованием новых и множества конструкций копирования в C ++. Я, конечно, буду тестировать приложение, прежде чем продолжить (я слишком занят, чтобы заново создать тестовую среду), но любое обсуждение здесь будет полезно независимо от результата.
--- редактировать 1 ---
Эта реализация была вдохновлена некоторыми комментариями Маркра, проверьте ответ ниже для некоторого обсуждения. Немного больше размышлений приведет к следующему.
Хорошо, сейчас я думаю о кольцевом буфере (скажем, 1mb ^ x). Состоит из элементов одинакового размера (возможно, кратных 128 байтам). Распределения происходят постепенно до конца (с пометкой на голове allocatex (A), откуда могут происходить выделения, и свободным маркером (F), отмечающим приглушенные свободные области (0), возвращаемые в пул. Рассмотрим следующую иллюстрацию ASCII:
|000000F101110001111A00000000|
0 = свободен, 1 = занят.
Некоторые конкретные:
- Свободный маркер разграничивает свободные смежные области. На рисунке F будет двигаться только два вперед, когда будет возвращен непосредственно следующий фрагмент.
- Как только выделение достигает конца, или в конце недостаточно места для выполнения непрерывного выделения, головка выделения перемещается в начало; к этому моменту большая часть буфера должна быть свободной.
- как только сообщение перемещается к месту назначения, куски немедленно возвращаются в буфер.
- При доставке на той же машине сообщения будут доставляться в короткие сроки (с некоторыми блокировками) или сразу, если для этого требуется просто вызов функции без потоков.
Комментарии было бы неплохо, и Я бы по-прежнему предпочел бы какую-нибудь автономную переносимую библиотеку, если кто-нибудь знает какую-либо , которая делает что-то подобное. Распределитель фрагментов GLIBC выглядел хорошо, но я считаю, что приведенная выше реализация проще и, вероятно, быстрее, и активные области в буфере всегда будут иметь хорошую локальность.