Read Look-Ahead и read-ahead (readahead) - это баг или фича

archive view archive save

hdd-logo1.jpg Кто бы мог подумать, что Read Look-Ahead и read-ahead (readahead) могут оказаться страшнейшим багом, а не фичей! Баг сей ярко выражен в жутком свопинге, когда выполняется чтение больших файлов, в результате которого всё встаёт колом на одном месте без движений в какую либо сторону.

Днями ранее мы писали про проблему со скачиванием большого файла БД Беркли размером 80G, когда FileZilla вместо 80G накачало целых 83G и качало бы дальше ели бы её принудительно не остановили (перекачивали заново wget-ом).

Так вот, пробовали мы этот файл БД Беркли 80G как-то читать...

Всё встаёт колом на одном месте без любых движений в какую либо сторону. iotop показывает скорость чтения под 60+ МБ, но в итоге всей системе наступает .опа.

Изменения любых параметров подсистемы vm.* никак не влияли на итоговый результат.

  1. Описание read-ahead (readahead)
  2. Отключение read-ahead (readahead)
  3. Бенчмарк
  4. readahead=16384 reduce performance
  5. readahead after reboot
  6. Откуда берётся 16384 для readahead
  7. readahead - бага или фича

Описание read-ahead (readahead)

Если короче говоря, простыми словами подытожить, то все эти фичи read-ahead и read-lookahead, -

  • в пределах дорожки диска (секторов) с которой были запрошены данные, кроме фактически нужных данных ещё дополнительно последовательно (подряд) читает шо попало на случай а вдруг в хозяйстве пригодится (как одна поломанная лыжа на балконе);
  • нужные данные вместе с readahead-шлаком складываются тебе в оперативку (в кеш-буфера), а когда она заканчивается, то весь тот высер передвигается тебе в своп;
  • при чтении больших файлов, которые ещё могут быть разосраны разбросаны (aka non-contiguous) по по куче разных дорожек/секторов (участков диска), а также при больших значениях readahead в настройках диска, - объёмы лишних данных оперативка -> своп могут нарастать как снежный ком;
  • аналогичная ситуация может произойти даже в случае, когда запрошенные данные расположены на одной дорожке, - это вовсе не означает, что программе кроме фактически запрошенных потребуются ещё и дополнительно считанные readahead данные;
  • проблема особо ярко выражена, когда программа выполняется с неконтролируемой (нет настроек) многопоточностью (threads);
  • финал выше изложенного думаю заранее известен: заканчивается РАМа, заканчивается своп, и наступает.., - ночь.

Вот, как-то так оно (упреждающее чтение) и работает:

Всё выше изложенное в равной степени применимо и к упреждающему чтению с файла подкачки (vm.page-cluster).

На мелких файлах read-ahead (readahead) может быть полезна, но при работе с большими файлами, - может превратится в страшнейший БАГ!

Это касается не только HDD, но и SSD дисков, где тормоза могут быть не такими лютыми, но принцип работы read-ahead (readahead) от этого не меняется!

Отключение read-ahead (readahead)

Отключение read-ahead (readahead) на уровне ОС:

sudo vi /etc/sysctl.conf
...
vm.swappiness=0
vm.page-cluster=0
 
sudo sysctl -p

Отключение read-ahead (readahead) на уровне HDD:

# setting fs readahead to 0
sudo hdparm -a0 /dev/sda
 
# setting drive read-lookahead to 0 (off)
sudo hdparm -A0 /dev/sda
 
# setting multcount to обычно 8 или 16 (по-умолчанию)
sudo hdparm -m8 --yes-i-know-what-i-am-doing /dev/sda

Справедливости ради нужно отметить, что изначальное значение readahead в настройках HDD было подозрительно и неприлично велико (16384):

# sudo hdparm /dev/sda
 
/dev/sda:
 multcount = 16 (on)
 IO_support = 1 (32-bit)
 readonly = 0 (off)
 readahead = 16384 (on)
 geometry = 60801/255/63, sectors = 976773168, start = 0

Т.е. 16384 секторов * 512 б (традиционный HDD sector size, диск 500 ГБ) / 1024 = 8192 кб - это 8 мб упреждающего чтения получилось?!

Возникает вопрос, как такое неприлично большое значение появилось в настройках HDD?! Когда, после отключения попытка установить то же самое значение 16384 завершились ошибкой:

sudo hdparm -a16384 /dev/sda
 -a: bad/missing filesystem-read-ahead value (0..2048)

Возможно это результат работы старой версии hdparm, в которой не было такого лимита.

В текущей же версии программы hdparm значение для readahead допустимо в пределах 0-2048 сектора, что при 512 б разметке равно 1 МБ, что в условиях малого количества РАМы да при чтении больших файлов вероятно тоже многовато будет.

ОС манипулирует блоками: In practice, operating systems typically operate on blocks of data, which may span multiple sectors.[3]

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

ls -l /dev/sda
brw-rw---- 1 root disk 8, 0 сен 21 17:28 /dev/sda
 
