Несомненно, вопрос, вынесенный в заголовок статьи, не нов, поднимался не раз и по нему достигнут консенсус «не особо нужна, и даже может быть вредна».
Однако недавнее обсуждение в комментариях заставило меня ещё раз задуматься.
Со временем любой SSD всё равно сильно фрагментируется (внутри, в FTL)… Свежезаписанный SSD при линейном чтении даст высокую скорость, а уже поработавший — гораздо ниже, потому что линейными оно будет только для вас.
Да, обычно такое не должно происходить: или мы пишем «понемногу» в мелкие файлы/небольшие блоки метаинформации ФС (скорость линейного чтения которых нас не особо волнует), либо же мы пишем «помногу» в большие файлы и всё будет хорошо. Бывает и дозапись мелкими блоками в большие файлы — логи, например, однако они относительно короткоживущие и особой проблемы я тут не вижу.
Но легко представился вполне реальный сценарий, при котором всё-таки внутренняя фрагментация SSD может проявиться: файл базы данных, в который идёт достаточно активная случайная запись. Со временем он (оставаясь нефрагментированным на уровне операционной системы) окажется физически очень даже фрагментированным, что может существенно снизить скорость seq scan, резервного копирования и т.п.
Для проверки я написал скрипт и провёл тесты.
Спойлер: проблема присутствует (существенно влияет на производительность) только на одной из попавшихся под руки моделей (и та позиционируется производителем не как datacenter, а как десктопная/ноутбучная).
Если в двух словах, SSD устроен очень непросто. В NAND flash можно писать (точнее стирать) только большими блоками. А операционная система видит SSD как набор 512-байтовых (или 4096-байтовых) секторов, каждый из которых может быть адресован независимо.
Чтобы как-то это совместить, придумана такая вещь, как FTL (flash translation layer): данные во flash-памяти лежат не последовательно, а (очень условно) в том порядке, в котором они были записаны, что-то вроде log-структурированных файловых систем.
Такие структуры очень хорошо обрабатывают случайную запись, превращая её в последовательную, но, увы, ничто не бывает бесплатно — в результате зачастую последовательное чтение превращается в случайное.
Алгоритмы, по которым работают FTL, закрыты, однако, насколько мы можем судить, у разных производителей они могут кардинально различаться. Соответственно, кардинально может различаться и поведение накопителей под нагрузкой.
Именно это мы и будет исследовать.
Идея скрипта: создаём файл на несколько гигабайт, заполненный случайными данными, замеряем скорость последовательного чтения.
Далее используя случайный доступ переписываем часть тестового файла и снова измеряем скорость линейного чтения. Если наши подозрения верны, то теперь чтение из файла будет идти медленнее.
После каждой записи делаем по три операции чтения с задержкой между ними на случай, если какой-то накопитель в фоне производит дефрагментацию и потом скорость чтения улучшится.
Не раз встречал обзоры, в которых запускают чтение с нового накопителя, получают какие-то фантастические цифры и, ничтоже сумняшеся, публикуют их. Через какое-то время тест повторяют уже на не столь девственном диске, и вдруг оказывается, что время доступа выросло, а скорость, соответственно, упала.
Дело в поддержке TRIM: контроллер внутри SSD может «знать», что в конкретном блоке нет полезных данных, информация об этом хранится в FTL. И при запросе на чтение из такого блока он не обращается к медленной NAND flash, а сразу возвращает нули. На новом накопителе все блоки помечены как неиспользуемые, соответственно, в тестах на чтение он готов ставить рекорды. Только нас же интересует с какой скоростью SSD умеет отдавать не нули, а данные.
Кроме этого, некоторые накопители умеют сжимать данные, и на хорошо сжимаемых тестовых данных могут показывать не совсем те результаты, которые будут в реальной жизни.
Поэтому, перед тестированием стоит заполнять SSD несжимаемыми данными (в linux хорошим источником может служить /dev/urandom
).
тестовый файл создаётся в текущем каталоге.
тестировал только под linux c dash, coreutils и fio из debian buster, с другими дистрибутивами навряд ли будут проблемы, а вот под freebsd и другие операционные системы скорее всего скрипт придётся «допиливать».
echo preparing...
dd if=/dev/urandom of=testfile bs=1M count=4096 status=none
sync
for A in 1 2 3; do
sleep 10
dd if=testfile of=/dev/null bs=1M iflag=direct
done
for A in 50 200 800 4000; do
echo fio: write ${A}M...
fio --name=test1 --filename=testfile --bs=4k --iodepth=1 --numjobs=1 --rw=randwrite --io_size=${A}M --randrepeat=0 --direct=1 --size=4096M > /dev/null
sync
for B in 1 2 3; do
echo sleep ${B}0
sleep ${B}0
dd if=testfile of=/dev/null bs=1M iflag=direct
done
done
echo sleep 3600
sleep 3600
dd if=testfile of=/dev/null bs=1M iflag=direct
Обнаружилось, что NVMe-накопители intel у меня сейчас только на серверах с windows; пришлось с помощью гугла, stackexchange и какой-то матери слепить вариант и под винду
Из внешних зависимостей только fio
; путь к exe-файлу и временному файлу указывается в первых строчках скрипта.
$testfile = "c:\temp\testfile"
$fio = "c:\temp\fio-3.18-x64\fio"
echo "preparing..."
$filestream = New-Object System.IO.FileStream($testfile, "Create")
$binarywriter = New-Object System.IO.BinaryWriter($filestream)
$out = new-object byte[] 1048576
For ($i=1; $i -le 4096; $i++) {
(new-object Random).NextBytes($out);
$binarywriter.write($out)
}
$binarywriter.Close()
For ($i=1; $i -le 3; $i++) {
sleep 10
$time = Measure-Command {
Invoke-Expression "$fio --name=test1 --filename=$testfile --bs=1M --iodepth=1 --numjobs=1 --rw=read --direct=1 --size=4096M" *>$null
}
$seconds = $time.Minutes*60+$time.Seconds+$time.Milliseconds/1000
echo "read in $seconds"
}
foreach ($A in 50,200,800,4000) {
echo "fio: write ${A}M..."
Invoke-Expression "$fio --name=test1 --filename=$testfile --bs=4k --iodepth=1 --numjobs=1 --rw=randwrite --io_size=${A}M --randrepeat=0 --direct=1 --size=4096M" *>$null
For ($i=10; $i -le 30; $i+=10) {
echo "sleep $i"
sleep $i
$time = Measure-Command {
Invoke-Expression "$fio --name=test1 --filename=$testfile --bs=1M --iodepth=1 --numjobs=1 --rw=read --direct=1 --size=4096M" *>$null
}
$seconds = $time.Minutes*60+$time.Seconds+$time.Milliseconds/1000
echo "read in $seconds"
}
}
rm $testfile
Получил следующие результаты:
- фоновой дефрагментации в тестируемых моделях не обнаружено: скорость чтения не повышается через некоторое время после записи, в том числе длительный «отстой» (час и даже более суток) ничего не меняет, поэтому в таблице ниже привожу просто лучший результат из трёх запусков;
- под windows почему-то время чтения менее стабильно и оказалось выше ожидаемого (впрочем, возможно, дело в том, что эти сервера оказались более нагружены);
- продолжение записи сверх указанного в скрипте (перезапись файла более одного раза) не влияет на производительность.
Время чтения (в секундах) файла размером 4Гб для разных дисков:
Диск | Первое чтение после последовательного заполнения файла | После случайной записи 50Мб | +200Мб | +800Мб | +4000Мб |
---|---|---|---|---|---|
intel S3510 SSDSC2BB480G6 | 10.7 | 10.7 | 10.8 | 10.8 | 10.8 |
toshiba XG5 KXG50ZNV512G | 1.9 | 2.9 | 3.7 | 4.8 | 6.8 |
samsung PM963 MZQLW960HMJP | 2.8 | 3.2 | 3.5 | 3.7 | 4.2 |
samsung PM983 MZQLB960HAJR | 3.3 | 3.6 | 3.4 | 3.4 | 3.4 |
samsung PM981 MZVLB1T0HALR | 1.8 | 1.8 | 2.1 | 2.5 | 3.5 |
samsung PM1725b MZPLL1T6HAJQ | 1.8 | 1.9 | 2.0 | 2.3 | 2.9 |
micron 5200 eco | 9.3 | 9.8 | 10.4 | 12.2 | 10.7 |
samsung PM883 MZ7LH1T9HMLT | 7.9 | 7.9 | 8.1 | 8.1 | 8.0 |
intel P3520 (win) | 5.8 | 5.9 | 6.0 | 6.1 | 5.8 |
intel P4500 (win) | 4.2 | 4.2 | 4.3 | 4.4 | 4.3 |
Жирным отмечены DC модели (остальные — десктопные/ноутбучные); где SATA, а где NVMe, думаю, видно без пояснений.
Мы видим, что по мере случайной записи в файл у самсунга PM981 скорость чтения падала и в итоге упала вдвое (но осталась, правда, достаточно неплохой), а у единственной тошибы в таблице — вовсе в 3.5 раза, фактически сравнявшись с таковой у SATA устройств.
С другой стороны, у большинства устройств случайная запись или вовсе не повлияла на производительность, или повлияла незначительно.
Моя интерпретация этих результатов: скорость линейного чтения у SSD действительно может деградировать со временем, однако деградация, вызванная внутренней фрагментацией, не носит совсем уж фатального характера на большинстве дисков (на дисках intel, например, она вовсе незаметна; на дисках samsung если и заметна, всё равно скорость чтения остаётся вполне приемлемой).
Остаётся открытым вопрос деградирует ли скорость чтения со временем по другим причинам (например, из-за износа NAND flash).
Могу сказать про тошибу XG5: разницы в поведении между диском, на который по SMART было записано >>150Тб, и новым диском я не заметил — или 300-400 перезаписей недостаточно, чтобы износ flash стал заметен, или он вовсе не влияет на производительность SSD.
По поводу падения производительности после случайной записи: у меня как раз на такой тошибе хранится достаточно нагруженная БД mysql размером около 100Гб. Действительно, в полном соответствии с изложенными выше теорией и измерениями, скорость чтения «боевых» таблиц mysql оказалась достаточно низкой (около 600Мб/с), скорость же чтения других крупных файлов с той же файловой системы гораздо выше (>2Гб/с).
Если хочется побороть, то можно воспользоваться одним из первых методов дефрагментации: делаем бэкап, удаляем файлы, восстанавливаем из бэкапа. Недостаток этого метода в том, что он достаточно долгий и подразумевает downtime (а через некоторое время данные во флеш-памяти снова окажутся фрагментированными и всё придётся повторять сначала). Так что проще или смириться, или выбирать диски, которые не подвержены этой проблеме.
Придумал относительно быстрый способ избавиться от внутренней (и только от внутренней) фрагментации SSD:
sync
fsfreeze -f /mountpoint
dd if=/dev/nvme0n1p2 of=/dev/nvme0n1p2 bs=512M iflag=direct oflag=direct status=progress
fsfreeze -u /mountpoint
Его можно запустить на «живой» системе без размонтирования ФС и остановки сервисов. Он тоже может привести к некоторому простою из-за замораживания ФС, но при желании можно разбить его на несколько итераций, чтобы уменьшить время, на которое замораживается ввод-вывод. Есть ещё одно «но»: я не уверен на 100%, что все SSD правильно обрабатывают ситуацию «пишем нули в область, для которой до этого делали TRIM» (то есть с точки зрения накопителя области ФС, на которые ранее делали TRIM, могут теперь считаться не свободными, а занятыми данными).
В целом, рекомендация «забить смириться или выбирать диски» остаётся в силе.
Резюме: дефрагментация может быть полезна для некоторых SSD, однако не совсем такая (совсем не такая?) как для HDD. Нам важно не только то, что файл расположен в непрерывной цепочке секторов, но и то, что запись в эти секторы шла последовательно.
P.S. был бы благодарен, если бы читатели запустили скрипт у себя и привели цифры для своих SSD, так как моя выборка накопителей достаточно однобокая.
paluke
А почему вообще для ssd скорость чтения зависит от того, читаем мы последовательно или в случайном порядке? Позиционирования головок то нет.
GPavlikh
Вангую что зависит от поддерживаемых контроллером команд обращения к памяти. Одни поддерживают кэшированное чтение по случайным адресам, а другие — только последовательное.
redsh0927
В иопсы упирается видимо. Лично я после установки системы ссд дефрагментирую. Пусть системные файлы лежат последовательно — хуже не будет, а несколько гигов ресурса флеша погоды не сделают.
Akon32
Последовательно? Но в ssd же таблицы трансляции адресов, которые ОС не контролирует…
У вас есть замеры скорости до и после дефрагментации?
HardWrMan
Как я уже написал ниже, фрагментация на уровне FS может приводить к дополнительным чтениям, что опять же тратит такой ресурс как IOPS.
redsh0927
По идее расположение данных во флеше вообще роли не играет. Всё равно ссд для каждого блока лезет в таблицу трансляции. Тем не менее последовательное чтение файла гораздо быстрее. Пока команда дойдёт до таблицы адресов — уже сожрёт изрядно времени цпу и контроллера ссд…
HardWrMan
Именно. А IOPSы складываются из времени доступа к матрице FLASH. Например, если взять типичную NAND, то у неё между матрицей FLASH и шиной есть два буфера, размер которых совпадает с размером страницы чтения/записи (не путать с размером блока стирания, он больше). И при выполнении команды чтения время на загрузку этого буфера не нулевое и вполне конкретное (например, для K9F4G08U0A это 25мкс). Но если изменить алгоритм чтения и задействовать второй буфер, то совмещение времени выборки данных из матрицы и выгрузкой их на шину данных позволит получить почти двухкратный прирост. При записи это время ожидаемо сильно растёт.
Накопители же используют сразу несколько микросхем в параллель для увеличения скорости чтения/записи, используют достаточно крупный набортный буфер ОЗУ, чтобы скрыть время работы с массивом FLASH между отсутствием обращения к накопителю со стороны системы. Сами NANDы тоже развиваются в плане достижения более высоких скоростей работы, но базовый принцип работы NAND это не отменяет.
Именно поэтому, скорость работы SSD следует оценивать не по линейной скорости а конкретно по IOPS. Причём, не по маркетинговым цифрам а в тестах. Ведь, даже если читать фрагментированный большой файл или папку то приходится делать дополнительные чтения метаданных.
scanters
Данные «последовательно», или «равномерно и симметрично размазанными по чипам и блокам флэша» не будут находится — FTL будет их часто двигать для выравнивания износа ячеек, не только из-за записи но даже из-за интенсивного чтения.
tunelix
всегда мучал вопрос: если у меня пол диска разбито на разделы и еще половина не разбита. диск использует ту часть для записи или елозит только по разбитой области?
scanters
Зонирование и неймспейсы еще большая редкость, да и-то будут доступны только для NVMe.
А вообще SSD пишут во всю доступную емкость флэша, и параллельно на все чипы NAND, только данные по блокам не всегда будут симметрично записаны, иногда вне очереди влетает срочная запись от сборщика мусора или flush от драйвера. Так что данные на NAND в принципе почти всегда фрагментированы, вопрос в том, насколько они равномерно замаплены на флэше, чтобы их можно было в параллели читать, или что-то приходится читать в одну нитку с одного чипа (в худшем случае).
inetstar
Диск использует весь объём для записи.
edo1h Автор
накопитель вообще ничего не знает о разделах. для него есть набор секторов, в некоторых из которых есть данные (если вы неиспользуемую область никак не инициализировали, то с точки зрения накопителя все эти сектора пустые).
у накопителя в результате куча свободного места, которое он может использовать для записи не запуская сборщик мусора, это называется over-provisioning, почитать можно, например, тут.
скорее опасна обратная ситуация: 90% диска забито статичными данными (скажем, коллекция фильмов), а в оставшиеся 10% идёт активная запись (ну, например своп, и на машине у вас куча виртуалок, которым не хватает памяти). если вся запись пойдёт только в эти 10% диска, то ресурс этих областей быстро исчерпается, чтобы такого не произошло, накопитель должен периодически перемещать записанные данные. но как это реализовано (и реализовано ли во всех накопителях) — мы не знаем.
saboteur_kiev
У вас странные выводы.
Вы протестировали несколько устройств, получили РАЗНЫЕ результаты, в том числе и явно свидетельствующие, что некоторые производители уже решили эту проблемы.
Но вывод у вас «SSD деградируют».
Так давайте все-же честно интерпретировать результаты — некоторые модели некоторых производителей имеют неоптимальные алгоритмы, из-за чего они могут деградировать в таких случаях.
edo1h Автор
поясните, пожалуйста, к чему относится ваш комментарий. перечитал несколько раз свой комментарий, на который вы ответили —ничего про «SSD деградируют» я в нём не писал.
HardWrMan
Быть может речь за это?
mrzar
Напоминает:
"Лично я садясь на самолёт на всякий случай окропил его святой водой, перекрестился. Хуже не будет, а покой дорог, однако"
redsh0927
Ну, кому напоминает — пусть хихикают. А мне не напоминает: если эффект от религиозных ритуалов ни один прибор не засекает, то чтение каждого дополнительного блока файла — это вполне конкретная ~сотня микросекунд накладных расходов.
rst07
Автор предположил, что возможно это связано с трансляцией блоков, т.е. файловая система устроена блоками по 512 байт или 4кбайта, а на диске физически лежит всё немного по другому, например, может быть по 4 килобайта, или ещё как нибудь, например страница по 16 кб, в этом случае сперва читаем кусочек из одной страницы кратный сектору нашей ФС, а потом другой, и так читая по кусочкам получается что мы прочитали например 10 страниц по 16 кб, а забрали оттуда например всего 10х512 байт. Такая же проблема с фрагментацией есть на HDD, но там не только связано с расположением секторов на диске, но и перемещение головки на новую дорожку. Кстати с появлением HDD с сектором 4К появилась проблема с выравниванием секторов, вроде бы эта же проблема появилась и на SSD.
edo1h Автор
очень правильный вопрос.
в первоначальном обсуждении мы выяснили, что срабатывают эвристики операционной системы на упреждающее чтение.
вот с тестовой машины:
первая попытка: последовательное чтение блоками по 4кб (включается упреждающее чтение операционной системы), имеем >>400к iops.
вторая попытка: случайное чтение, имеем 12.4к iops (операционная система догадывается отключить упреждающее чтение).
третья попытка: последовательное чтение в обход кэша, имеем 12.5к iops (операционная система не может использовать упреждающее чтение).
третья попытка: последовательное чтение с явно отключенным упреждающим чтением, имеем 12.5к iops.
итого: именно упреждающее чтение обеспечивает более высокую скорость линейного чтения на SSD (операционная система сбрасывает на диск несколько операций чтения сразу, что позволяет устройству обрабатывать их параллельно).
почему же при внутренней фрагментации падает скорость линейного чтения — сие пока для меня тайна великая есть.
inetstar
Ответ тут.
На верхней части этой картинки:
edo1h Автор
не понимаю.
контроллеру пришли 32 команды "прочитай 4кб сектор", какая ему разница, расположены физически эти сектора подряд, или разбросаны по нескольким страницам с «дырками»?
inetstar
Это можно объяснить только тем, что он читает не по 4КБ, а гораздо большими областями, например, целыми страницами. Думаю, это основной момент.
У многих профессиональных SSD сектор вообще 512 байт. Явно это сделано в угоду совместимости, а не является оптимальным размером для NAND.
Плюс не нужно тратить время на адресацию микросхем, блоков. Скорее всего там многоступенчатая система адресации.
Вот из википедии:
saboteur_kiev
Так ведь уже с 2011 года размер сектора 4к у всех HDD
Почему SSD все еще 512байт спустя 10 лет?
Тем более что кластера и блоки по дефолту тоже уже 4к лет 10.
inetstar
Нет, не у всех. У многих HDD сектор 512N и 512E.
У большинства SSD сектор 512 байт.
Потому что сектор 512 байт универсальнее. Вот почему. Например, vmware лучше работает с сектором 512 байт.
yatanai
Так ведь чипов не 32 шт. чтоб считать сразу со всех одновременно, а некоторые архитектуры ещё имеют нелинейную адресацию, что может вызывать проблемы*
Из тех что я тыкал, были, условные, команды -«читать не переставая», «читать х последовательно», «читать одну страницу». Притом время на команду была сильно разная, и иногда было выгодней «дефрагментировать» и читать целиком 1МБ, а не по 100 раз обрывая чтение. Разница при этом может быть до ~40% (могу и врать, это было года 3-4 назад)
edo1h Автор
извиняюсь, поторопился с выводами.
скорость последовательного чтения с отключенным упреждающим чтением равна скорости случайного чтения не на всех моделях SSD. видимо, некоторые модели делают упреждающее чтение во внутренний буфер (или просто оперируют при чтении блоками размером больше 4Кб)
inetstar
А почему вы так прицепились к 4КБ? У большинства ссд логический блок вообще 512 байт. Поэтому можно было написать "блоками больше 512 байт".
edo1h Автор
но мы же обычно не обращаемся к блочным устройствам напрямую, а используем файловые системы.
размер блока по умолчанию в ext4 и xfs как раз и есть 4кб. ЕМНИП в ntfs размер кластера по умолчанию тоже 4кб.
inetstar
А файловые системы обращаются к диску, используя размер именно логического сектора накопителя. Нет никакой гарантии, что диск всегда умеет правильно объединять эти сектора.
В особенности при параллельной нагрузке.
saboteur_kiev
Эм, эта проблема была решена очень давно, еще в 2010-2012 годах. Некоторые производители винчестеров даже предоставляли align tool.
Сейчас при разбитии диска на разделы, современный софт сам правильно начинает новый раздел.
inetstar
Это совершенно другая проблема. Когда логический ФС сектор был не на границе физического сектора устройства.
saboteur_kiev
Это именно оно. WD align и другие подобные утилиты как раз и выравнивали начало раздела (то есть кластера ФС) с началом сектора.
khajiit
Чтение и запись группы секторов были включены еще в ATA-2, раздел 10.6.
saboteur_kiev
Вы уверены, что первая попытка это было упреждающее чтение, а не просто кэш от предыдущей операции с этим файлом?
inetstar
Потому что erase block сильно больше write block. И при чтении будет читаться много лишнего, если write block'и раскиданы по разным erase block.
paluke
К чтению erase block никакого отношения не имеет.