HDD посвящается: усмиряем приложение, прожорливое на дисковое время

    Корень всех зол


    Долгое время у меня была проблема — система очень сильно тормозила после старта. У меня ноутбук с жёстким диском (HDD) и Ubuntu 14.04.
    Как выяснилось, причина крылась в одной лишь программе — демоне Dropbox. Dropbox — это онлайновое файловое хранилище, а его демон — программка, синхронизирующая файлы, расположенные в определённой папке, с онлайн-хранилищем. На старте демон начинает считывать свой кэш. У меня он занимает не одну сотню мегабайт, а удалять его вручную не стоит — есть вероятность потерять данные. Учитывая, что у меня жёсткий диск — устройство с механическими частями — демон начинал потреблять время доступа к нему настолько, что пользоваться компьютером и запускать приложения становилось малореально, пока он не прогрузится. Убрать его из автозапуска и запускать вручную? Неприятное решение, у меня и так есть вещи, которые я на старте вынужден запускать сам (например, iotop, он без прав суперпользователя не запускается). Нужно было найти способ сделать приложение менее прожорливым именно на диск.

    ionice нам в помощь. Или нет?


    Первое, о чём я услышал по этой теме, была утилитка ionice. Она позволяет изменять приоритет доступа к диску для заданного процесса. Согласно мануалу, существуют три класса приоритетности ввода/вывода. Меня интересовал idle, поскольку он, как я понял из описания, нейтрализует прожорливость приложения, пропуская всегда вперёд него процессы других классов приоритета. Казалось, дело в шляпе, и всё, что надо сделать, это:
    ionice -c3 -p $(pgrep dropbox)

    Сделано. Эффекта — ноль. Как же так? Ладно, роем дальше.

    Scheduler'ы, или «я ничего не понял, но звучит круто»


    Как выяснилось, по умолчанию ionice на моей системе вообще не функционирует, поскольку должен быть включён планировщик CFQ. А по умолчанию в новых Убунтах включён планировщик deadline. Не стану вдаваться в подробности, чем они отличаются, ибо сам не до конца въехал. В общем, читаю краткое пояснение по этому делу, а потом меняю планировщик командой (вернее, я добавил эту команду в /etc/rc.local, чтобы планировщик GFQ выставлялся на старте автоматически):
    echo cfq > /sys/block/sda/queue/scheduler

    Заметка
    Кстати, в разделе «замечания» мануала отмечено, что поддержка классов и приоритетов работает на CFQ. Но поскольку до туда всё равно никто не дочитывает… XD

    Что-то в системе слегка изменилось. Индикатор load при старте системы стал подниматься выше 11, а процессорная нагрузка стала состоять по большей части из wait. Не уверен, что это всё значит в долгосрочной перспективе, но проблема осталась — переключение на CFQ и последующее применение ionice на процесс dropbox на производительность системы на старте никакого заметного эффекта не произвело: ни положительного, ни отрицательного.

    Ну кто столько потоков запускает?! О специфике демона Dropbox.


    Я уж было разочаровался, но тут я заметил, что в top показан только один процесс dropbox, а iotop показывает несколько. И кстати, почему в top колонка идентификаторов процессов называется PID, а в iotop — TID? В чём разница между ними? Ответ быстро нашёлся. В моём же случае самое важное открытие: демон Dropbox для своих дел запускает несколько потоков. Много потоков. Выяснилось, что не один десяток. И диск начинают читать иногда по нескольку одновременно. Так вот откуда такие тормоза! А ionice не работал потому, что, присвоив класс приоритета основному процессу, он не передаёт этот класс приоритета дочерним потокам. Поэтому они как были со стандартным классом приоритета, так с ним и остались.
    Что ж, как говорят британцы, будем применять bodging. По-нашему, «костыли». Сделаем скриптик, который будет засекать процесс dropbox и все его дочерние потоки, брать их идентификаторы (PID или TID, в данном случае ionice'у это изофаллично до лампочки без особой разницы), и присваивать всем класс idle время от времени (на случай, если dropbox убивает часть дочерних потоков и/или создаёт новые).
    #!/bin/bash
    
    while true
    do
    	#собираем список дочерних потоков
    	TIDS=( $(ps -L --pid $(pgrep -x dropbox) -o tid=) )
    
    	for i in "${TIDS[@]}"
    	do
    		echo $i
    		#присваиваем приоритет idle
    		ionice -c3 -p $i
    	done
    
    	sleep 5
    
    done
    

    Добавляем в автозапуск, перезагружаемся.
    И всё заработало почти как будто демона Dropbox вообще нету. Эврика!

    Итоги
    Таким образом можно работать с любым процессом, потребляющим много дискового времени. Достаточно задать приоритет с помощью ionice и не забывать о подводных камнях, в частности:

    • следить, не запускает ли процесс прожорливые дочерние потоки
    • иметь в виду, что на некоторых системах планировщик ввода/вывода по умолчанию — не CFQ (который поддерживает классы и приоритеты)


    Какие ещё вещи следует учитывать? Расскажите о своём опыте в комментариях.
    Поделиться публикацией

    Комментарии 47

      +6
      Я думаю, это скорее косяк утилиты ionice, что дисковый приоритет выставляется только для одного процесса, но не его тредов. Если запускать программу с ionice изначально, приоритет, я думаю, все же должен наследоваться
        +1
        Индикатор load при старте системы стал подниматься выше 11, а процессорная нагрузка стала состоять по большей части из wait.

        Это говорит о том, что планировщик deadline работал оптимальнее. wait в нагрузке процессора означает, что текущая выполняемая задача ожидает завершения I/O операций. И пока операция не завершится, другие задачи тоже вынуждены ждать, о чём и говорит рост очереди (параметр load).


        Как можно предположить, при использовании Deadline приоритет отдаётся задачам (процессам), не так активно использующим ввод-вывод, они быстрее отрабатывают, и очередь не растёт. Выходит, что при переключении на CFQ произошла деградация, и разумнее вернуться на Deadline.

          0
          Но деградация была в промежуточный момент, когда ionice не выставляла правильный приоритет потокам Dropbox'а. Сейчас, скорее всего, всё намного лучше, да, Highstaker?
            0
            Да, стало намного лучше.
            Видимо, deadline неправильно выставлял приоритеты процессам, и потоки dropbox в результате съедали много, просто за счёт их количества.

            Пришлось через ionice вручную сообщить системе, что все потоки dropbox должны иметь низкий приоритет. И всё полетело как в попу ужаленное ракета 8)
              0
              А если занизить приоритет на выполнение самого демона dropbox. Не ввод-вывод а именно исполнение. Возможно будет тоже неплохо.
          +12
          Во многих современных дистрибутивах используется новая система инициализации — systemd. Можно красиво и технично решить вашу задачу. Для Dropbox сделать стартовый юнит, что не сложно и указать там BlockIOReadBandwith
          Хорошие примеры
          http://0pointer.de/blog/projects/resources.html
            0
            В 14.04 и до 15.04 использовалась SysV init, в 15.04 можно было переключиться и использовать systemd, а в 15.10 уже systemd по умолчанию, как и во многих современных дистрибутивах (хотя немало споров вокруг этого идёт).
              0

              В 14.04 используется upstart, совместимый с SysV init, SystemD выпилен, есть systemd-shim, но самого systemd нету — демонами управлять он не может и поставить нормальный systemd не представляется возможным.
              Да и насколько я знаю, Dropbox запускается не системой, а DE, то есть запускается не какой-нибудь /etc/init.d/dropbox, а Dropbox.desktop, так что это обсуждение — бессмысленно.

            +7
            Несколько советов вам по оптимизации этого хозяйства.

            Для начала попроще собираем пиды тредов dropbox:
            $(ps -LC dropbox -o tid=)

            Потом обращаем внимание, что ваше решение приводит к запуску 50 процессов каждые 5 секунд. Это условно ~5% загрузки процессора. Не жалко вам на это тратить ресурсы? ionice может за раз выставить io класс нескольким пидам:
            ionice -c3 --pid $(ps -LC dropbox -o tid=)
            Так сокращаем количество порождаемых каждые 5 секунд процессов с N тредов dropbox (у меня 51) +2 до 3

            Но вообще постоянно висящий фоном скрипт с циклом тоже излишен. Достаточно заменить автозапуск dropbox на автозапуск
            ionice -c3 dropbox

            io класс процесса наследуется при порождении новых процессов/тредов (как и переменные окружения и практически все свойства процесса). Так что все треды, которые наклепает dropbox, тоже будут с классом idle.
              +2
              ionice -c3 --pid $(ps -LC dropbox -o tid=)

              Кстати, не знал, спасибо, учту.

              ionice -c3 dropbox не работает, потому что демон ДБ запускается через отдельный скрипт командой dropbox start -i. Я пытался запускать через ionice, эффекта не дало. Можно, конечно, порыться в этом скрипте…
              0
              Возможно чего-то не понял, но не проще ли было изначально запускать дропбокс через ionice?
              ionice -c3 dropbox
                0
                Пробовал и это. Эффекта нет 8(
                +1
                Пусть лучше pfactum нам расскажет про wb-buf-throttle
                  0
                  Что именно?
                    0
                    Все!
                      0
                      Всё здесь: https://lwn.net/Articles/680989/
                  +1
                  А есть ли что-то подобное для Windows?
                  Dropbox на пару с GoogleDrive на старте занимают лаптоп на добрых 10 минут.
                    +3
                    конкнретно приоритеты занижаются так:
                    Скрытый текст
                    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\syncthing.exe]
                    
                    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\syncthing.exe\PerfOptions]
                    "IoPriority"=dword:00000000
                    "PagePriority"=dword:00000001
                    
                    [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\syncthing.exe]
                    
                    [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\syncthing.exe\PerfOptions]
                    "IoPriority"=dword:00000000
                    "PagePriority"=dword:00000001
                    
                      0
                      В какой версии?
                        0
                        Это работает для любого ПО как минимум в win7 и старше. Возможно и в висте. Это фитча самой ОС.
                          –1
                          как минимум в win7 и старше. Возможно и в висте.

                          емнип, в висте появилась возможность задавать io приоритет.
                          Это фитча самой ОС

                          Быть того не может!!!
                        +1
                        Спасибо, попробую.
                        Это получается настройки берутся по названию exe-шки?
                        А как быть если разные файлы с одним названием? Какие-нибудь launcher.exe для различных софтов, или вообще java.exe. Просто любопытно.
                          0
                          В ...\Image File Execution Options\<filename>.exe\ можно создать значение "UseFilter"=dword:00000001 и произвольное количество подключей с произвольными именами, внутри подключей — значение "FilterFullPath"="C:\<полный путь к filename>.exe", тогда настройки будут браться из подключа с FilterFullPath, совпавшим с полным путём (если такого не нашлось, то, как и раньше, из самого IFEO\<filename>.exe\).
                          +1
                          Только IoPriority лучше ставить Low, а не VeryLow ибо при последнем клиент будет вынужденно писать и читать данные маленькими порциями, а это увеличит количество вызовов системных API что плохо скажется на производительности в случае синхронизации большого объёма данных.

                          "IoPriority"=dword:00000001

                          и для Vitaljok и MacIn

                          P.S. да и в целом MS рекомендует выставлять только CPUPriority, который также называется BasePriority ибо IO выставляется тоже, вот тут разъяснения https://bitsum.com//pl_io_priority.php, но ссылки на msdn не приведу.
                          0
                          А что делать если ни в той ни в другой ветке у меня нету файла Dropbox.exe?
                            0
                            Собственно чё это я. Создал. Будем смотреть, как пойдёт
                              0
                              ProcessExplorer — пишут — позволяет выставлять руками. Можно запустить и проверить, сработала ли установка через реестр.
                          –3
                          Выкинуть cd-rom, воткнуть второй hdd, перенести хранилище туда, блаженствовать.
                          На мой взгляд выносить хранилище таких вот штук на отдельный от системного физический носитель — лучшее из возможных решений. Если нет такой возможности — стартовать ручками по необходимости.
                          0
                          Для периодических бэкапов с помощью rsnapshot использую стратегию Best-effort вместо Idle. Выглядит как `nice -n 19 ionice -c 2 -n 7 rsnapshot`. Почитал man ionice и так и не понял, в каких случаях лучше использовать Best-effort, а в каких Idle. Пробовал раньше использовать Idle, но производительность не устраивает в любом случае. Юниты с таймерами в systemd кажуются самым элегантным решением в сложившейся ситуации.
                            –1
                            Не ради хейтерства, да и хочется понять, что это было и как с этим бороться:
                            Летом 2015 года, после скачки и установки deb с сайта dropbox'а и после запуска: инсталлятор начал скачивать сам клиента dropbox, попутно выжрав во время! скачивания свободные 7/8гб оперативки, все 4гб свопа(больше 500мб не поднимался прежде) и устроив нагрузку процессора(4ядра) за 50 и не установившись в итоге.
                            Следующая версия dropbox'а встала без проблем на тот же ноут с HDD и ubuntu 14.04 x64.
                            Вот такое у меня было первое знакомство с ним, так и не прижился он у меня.
                              0
                              Бага это в скрипте была, судя по симптомам. Что-то вызывало само себя бесконечно, в итоге отожрало само у себя все ресурсы и упало дружной толпой.
                              Я пока учился скрипты писать тоже такое сделал разок.
                              +1
                              Привет, я работаю в Dropbox (правда, в несвязанной с топиком области). Может быть, можно ключевые моменты перевести на английский, чтобы я мог передать фидбек кому надо?
                                +2

                                Насколько я понял суть:
                                Dropbox, at least under Linux, creates ~50 threads, some of which end up competing for disk I/O on Dropbox client startup (i.e. during user login), which in turn, makes system unresponsive for minutes right after logging in.
                                This behavior was confirmed at least under Ubuntu 14.04.
                                Some people work around this decreasing I/O priority for Dropbox.


                                Отсебятина:
                                But the real fix should be either eliminating need for such massive I/O on startup or adding options to decrease number of threads that do I/O simultaneously or decreasing their priority.

                                0

                                @Highstaker, напишите, пожалуйста внизу поста всем, что в Ubuntu 14.04 systemd выпилен с потрохами, место под него залито бетоном, обнесено ключей проволокой и по периметру стоят пулеметные вышки.


                                Юзерские стартап задачи запускаются, обычно, десктопным окружением через .desktop файлы. Можно в ~/.local создать свой Dropbox.desktop, с ionice и запускать его. Пробовали?

                                  –2
                                  Не в обиду никому, но приоритезация не ускорит I/O, а только переместит проблему в другое место, где ее будет не так сильно заметно. Почему бы не приобрести SSD (если бюджет скромный) или не замутить RAID (если деньги позволяют)?
                                    0
                                    SSD или RAID? На этом ноуте? Не вариант.
                                      +5
                                      SSD в данном случае не решение проблемы, а уход от нее. Сама же проблема в плохо спроектированном софте: дропбокс не учитывает разницу между ssd/hdd, спавнит дофига потоков которые долбят диск одновременными запросами, тем самым убивая производительность hdd. Можно ведь запросто сделать только один читающий поток и кучу обрабатывающих и это вероятнее всего почти полностью решит проблему, особенно если добиться чтобы индексы в фс всегда хранились без фрагментации(не знаю возможно ли).
                                      0
                                      Да, костылизм тот ещё.
                                      Если сделать предположение, что демон синхронизации дропбокса нагружает дисковое io синхронно с cpu (ну там же скорее всего диффы/хэши считаются в этот момент), то более красивым может быть решение с планировщиком процессов (не io планировщики, которые вы пытались крутить).
                                      Гуглить по SCHED_BATCH — процесс с таким правилом будет получать свои тики только когда все остальные отдыхают. Предположительно, это даст именно то, что хотите — дропбокс будет работать только когда вы, условно, «отошли от комьютера».
                                        0
                                        CPU нагружается процессом dropbox по минимуму. Бутылочное горлышко именно в I/O
                                          0
                                          Я понимаю, что проблема в io. Смысл в том, что процесс дропбокса не будет получать управление, когда работают другие процессы, а значит и обращений к диску не будет. Результат не гарантирован, но можно попробовать. Это более прямое решение.
                                            0
                                            Обычно проблема в том, что другие процессы работать не могут, т.к. сидят ждут чтения/записи диска в очереди. Именно поэтому я на ноуте с медленным диском аналогичным образом понижал приоритеты именно i/o некоторым процессам. Отзывчивость системы сильно повышается при этом.
                                              0
                                              Ну что вы мне третий раз одно и то же разными словами говорите? :) Понятно, что iowait. Но дропбокс плодит потоки и каждому нужно переназначать ionice, для чего в бесконечном цикле дёргается команда ionice. Работает, но выглядит костыльно, поэтому я предложил попробовать воркэраунд через планировщик процессов.
                                        0
                                        Для автовыставления приоритетов CPU и IO, правила можно дописывать самому, в частности для дробокса и пушить в апстрим.
                                        https://github.com/Nefelim4ag/Ananicy
                                          0
                                          А что мешает ограничить процессы dropbox-а с помощью cgroups?
                                            0
                                            Видимо потому что запускается от того же юзера.
                                              +1
                                              Возможности cgroups не настолько ограничены, а свойство наследования группы cgroup решит проблему с запуском дочерних процессов. Relationships Between Subsystems, Hierarchies, Control Groups and Tasks.
                                              В простейшей схеме, можно поместить запускаемый процесс в нужную группу запустив через cgexec.

                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                          Самое читаемое