LINUX.ORG.RU

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

 ,


1

6

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

А как вы читаете/пишете? read/write? Делаете принудительный сброс на диск? Пробовали mmap()-ить и потом обрабатывать?

Deleted
()

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

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

А как вы читаете/пишете? read/write?

QFile::write, QFile::read

Попробую mmap.

ну 20х это сильно, смотрите что в фоне крутится

пока файл маленький, то там 3х относительно чтения.

victor79
() автор топика

Использовать бд вроде sqlite вместо файла

annulen ★★★★★
()

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

Anoxemian ★★★★★
()

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

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

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

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

vcerloman
()

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

anonymous
()

Плюсꙋю докꙋпить планкꙋ памѧти. Нꙋ еще можно диск сменить на хорошꙋю NVMe.

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

Сомнительное удовольствие, когда к целерону можно воткнуть 64гига оперативки.

anonymous
()

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

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

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

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

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

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

А может кто подсказать, как писать/читать диск напрямую?

я поторопился задавать этот вопрос, нашел ответ за пару минут в гугле: fopen(«/dev/sdX»,...

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

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

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

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

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

На самом деле это мало на что повлияет. Попробуй собрать статистику и сделать профилирование.

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

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

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

vcerloman
()

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

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

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

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

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

anonymous
()

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

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

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

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

vcerloman
()

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

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

Результат дальнейших наблюдений: все записи сделал пакетными, и отделил от чтения. И наблюдаю, что при файлах от гигов, операции чтения замедляются в 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/менять алгоритмы

Deleted
()

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

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
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.