4

Мне нужен один механизм записи и множественного считывателя (до 5), который автор нажимает на данные размером почти 1 МБ каждый и 15 пакетов в секунду непрерывно, что будет быть написанным в C++. То, что я пытаюсь сделать, это один поток, который продолжает записывать данные, в то время как 5 читателей будут делать некоторые операции поиска в соответствии с меткой времени одновременно. Я должен хранить каждый пакет данных 60 минут, а затем их можно удалить из контейнера.Буферный механизм Writer/Reader для больших размеров - данные с высокой частотой C++

Поскольку данные могут расти как 15 МБ * 60 с * 60 мин = 54000 МБ/ч Мне нужно почти 50 ГБ пространства для хранения данных и сделать операции достаточно быстрыми для писателя и читателей. Будем в том, что мы не можем хранить данные этого размера в кеше или ОЗУ, поэтому он должен быть на жестком диске, таком как SSD (жесткий диск будет слишком медленным для такого рода операций)

До сих пор, о чем я думал , чтобы сделать круговой буфер (так как я могу вычислить максимальный размер), непосредственно реализованный на SSD, который я не мог найти подходящего примера до сих пор, и я не знаю, возможно ли это или нет, или реализовать какой-то механизм отображения, который будет иметь один круговой массив в ОЗУ, который просто сохраняет временные метки данных и физического адреса памяти для поиска данных, доступных на жестком диске. Так что, по крайней мере, поисковые операции будут быстрее, я думаю.

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

EDIT: Есть ли какая-либо другая идея, а не больший объем оперативной памяти?

+0

Если верхний предел действительно 50 гб, я уверен, что вы можете получить машину, которая справляется с этим с легкостью. 128gb довольно стандартный для тяжелых рабочих станций. – stijn

+0