sudo blockdev --getbsz /dev/sda1
4096

Ещё раз подумав над вопросом: Так readahead и read-lookahead - это бага или всё же фича? Упреждающее чтение попробовали включить взад, но с малыми значениями:

# 8 КБ упреждающего чтения
sudo hdparm -a16 /dev/sda
 
# включаем взад read-lookahead
sudo hdparm -A1 /dev/sda

А что с отключенным vm.page-cluster=0, какие там размер чтения linux swap page-cluster size? Ответ очевиден из документации ядра Линуха: в своп скидываются страницы РАМы, RAM page size которых можно проверить выполнив:

grep -ir pagesize /proc/self/smaps
KernelPageSize: 4 kB
MMUPageSize: 4 kB
 
sudo getconf PAGESIZE
4096

Так и сделаем:

sudo sysctl -w vm.page-cluster=1

vm.page-cluster=1 будет читать по 2 страницы * 4 КБ = 8 КБ, что соразмерно с новыми настройками readahead=16 в нашем многострадальном HDD.

Бенчмарк

Никаких бенчмарк-прог не нужно, разницу можно заметить по так сказать органолептическим показателям.

После изменения значения read-ahead (readahead) с аномально высокого 16384 на 16, а vm.page-cluster=1, при вычитке файла БД Беркли 80G стало возможным ещё и одновременная работа до 10 программ, среди которых браузер, текстовый редактор, почтовый клиент, ФМ, HDD заметно зажужжал и стал ощутимо греется.

В сравнении с тем, что было и что стало, - это космос, Аллилуйя!

Вот тебе и весь бенчмарк.

readahead=16384 reduce performance

В том, что 8MB (16384 секторов * 512 байт) упреждающего чтения с диска в кэш оперативной памяти, - это слишком много, убедились не только мы.

При чтении множества файлов маленьких размеров и таких размерах лишнего кеша системе непреиенно наступит жопа!

readahead=16384 снижает производительность - это может, кто.., подтвердит:

Poor READ performance on RAID5/RAID6/SANwith PE1950(gen3) under RHEL Linux | DELL Technologies

...

It's important to note that the read-ahead value needs to be tuned to the specific workload of the server. 8MB is too large for lots of random access of small blocks of data, since the server ends up pre-fetching data that will be discarded.

Значение readahead подбирается для каждого конкретного случая в отдельности:

  • для файловых серверов можно увеличивать;
  • для веб-серверов нужно уменьшать.

Кроме того, ещё нужно учитывать общую нагрузку и объёмы имеющейся в наличии оперативной памяти!

Например даже при больших размерах файлов (1 ГБ и более), их последователном размещении (записи) на диске, оптимальных значениях read-ahead, но с малым объёмом оперативной памяти, - кэша в РАМе под большие размеры readahead не хватит, поплывёт оно всё на своп, система снова встанет колом.

readahead after reboot

Ещё одно горе, когда после перезагрузки возвращается прежнее значение readahead=16384.

readahead=16384 устанавливается автоматически, очевидно операционной системой, на все диски включая СД-карты и внешние диски.

Тогда, в /etc/rc.local добавим одну или две команды сразу для надёжности:

vi /etc/rc.local
 
sudo hdparm -a16 /dev/sd*
# or
sudo blockdev --setra 16 /dev/sd*

Команд в /etc/rc.local на VPS серверах будет достаточно, но на рабочих станциях, где съёмные внешние диски туда-сюда отключаются-подключаются нужно создать udev rule для автоматического изменения readahead=16384 на оптимальное под нашу систему при подключени любого внешнего диска или microSD карт - значение read_ahead_kb устанавливается НЕ в секторах диска, а в КБ (8 кб = 16 секторов по 512 байт)!

# vi /etc/udev/rules.d/99-disk.rules
 
# Show param for udev rule:
# udevadm info -ap /sys/block/sda
# Test udev rule for dev:
# udevadm test --action=add /sys/block/sda
# Get param after dev mount/attach
# sudo blockdev --getra /dev/sda
# sudo hdparm -a /dev/sda
#
# for all other block devices set a readahead
SUBSYSTEM=="block", ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{bdi/read_ahead_kb}="8"

Откуда берётся 16384 для readahead

Так, а откуда же всё-таки берётся 16384 для readahead!?

Ведь это значение откуда-то же произрастает? Точно не из прошивки диска потому, что для всех подключаемых дисков это значение одинаково = 16384.

Значение 16384 для readahead точно устанавливается операционной системой Linux, настройки которой хранятся преимущественно в текстовых файлах - давайте искать:

# sudo sysctl -a|grep 16384
fs.fanotify.max_queued_events = 16384
fs.inotify.max_queued_events = 16384
fs.pipe-user-pages-soft = 16384
kernel.msgmnb = 16384
net.ipv4.tcp_wmem = 4096 16384 4194304
 
