Я пытаюсь найти наилучшую структуру данных для использования на высокопроизводительном сервере С++. Структура данных будет использоваться для хранения всего от нескольких до нескольких миллионов объектов, и сортировка не требуется (хотя уникальный ключ сортировки может быть предоставлен очень дешево).
Требования заключаются в том, что он может поддерживать эффективную вставку, в идеале O (1), умеренно эффективное удаление и эффективный обход. Он не нуждается в поддержке операции поиска (кроме необходимости удаления).
Твист состоит в том, что он должен быть потокобезопасным по отношению к модификациям, в то время как другие потоки перечисляют структуру данных. Это означает, что простое красно-черное дерево не работает, так как один поток не может вставить элемент (и выполнять необходимые вращения дерева), не испорчая никаких курсоров, удерживаемых другими потоками.
не рекомендуется использовать блокировку чтения/записи и откладывать операции записи до тех пор, пока все читатели не закончат, так как операции чтения могут быть долговечными. Не имеет значения, видимы ли вставки, которые происходят во время чтения, читателю или нет.
След за память также очень важен, а маленький, очевидно, лучше!
Какие существуют предложения?
Ответ на комментарии:
Спасибо за ответы.
Нет, вставки не могут аннулировать существующие итераторы. Итераторы могут видеть или не видеть новую вставку, но они должны видеть все, что они увидели бы, если бы вставка не произошла.
Требуется удаление, однако из-за правил более высокого уровня я могу гарантировать, что итератор никогда не будет остановлен на элементе, доступном для удаления.
Блокировка за node для курсора окажет слишком большое влияние на производительность. Одновременно может просматриваться несколько потоков, и любое горячее пятно памяти, которое использует несколько потоков в замке, убивает пропускную способность памяти (поскольку мы обнаружили трудный путь!). Даже простой счетчик с несколькими потоками, вызывающий InterlockedIncrement, не может масштабироваться чисто.
Я согласен, что связанный список, вероятно, лучший подход. Удаления редки, поэтому уплата штрафа за память для обратных указателей для поддержки O (1) delete является дорогостоящей, и мы можем вычислить их отдельно по требованию, а так как удаления имеют тенденцию к пакетным операциям.
К счастью, вставка в связанный список не требует блокировки для читателей, если указатели обновлены во вложенном node до изменения указателя головы.
Идея блокировки-разблокировки интересна. Объем данных слишком большой, чтобы это работало по умолчанию для читателей, но оно могло использоваться для писателей, когда они сталкиваются с читателями. Блокировка чтения/записи защитит всю структуру, и запись будет клонировать структуру данных, если она столкнется с читателем. Записи гораздо реже, чем чтение.