Открыть сервис

Циклический буфер

Циклический буфер (также кольцевой буфер, кольцевая очередь, англ. circular buffer, ring buffer) — это структура данных, представляющая собой фиксированный по размеру массив, в котором запись и чтение данных осуществляются циклически: при достижении конца массива указатель переходит на его начало. Циклический буфер используется для организации очередей фиксированной длины, где требуется эффективное управление потоком данных без динамического перераспределения памяти.

Принцип работы

Циклический буфер основан на линейном массиве фиксированного размера. Для управления операциями записи и чтения используются два указателя (или индекса): указатель записи (head, tail в зависимости от реализации) и указатель чтения (tail или head). При записи данных в буфер указатель записи увеличивается на единицу; при достижении последнего элемента массива он сбрасывается на нулевой индекс. Аналогично, при чтении данных указатель чтения также перемещается циклически.

Состояние буфера определяется взаимным расположением указателей:

  • Если указатели равны, буфер считается пустым.
  • Если указатель записи находится на один шаг позади указателя чтения (в циклическом смысле), буфер считается полным.

Для различения пустого и полного состояния часто используется дополнительный счётчик количества элементов, флаг заполненности или резервирование одного элемента массива, который никогда не используется для хранения данных.

История

Концепция циклического буфера возникла в контексте ранних вычислительных систем и цифровой обработки сигналов. Одним из первых применений стали буферы в телетайпах и последовательных интерфейсах, где требовалось сглаживать разницу в скорости передачи и приёма данных. В 1960-х годах циклические буферы начали активно использоваться в операционных системах для организации каналов ввода-вывода и очередей сообщений. С развитием микропроцессорной техники и встраиваемых систем кольцевые буферы стали стандартным решением для работы с прерываниями, АЦП, DMA и аудиопотоками.

Классификация

Циклические буферы классифицируются по нескольким признакам.

По режиму доступа

  • Одно-производитель, один-потребитель (SPSC) — простейший вариант, где один поток (или процесс) пишет данные, а один — читает. Не требует блокировок при корректной реализации.
  • Много-производителей, один-потребитель (MPSC) — несколько потоков могут записывать данные, но читает только один. Требует синхронизации записи.
  • Один-производитель, много-потребителей (SPMC) — один источник данных, несколько читателей. Используется реже.
  • Много-производителей, много-потребителей (MPMC) — наиболее сложный вариант, требующий атомарных операций и блокировок.

По способу синхронизации

  • Блокирующие — используют мьютексы, семафоры или условные переменные для ожидания освобождения места или появления данных.
  • Безблокировочные (lock-free) — реализуются с помощью атомарных операций (CAS, fetch-and-add) и не требуют приостановки потоков. Широко применяются в высоконагруженных системах.

По размеру

  • Фиксированного размера — размер буфера задаётся при инициализации и не меняется.
  • Динамического размера — могут расширяться или сжиматься, но это нарушает основное преимущество кольцевого буфера — предсказуемость и отсутствие перераспределения памяти.

Устройство и характеристики

Основные компоненты циклического буфера:

  • Массив данных — непрерывная область памяти фиксированной длины.
  • Указатель записи (write index, head) — индекс следующей свободной ячейки для записи.
  • Указатель чтения (read index, tail) — индекс следующей ячейки для чтения.
  • Счётчик элементов (опционально) — количество хранящихся в буфере данных.

Ключевые характеристики:

  • Ёмкость (capacity) — максимальное количество элементов, которое может хранить буфер.
  • Текущий размер (size) — количество элементов в буфере в данный момент.
  • Задержка (latency) — время выполнения операций записи и чтения, обычно O(1).
  • Пропускная способность (throughput) — количество операций в единицу времени, ограниченная скоростью копирования данных и атомарных операций.

Применение

Циклические буферы находят применение в самых разных областях вычислительной техники и программирования.

Встраиваемые системы и микроконтроллеры

В микроконтроллерах циклические буферы используются для организации очередей прерываний, буферизации данных от АЦП, приёма и передачи данных по UART, SPI, I2C. Благодаря фиксированному размеру и отсутствию динамического выделения памяти они идеально подходят для систем с ограниченными ресурсами.