# grep -rnw '/etc/' -e '16384'
/etc/mke2fs.conf:7: inode_ratio = 16384
/etc/X11/app-defaults/Xedit:325:*ispell*list.longest: 16384
/etc/init.d/tor:46: MAX_FILEDESCRIPTORS=16384
/etc/xpra/xorg.conf:47: #Modeline "16384x8192" 167.75 16384 16800 18432 20480 8192 8195 8205 8208
/etc/xpra/xorg.conf:1642: #Virtual 16384 16384
/etc/xpra/xorg.conf:1651: #Virtual 16384 16384
/etc/xpra/xorg.conf:1660: #Virtual 16384 16384
/etc/xpra/xorg.conf:1669: #Virtual 16384 16384
/etc/xpra/xorg-uinput.conf:51: #Modeline "16384x8192" 167.75 16384 16800 18432 20480 8192 8195 8205 8208
/etc/xpra/xorg-uinput.conf:1643: #Virtual 16384 16384
/etc/xpra/xorg-uinput.conf:1652: #Virtual 16384 16384
/etc/xpra/xorg-uinput.conf:1661: #Virtual 16384 16384
/etc/xpra/xorg-uinput.conf:1670: #Virtual 16384 16384
 
# grep -rnw '/boot/' -e '16384'
/boot/config-6.1.0-23-686:2461:CONFIG_BLK_DEV_RAM_SIZE=16384
/boot/config-6.1.0-23-686-pae:2455:CONFIG_BLK_DEV_RAM_SIZE=16384
/boot/config-5.10.0-32-686-pae:2360:CONFIG_BLK_DEV_RAM_SIZE=16384
/boot/config-5.10.0-32-686:2369:CONFIG_BLK_DEV_RAM_SIZE=16384
/boot/config-6.1.0-23-rt-686-pae:2458:CONFIG_BLK_DEV_RAM_SIZE=16384
/boot/config-5.10.0-0.deb10.27-rt-686-pae:2360:CONFIG_BLK_DEV_RAM_SIZE=16384

Не оно всё? Нет.

16384 - это в секторах, попробуем поискать в КБ - 16384 / 2 = 8192:

# sudo sysctl -a|grep 8192
# grep -rnw '/boot/' -e '8192'
# grep -rnw '/etc/' -e '8192'
...
/etc/rc.local:134:echo 8192 > /sys/block/sda/queue/read_ahead_kb <<<--- ВОТ ОНО
...
/etc/udev/rules.d/60-cfqconfig.rules:5:ACTION=="add|change", KERNEL=="sd[ab]", ATTR{queue/read_ahead_kb}="8192", ... <<<--- ВОТ ОНО

Конфигурация под старый давно уже выбывший из обихода планировщи cfq, вместо которого теперь [mq-deadline] none.

readahead - бага или фича

Ответ всегда зависит от того, с какой точки зрения смотреть на предмет.

Важно не только чем, куда, когда, зачем и почему, но также важно ещё и как.., - это что-то происходит!

Факторов множество и от их параметров конфигурации зависит конечный результат, а также ответ на вопрос: Так readahead и read-lookahead - это бага или всё же фича?

Этот HDD диск нам Любі друзі підарілы, с настройками которого очевидно ранее уже экспериментировали либо такое безумное значение readahead=16384 изначально зацементировано в прошивке диска производителем, или они устанавливаются операционной системой при старте.

В ходе исследования данной темы выяснилось, что значение readahead=16384 устанавливалось операционной системой из конфиг файлов /etc/rc.local и /etc/udev/rules.d/60-cfqconfig.rules.

С прежними значениями readahead в 16384 секторов, при чтении 80-гигового файла БД readahead оказался лютым багом, но при изменении значения на 16 секторов (8 КБ) readahead превратился в полезную фичу.

Вывод однозначен:

  1. НЕ ВСЕ мануалы по Performance Tuning on Linux — Disk I/O одинаково полезны;
  2. При малом количестве оперативной памяти, независимо от скорости диска (будь то HDD или SSD), небольших файлах (хостинг сайтов и т.д.), повышенные значения (по-умолчанию queue/read_ahead_kb = 128) превращают упреждающее чтение readahead в багу;
  3. При достаточном количестве оперативной памяти, больших файлах, увеличение значения read_ahead_kb может быть фичей;
  4. read_ahead_kb нужно изменять осторожно, с учётом общей нагрузки системы и других параметров /sys/block/xxx/queue/;
  5. В некоторых случаях упреждающее чтение с диска лучше даже полностью отключить.

Превращение readahead из бага в фичу заняло секунды времени, в отличии от времени потраченного на написание данного материала.

Выше изложенное в очередной раз подтверждает, что больше не всегда означает лучше.

Под занавес упомянём про ещё одну не менее интересную переключалку, отключение которой в некоторых случаях сможет повысить производительность чтения/записи на диске - front_merges (/sys/block/sd?/queue/iosched/front_merges).

Полезно будет почитать:

P.S. Разные рабочие нагрузки, разные случаи, а подбор конфигурации для системы чем-то схож на подбор диеты, когда нужно чтобы и волки были сыты и овцы целы, - Трудно быть Богом! :)


Комментарии в блоге
Новое на форуме