Как лучше всего управлять поведением буферизации Linux при написании потока данных с высокой пропускной способностью?

моя проблема заключается в следующем: у меня есть приложение C/C++, которое работает под Linux, и это приложение получает поток данных с постоянной скоростью (~27 МБ/сек), который ему нужно передать в файл (или файлы). Компьютер, на котором он работает, является четырехъядерным 2GHz Xeon под управлением Linux. Файловая система ext4, а диск представляет собой твердотельный диск E-SATA, который должен быть достаточно быстрым для этой цели.

проблема в слишком умном поведении буферизации Linux. В частности, вместо записи данных диск сразу же, или вскоре после вызова write (), Linux будет хранить "записанные" данные в ОЗУ, а затем через некоторое время (я подозреваю, когда 2 ГБ ОЗУ начнет заполняться) он внезапно попытается записать несколько сотен мегабайт кэшированных данных на диск, все сразу. Проблема в том, что этот кэш-флеш большой и задерживает код сбора данных в течение значительного периода времени, что приводит к потере некоторых текущих входящих данных.

мой вопрос: есть ли разумный способ "настроить" поведение кэширования Linux, так что либо он вообще не кэширует исходящие данные, либо, если он должен кэшировать, он кэширует только меньшее количество за раз, тем самым сглаживая использование полосы пропускания диска и улучшая производительность кода?

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

7 ответов


можно использовать posix_fadvise() С POSIX_FADV_DONTNEED совет (возможно, в сочетании с призывами к fdatasync()), чтобы заставить систему очистить данные и выселить их из кэша.

посмотреть в этой статье для практического примера.


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

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


Если мы говорим о std:: fstream (или любом объекте потока C++)

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

streambuf * ios:: rdbuf (streambuf* streambuffer);

, определив свой собственный буфер, вы можете настроить поведение потока.

в качестве альтернативы вы всегда можете очистить буфер вручную с заданными интервалами.

Примечание: существует резонанс для наличия буфера. Это быстрее, чем писать диск напрямую (каждые 10 байт). Существует очень мало причин для записи на диск кусками меньше, чем размер блока диска. Если вы пишете слишком frquently, контроллер диска станет вашим горлышком бутылки.

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


вы можете настроить параметры кэша страницы в /proc/sys /vm, (см./proc/sys/vm /dirty_ratio,/proc/sys/vm / swappiness специально), чтобы настроить кэш страницы по своему вкусу.


Я знаю, что этот вопрос старый, но мы знаем несколько вещей, которые мы не знали, когда этот вопрос был впервые задан.

часть проблемы заключается в том, что значения по умолчанию для /proc/sys/vm/dirty_ratio и /proc/sys/vm/dirty_background_ratio не подходят для новых машин с большим объемом памяти. Linux начинает флеш при достижении dirty_background_ratio и блокирует все операции ввода-вывода при достижении dirty_ratio. ниже dirty_background_ratio, чтобы начать промывку раньше, и поднять dirty_ratio, чтобы начать блокировать ввод-вывод позже. В очень больших системах памяти (32 ГБ или более) вы можете даже использовать dirty_bytes и dirty_background_bytes, так как минимальный прирост 1% для настроек _ratio слишком груб. Читать https://lonesysadmin.net/2013/12/22/better-linux-disk-caching-performance-vm-dirty_ratio/ для более подробного объяснения.

кроме того, если вы знаете, что вам не нужно будет снова читать данные, вызовите posix_fadvise с помощью FADV_DONTNEED для обеспечения кэширует страницы можно повторно использовать раньше. Это должно быть сделано после того, как linux спустил страницу на диск, иначе флеш переместит страницу обратно в активный список (фактически отрицая эффект fadvise).

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


ну, попробуйте это решение с десятифунтовым молотком, которое может оказаться полезным, чтобы увидеть, способствует ли кэширование системы ввода-вывода проблеме: каждые 100 Мб или около того, вызовите sync().


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