Если ваша память будет ограничена, просто сохраните последние несколько секунд в ОЗУ и сфотографируйте другую на диск, используя [файлы с отображением памяти] (http://en.wikipedia.org/wiki/Memory-mapped_file). –

+0

@JoachimPileborg, сохраняющий всего несколько секунд в ОЗУ, не помогает. Поскольку потоки чтения могут требовать данные из любого временного интервала, а не только из последних. – user2955554

ответ

0

54GB/час = 15MB/s. Хороший SSD в эти дни может писать 300+ Мбайт/с. Если вы сохраняете 1 час в ОЗУ, а затем иногда очищаете старые данные на диске, вы должны иметь возможность обрабатывать 10x больше 15 МБ/с (при условии, что алгоритм поиска будет достаточно быстрым, чтобы не отставать).

Что касается быстрого блокировочного механизма между вашими нитями, я бы предложил изучить RCU - Read-Copy Update. Ядро Linux в настоящее время использует его для достижения очень эффективной блокировки.

+0

На самом деле система должна работать для разных размеров и частот. Я имею в виду, что данные/сек могут быть больше для других реализаций, поэтому он должен быть общим. Но вы правы SSD достаточно хороши, чтобы заполнить счет. Но я не уверен, что SSD можно использовать как оперативную память, чтобы я мог реализовать любую структуру contaioner и делать все операции чтения и записи непосредственно над ней. А так как данные за 1 час превышают 50 ГБ, невозможно оставить их в ОЗУ, верно? – user2955554

+0

Вы можете найти материнскую плату, которая [поддерживает 128 ГБ ОЗУ] (http://www.newegg.com/Product/Product.aspx?Item=N82E16813131861).Это не дешево, но ваш общий компьютер не должен превышать 2000 долларов. – mvp

+0

@mvp: на самом деле супертяжелые серверы баз данных часто обходят стороной 1 ТБ/2 ТБ в настоящее время (не дешево, но человеко-дни работы тоже не дешевы). –

0

У вас есть минимальные требования к оборудованию? 54ГБ в памяти вполне возможно в наши дни (многие материнские платы могут принимать 4х16 ГБ в эти дни, и это даже не серверное оборудование). Итак, , если вы хотите потребовать SSD, вы, возможно, просто нуждаетесь в большом количестве оперативной памяти и имеете круговой буфер в памяти, как вы предлагаете.

Кроме того, если в данных имеется достаточная продуманность, может быть целесообразно использовать некоторые дешевые алгоритмы сжатия (те, которые легки на процессоре, то есть какое-то сжатие уровня 0). То есть вы не храните необработанные данные, а некоторый сжатый формат (и, возможно, некоторый индекс), который распаковывается читателями.

+0

Существует не такие строгие требования к оборудованию, но я не могу просто сосредоточиться на ОЗУ. Поскольку система должна быть дополнительно изменена, для разных и, возможно, больших размеров данных и более высоких частот. Поэтому достаточно использовать жесткий диск или, по крайней мере, SSD. – user2955554

+2

@ user2955554: 64 ГБ ОЗУ - это то, что может обрабатывать товарный ПК. В компании, в которой я работаю, у нас есть несколько супертяжелых серверов баз данных с 512 ГБ или 1 ТБ ОЗУ. Вероятно, будет дешевле (в долгосрочной перспективе) иметь прямую реализацию и больше оперативной памяти. –

3

Это может быть сделано на товарном ПК (и может масштабироваться до сервера без изменений кода).

Замки не являются проблемой. С одним автором и несколькими потребителями, которые занимаются трудоемкими задачами на больших данных, у вас будет редкая блокировка и практически полное нарушение блокировки, поэтому это не проблема.
Все, что угодно от простой спин-блокировки (если вы действительно отчаянно нуждаетесь в малой задержке), или, желательно, pthread_mutex (который в любом случае возвращается к тому, чтобы быть спин-блокировкой). Ничего особенного.

Обратите внимание: не получить блокировку, получить мегабайт данных из сокета, записать его на диск, а затем отпустить блокировку. Это не так, как это работает.
Вы получаете мегабайт данных и записываете их в регион , который у вас есть исключительно, затем приобретите замок, измените указатель (и, таким образом, передайте право собственности) и отпустите замок. Блокировка защищает метаданные, а не каждый байт в буфере гигабайтного размера. Длительные задачи, короткие времена блокировки, утверждение = ноль.

Что касается фактических данных, то выписать 15MiB/s абсолютно не сложно, обычный жесткий диск будет делать в 5-6 раз больше, а SSD будет легко делать в 10-20 раз. Это также не то, что вам даже нужно делать самому. Это то, что вы можете оставить в операционной системе для управления.

Я хотел бы создать 54.1GB файл на диске и памяти карты, (если это система 64-разрядная, разумное предположение, когда речь идет о многогигабайтных-RAM-серверов, это не проблема). Операционная система заботится обо всем остальном. Вы просто записываете свои данные в отображаемую область, которую вы используете как круговой буфер .
То, что было написано в последнее время, будет более или менее гарантировано быть резидентом в ОЗУ, чтобы потребители могли получить к нему доступ без сбоев. Более старые данные могут быть или не быть в ОЗУ, в зависимости от наличия у вашего сервера достаточного количества физической памяти.

Данные, которые старше, по-прежнему могут быть доступны, но, вероятно, на немного более медленной скорости (если нет достаточной физической памяти, чтобы сохранить весь резидентный набор). Это, однако, не повлияет на производителя или потребителей, читающих недавно записанные данные (если только машина не настолько ужасна, что она не может даже удерживать 2-3 блока 1MiB в ОЗУ, но тогда у вас есть другая проблема!).

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

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

Возможно, это возможно даже с атомными операциями, но, видя, как вы редко блокируете, я даже не буду беспокоиться. Это бесполезно, используя замок, и нет никаких тонких ошибок, которые вы можете сделать.

Поскольку производитель знает, что потребители будут рассматривать данные только в пределах четко определенных границ, он может писать в области за пределами границ (область, известная как «emtpy») без блокировки. Это необходимо только для фиксации, чтобы впоследствии изменить границы.

Как 54.1Gib> 54Gib, у вас есть сто запасных блоков 1MiB в сопоставлении, на которое вы можете писать. Это, вероятно, намного больше, чем нужно (2 или 3 следует делать), но не больно иметь несколько дополнительных. Когда вы пишете новый блок (и увеличиваете допустимый диапазон на 1), также настраивайте другой конец «допустимого диапазона».Таким образом, потокам больше не будет разрешен доступ к старому блоку, но поток, все еще работающий в этом блоке, может завершить свою работу (данные все еще существуют).
Если вы строго придерживаетесь правильности, это может создать условие гонки, если обработка блока занимает очень много времени (более 1 1/2 минуты в этом случае). Если вы хотите быть абсолютно уверены, вам понадобится еще один замок, который может в худшем случае блокировать производителя. Это то, чего вы абсолютно не хотели, но блокирование продюсера в худшем случае - единственное, что на 100% правильное в каждом случае, если гипотетический компьютер не имеет неограниченной памяти.
Учитывая сложившуюся ситуацию, я думаю, что эта теоретическая гонка является «допустимой». Если обработка одного блока на самом деле занимает так много времени, так как количество данных постоянно поступает, у вас гораздо более серьезная проблема, поэтому практически, это не проблема.

Если ваш босс решает, в какой-то момент в будущем, что вы должны сохранить более 1 часа отставания, вы можете увеличить файл и переназначить, а когда «пустая» область будет следующей в конце старой размера буфера, просто расширьте «известный» размер файла и настройте значение max_size в производителе. Потребительские потоки даже не нужно знать. Разумеется, вы могли бы создать другой файл, скопировать данные, обменять и заблокировать потребителей в среднем, но я считаю, что это плохое решение. Вероятно, нет необходимости в том, чтобы увеличение размера было сразу видно, но, с другой стороны, очень желательно, чтобы это был «невидимый» процесс.
Если вы поместили больше ОЗУ в компьютер, ваша программа будет «волшебным образом» использовать его, без необходимости ничего менять. Операционная система будет просто хранить больше страниц в ОЗУ. Если вы добавите еще нескольких потребителей, они все равно будут работать одинаково.


Преднамеренно больше, чем то, что вам нужно, пусть будет несколько «лишних» блоков 1MiB.

Предпочтительно связываться, вы можете madvise операционную систему (если вы используете систему, которая имеет разрушительную DONT_NEED подсказку, например, Linux), что вы больше не заинтересованы в содержании перед перезаписью области. Но если вы этого не сделаете, это будет работать в любом случае, только немного менее эффективно, потому что ОС, возможно, выполнит операцию чтения-изменения-записи, где операции записи было бы достаточно.

Существует, конечно, никогда не действительно гарантию, но это то, что будет иметь место в любом случае.

+0

Большое спасибо за ваш подробный ответ. Но так как объяснение очень абстрактно, я в некоторых случаях смущен в его реализации. Вы знаете какой-либо пример реализации, документ, статью, образец кода и т. Д., Чтобы сделать его более конкретным? – user2955554

+0

Найти пример реализации, вероятно, будет сложно, так как это довольно специфическая проблема. Но вы, скорее всего, найдете примеры для большинства компонентов, которые вам нужны в вашей реализации. Картирование памяти является несколько системным и несколько низкоуровневым, поэтому нужно знать, по крайней мере, какую операционную систему или какой класс операционной системы (например, Linux, BSD, сервер Windows) вы намереваетесь запустить. Я предположил, что «сервер» == «POSIX», когда я сказал, например. «pthread_mutex», но вы, конечно, обычно можете делать то же самое в каждой операционной системе. – Damon

+0

Как правило, подход мертв просто. Вы используете ringbuffer, который несколько больше, чем 1-часовой обратный вызов, и вы защищаете доступ к метаданным ringbuffer (поля begin/end) с помощью мьютекса. Единственное, что является «особенным», это то, что вы помещаете его в область с отображением памяти, для которой требуется использовать специальный вызов операционной системы (например, mmap для POSIX и POSIX-подобных систем или CreateFileMapping/MapViewOfFile для систем Windows). Они хорошо документированы, но, как упоминалось выше, сначала нужно знать, в какой системе вы нацеливаетесь. – Damon

0

Много хороших рекомендаций вокруг. Я хотел бы просто добавить, что для реализации циклического буфера вы можете взглянуть на Boost Circular Buffer