Аудио- и видеообработка

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

Сетевые протоколы и операционные системы

В ядрах операционных систем циклические буферы применяются в драйверах сетевых карт (кольцевые буферы приёма/передачи пакетов), в реализации каналов (pipes) и очередей сообщений. Например, в Linux кольцевые буферы используются в подсистеме звука ALSA, в сетевом стеке (NAPI), в механизме futex.

Базы данных и журналирование

В системах управления базами данных циклические буферы применяются для реализации журналов предзаписи (WAL), где старые записи перезаписываются новыми. Это позволяет ограничить объём дискового пространства, занимаемого журналом.

Потоковая обработка данных

В системах реального времени, таких как Apache Kafka, циклические буферы используются для хранения сообщений в памяти перед записью на диск. В библиотеках для параллельного программирования (например, LMAX Disruptor) безблокировочные кольцевые буферы обеспечивают рекордную пропускную способность.

Примеры реализации

Простейшая реализация на C

```c

define BUFFER_SIZE 16

typedef struct { int buffer[BUFFER_SIZE]; size_t head; size_t tail; size_t count; } circular_buffer;

void cb_init(circular_buffer *cb) { cb->head = 0; cb->tail = 0; cb->count = 0; }

int cb_push(circular_buffer *cb, int data) { if (cb->count == BUFFER_SIZE) return -1; // переполнение cb->buffer[cb->head] = data; cb->head = (cb->head + 1) % BUFFER_SIZE; cb->count++; return 0; }

int cb_pop(circular_buffer cb, int data) { if (cb->count == 0) return -1; // пусто *data = cb->buffer[cb->tail]; cb->tail = (cb->tail + 1) % BUFFER_SIZE; cb->count--; return 0; } ```

Безблокировочная реализация для SPSC

В высокопроизводительных системах применяются атомарные операции для обновления указателей, что позволяет избежать блокировок. Например, в библиотеке LMAX Disruptor используется кольцевой буфер с последовательностями (sequence), где каждый потребитель хранит свою последовательность чтения, а производитель — последовательность записи.

Преимущества и недостатки

Преимущества

  • Постоянное время операцийзапись и чтение выполняются за O(1), независимо от размера буфера.
  • Отсутствие динамического выделения памятипамять выделяется один раз при инициализации, что предотвращает фрагментацию и снижает накладные расходы.
  • Эффективность для потоковой обработки — данные могут записываться и читаться непрерывно, без копирования или перемещения.
  • Простота реализации — базовая версия может быть реализована несколькими строками кода.

Недостатки

  • Фиксированный размер — при переполнении буфера данные теряются (если не предусмотрена блокировка или перезапись старых данных).
  • Сложность синхронизации — в многопоточных сценариях требуется аккуратное управление указателями и, возможно, блокировки.
  • Ограниченная применимость — не подходит для задач, где требуется произвольный доступ к элементам или изменение размера очереди.

Интересные факты

  • В некоторых реализациях циклического буфера размер массива выбирается равным степени двойки. Это позволяет заменить операцию взятия модуля на побитовое И (например, index & (size-1)), что значительно ускоряет выполнение.
  • В ядре Linux кольцевые буферы используются в механизме perf_event_open для сбора событий производительности, а также в подсистеме trace для хранения трассировочных записей.
  • В аудиоинтерфейсах профессионального уровня (например, ASIO) циклические буферы применяются для минимизации задержки между захватом и воспроизведением звука.

Источники

  • Кормен Т., Лейзерсон Ч., Ривест Р., Штайн К. Алгоритмы: построение и анализ. — 3-е изд. — М.: Вильямс, 2013.
  • Таненбаум Э., Бос Х. Современные операционные системы. — 4-е изд. — СПб.: Питер, 2015.
  • Martin Thompson. Mechanical Sympathy: LMAX Disruptor. — Technical report, 2011.
  • Документация ядра Linux: Documentation/circular-buffers.txt.
  • ISO/IEC 9899:2018 — стандарт языка C.

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →