LINUX.ORG.RU

Какие есть средства ускорить рандомную запись в один конкретный файл?

 ,


1

5

Есть один здоровый файл, несколько гигов, в который пишу и читаю 50/50 по 50 байт за раз в рандомном порядке. По замерам через rdtsc чтение происходит за порядка 5тыс тактов, а запись за порядка 100тыс тактов. Какие есть средства ускорения процесса записи? Включая переформатирования или покупку железа, если это не дорого обойдется? Сейчас это на обычном HDD на Ext4 под убунтой.

ну 20х это сильно, смотрите что в фоне крутится, проблема может быть не только в hdd/scheduler. попробуйте сварганить свой init, и уже реально посмотреть чтение/запись на эталонной системе

etwrq ★★★ ()
Последнее исправление: etwrq (всего исправлений: 1)

решение в лоб - ssd. но лучше разбить процесс на чтение/запись в памяти и синхронизацию с диском, а еще лучше при таких объемах какой-нибудь redis, чтобы не костылять свое решение при наличии годных поделок за тысячи человекочасов.

Anoxemian ★★★★★ ()

Есть один здоровый файл, несколько гигов, в который пишу и читаю 50/50 по 50 байт за раз в рандомном порядке.

Это не здоровый файл, даже близко. Такие объёмы нет смысла читать/писать на диск, особенно в «рандомном порядке».

Какие есть средства ускорения процесса записи?

Докупить планку на 2гига? А дальше прикручиваешь дамп памяти на диск и журнал. Получаешь минимум х100.

vcerloman ()

Re: Какие есть средства ускорить рандомную запись в один конкретный файл?

Писать последовательно. Современные HDD могут писать последовательно со скоростью 80-100 мб/сек, но случайная запись (ровно как и чтение) убивают производительность (порядка сотни seek-ов в секунду).

anonymous ()

По результатам текущих экспериментов получилось:

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

2. применение mmap, точнее Qt варианта QFile::map тоже немножко ускорило, хотя чуда не принесло.

На текущий момент остаются непонятки с тем, что в некоторые моменты запись начинает подвисать. Часть операций записи происходит быстренько, а часть с затыками в 20х раз медленнее чем операции чтения, и чем больше файл, тем таких затыков все больше.

А может кто подсказать, как писать/читать диск напрямую? Что бы исключить издержки работы с фатом, и прочими системными навесами. В том смысле, что отведу под эту штуку диск и без всяких разбивок писать напрямую.

victor79 ()
Ответ на: комментарий от victor79

заранее резервирую под будущие записи

в догонку, после QFile::resize обязательно нужно выполнить QFile::size, иначе resize будет выполняться асинхронно, и следствия: 1. если на этот файл сразу сделать map на весь размер, то будет ошибка. 2. если в текущем процессе выполняются еще другие добавления, то дефрагментация все равно останется.

А при выполнении size ожидается окончание resize. flush не помогает.

victor79 ()
Ответ на: комментарий от victor79

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

А в чём проблема отдать под эту штуку планку на 2гига, вместо диска? И не получать никаких проблем.

vcerloman ()

Как это делают в СУБД: разбивают пр-ва данных на страницы, накапливают в памяти изменённые страницы (грязные ~) и потом их сбрасывают на диск. Если в одну страницу попадает несколько обновлений, то получается ускорение. Аналогично работает и запись в самой ОС, но результат будет зависеть от фаз луны и настроек.

Второй вариант, это писать не рандомно. а в последовательный лог и при достижении определённого размера лога компактить данные. Такой вариант требует дополнительный иднекс для лога в памяти и фоновый процесс переписывания всех данных (компакшн). Так примерно работает репозиторий в git, да и СУБД тоже, в особенности т.н. noSQL.

anonymous ()
Ответ на: комментарий от victor79

А может кто подсказать, как писать/читать диск напрямую? Что бы исключить издержки работы с фатом, и прочими системными навесами.

Чтобы работать с диском напрямую нужно сначала придумать как будешь это делать, т.е. реализовывать кеши и прочую мелочь, которую с обычной записью реализует ядрро. Иначе можешь получить просадку в производительности.

anonymous ()

пишу .... по 50 байт

Стандартный размер блока НЖМД - 512 байт. Размер страницы памяти - 4096 байт. Отправлять на диск данные меньше размера блока - это трата производительности впустую. На SSD внутренние блоки ещё больше, хотя seek time намного меньше и поначалу производительности может хватать. Аноним правильно про лог посоветовал - лучше писать подряд.

legolegs ★★★★★ ()
Последнее исправление: legolegs (всего исправлений: 1)

Кэши у меня есть, но мне нужно именно на диске, т.к. данных впоследствии будет куда больше нескольких гигов.

Последовательный журнал изменений не годится, т.к. размер записей и размер узла индекса к этому журналу будет сопоставим, а фоновый процесс переделки из журнала в таблицу будет сам конкурировать с операциями записи и чтения, которые будут постоянными. Хотя может позже и до этого дойду.

Результат дальнейших наблюдений: все записи сделал пакетными, и отделил от чтения. И наблюдаю, что при файлах от гигов, операции чтения замедляются в 1000 раз, причем не после очередного пакета для записи, а в промежутке между пакетами шли с таким то временем, а чуть позже начали с большим в 1000 раз.

Виновата в этом операционка (Ubuntu 16), больше винить не кого. Другие процессы не влияют, т.к. замедление зависит от нарастания объема файлов моей программы, и пишет она на персонально выделенный диск. И SMART не влияет, т.к. его работа не зависит от размера файла. Открытие файлов делал через QIODevice::Unbeffered, но это похоже не влияет. Использование mmap ускоряет, но как постоянная составляющая.

Остается только переделывать на прямые обращения к диску.

victor79 ()
Ответ на: комментарий от anonymous

Ты что-то явно делаешь не то!
Покайся! грешник, для чего
тебе нужно писать в файло?

мат.статистика. Частота встречания паттернов. Что-то подобное поиску, но посимвольное и с расчетом вероятностей. Если более глобально, то это алгоритм прогнозирования.

victor79 ()

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

victor79 ()
Ответ на: комментарий от victor79

Так если это статистические данные (читай — числа, привязанные ко времени), может лучше взять какую-нибудь оптимизированную под хранение таких данных БД и работать с ней? Сходу могу предложить OpenTSDB и InfluxDB.

spijet ()
Ответ на: комментарий от victor79

Уже в общем написали в треде на что нужно смотреть, сделаю краткую выжимку:

  • ОС старается минимально фрагментировать файл, можно считать что он будет располагаться на диске более-менее последовательно
  • ОС держит в кеше куски файла, изменения сбрасываются на диск или по желанию ОС, или по просьбе пользователя, принудительно
  • Чтение/запись произвольного куска файла, который не в кеше состоит из позиционирования и собственно операции. Время позиционирования в зависимости от расстояния (т.е. от смещения в файле) может быть разным, вот тут можно немного почитать https://en.wikipedia.org/wiki/Hard_disk_drive_performance_characteristics
  • После позиционирования и чтения/записи ОС пытается прочитать некоторое количество данных больше чем запросили. Можно погуглить по prefetch или readahead как это настраивается
  • Последовательные операции (без seek) довольно быстрые

Остается только переделывать на прямые обращения к диску.

Не факт что если делать «в лоб» это даст какой-то выигрыш

Если нужно непредсказуемо скакать по огромному файлу (или диску) и писать/читать очень короткие куски, то эффективнее как уже посоветовали купить SSD/менять алгоритмы

vmx ★★ ()

После ресайза пиши нули на всём новом пространстве, а то ОС не будет выделять заранее место на диске, что при рандомной записи выльется в адское фрагментирование.

unC0Rr ★★★★★ ()
Ответ на: комментарий от victor79

Ты всё перепутал.

По замерам через rdtsc чтение происходит за порядка 5тыс тактов, а запись за порядка 100тыс тактов.

Ты совершенно не то намерил, никаких образом чтение у тебя не может быть 5тыс тактов. Ты писал в кеш, а не на диск.

Да и запись маловероятно, что реальная. Т.к. никакой диск тебе не даст десятки тысяч иопсов на РА.

Результат дальнейших наблюдений: все записи сделал пакетными, и отделил от чтения. И наблюдаю, что при файлах от гигов, операции чтения замедляются в 1000 раз, причем не после очередного пакета для записи, а в промежутке между пакетами шли с таким то временем, а чуть позже начали с большим в 1000 раз.

Правильно, потому что записи и нет. На реальной записи, на реальном РА - ты получишь с харда миллионы тактов на гигагерц, а не сотни тысяч.

Виновата в этом операционка (Ubuntu 16), больше винить не кого.

Виноват в этом victor79, а не операционка.

vcerloman ()
Ответ на: комментарий от vcerloman

spijet Так если это статистические данные (читай — числа, привязанные ко времени), может лучше взять какую-нибудь оптимизированную под хранение таких данных БД и работать с ней? Сходу могу предложить OpenTSDB и InfluxDB.

Нет, это агрегации по таким последовательным данным, что в итоге является обычными таблицами, а не добавляемыми в конец. Я уже попробовал SQLite, lmdb, leveldb, - все они проигрывали на более чем в десять раз специализированно прописанному мной алгоритму без них.

vmx Если нужно непредсказуемо скакать по огромному файлу (или диску) и писать/читать очень короткие куски, то эффективнее как уже посоветовали купить SSD/менять алгоритмы

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

vcerloman Ты всё перепутал.

Да я все перепутал. Но я потихоньку продвигаюсь в понимании. А что такое RA(РА)?

Переделал на прямую работу с /dev/disk/... и оно все равно идет не линейно, все равно ядро кэширует это устройство, хотя и немного по другому это проявляется. И как только забивается кэш, то сразу все тормозится. Кто-либо знает как полностью отключить кэширование на одном выбранном устройстве, и читать/писать напрямую с устройства посекторно? Так хоть не будет двойного кэширования и будет понимаемая картина что сколько выполняется. sudo hdparm -W0 изменений в выполнении не дало, хотя и написало, что для этого устройства WriteCache=disabled.

victor79 ()
Ответ на: комментарий от victor79

Переделал на прямую работу с /dev/disk/... и оно все равно идет не линейно, все равно ядро кэширует это устройство, хотя и немного по другому это проявляется.

На самом деле это тебе мало что даст. Дело в том, что кеш и ммап работает со страницами, которые не могут быть заполнены на половину. Т.е. с диска читаться/записываться они должны постранично, и если страница у нас 4к, а надо записать 50, то запишется 4к.

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

Ну и вот, эта запись 4к, вместо 50( тут надо так же учитывать то, какими блоками пишется на твой девайс. Они точно меньше 4к, но явно не 50) - съедает твой трупут, т.е. «линейную скорость записи».

Выше уже кто-то говорил, что это 512байт, что в 4раза меньше страницы. И если диск может 200раз в секунду передвигать головку на новое место, а его скорость линейного чтения ~50-100MB, то он спокойно может читать блоки по пол мегабайта.

Я не знаю, как это организованно и может ли быть так, что эти 4записи по 512 запишутся в разное время, а не вместе. Это, вроде как, должны побеждать планировщики. Хотя и смысла допускать такого нет, но кто знает.

Ну дак и вот, эта «прямая запись» - может решить( в твоём случае) именно эту гипотетическую проблему, которой скорее всего и нет.

И как только забивается кэш, то сразу все тормозится. Кто-либо знает как полностью отключить кэширование на одном выбранном устройстве, и читать/писать напрямую с устройства посекторно?

А с чего ты решил, что это что-то даст? Даже если у кеша будет ну прям гигантская летенси, то всё равно это будет на пору порядков меньше, нежели летенси диска.

Так хоть не будет двойного кэширования и будет понимаемая картина что сколько выполняется. sudo hdparm -W0 изменений в выполнении не дало, хотя и написало, что для этого устройства WriteCache=disabled.

В open есть флаг, а вообще - есть ядерное aio. Это именно то, что тебе нужно. Осталось найти биндинги.

Я не использовал open(), но скорее всего там всё не так просто с блокировками и ты намучаешься.

vcerloman ()
Ответ на: комментарий от vcerloman

А с чего ты решил, что это что-то даст? Даже если у кеша будет ну прям гигантская летенси, то всё равно это будет на пору порядков меньше, нежели летенси диска.

Если я буду видеть и понимать какая именно вещь и почему тормозит, то я под нее смогу подправить алгоритм. В open() нет отключения кэширования, есть флаг O_SYNC, но это не отключение кэширование, это подобие flush после каждой операции записи.

Кажется я поторопился задавать вопрос как отключить кэширование. Похоже на /dev/disk/... его если и есть то мало. Я с ним работаю через mmap, и кэширование происходит на этом маппировании. Отсюда и изменение поведения с остающимся впечатлением что кэшируется. Это наблюдение я заключил из /proc/meminfo который показывает, что Cached мало меняется, зато Mapped раздувается, и еще другие косвенные наблюдения. В общем попробую поэксперементировать с периодическими munmap либо делать без mmap.

Про страничную запись, это я все понимаю, fdisk -l показывает, какой оптимальный размер страницы у какого диска. Просто не видел, что выравнивание на страницы что-то меняет, у меня там на половину такое сделано, и большая часть страниц отправляется на запись в последовательности возрастания расположения (а все начиналось с таблички в SQLite...)

victor79 ()
Ответ на: комментарий от victor79

Если я буду видеть и понимать какая именно вещь и почему тормозит, то я под нее смогу подправить алгоритм. В open() нет отключения кэширования, есть флаг O_SYNC, но это не отключение кэширование, это подобие flush после каждой операции записи.

O_DIRECT

Про страничную запись, это я все понимаю, fdisk -l показывает, какой оптимальный размер страницы у какого диска.

Это не те «страницы».

Просто не видел, что выравнивание на страницы что-то меняет, у меня там на половину такое сделано, и большая часть страниц отправляется на запись в последовательности возрастания расположения (а все начиналось с таблички в SQLite...)

А оно и не должно влиять. Абсолютно неважно то, сколько там запишется страниц, естественно, что до тех пор пока всё упирается в летенси.

Как я уже говорил, если взять летенси диска за 1/200 секунды, то на диск спокойно можно писать хоть по 20/160 страниц - это ничего не изменит. И естественно, что лишняя смена одной страницы на две - ничего не даёт.

Ах да, что такое РА - это random access.

vcerloman ()