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


Долгое время у меня была проблема — система очень сильно тормозила после старта. У меня ноутбук с жёстким диском (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 (который поддерживает классы и приоритеты)


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

Комментарии (46)


  1. youROCK
    02.05.2016 11:02
    +6

    Я думаю, это скорее косяк утилиты ionice, что дисковый приоритет выставляется только для одного процесса, но не его тредов. Если запускать программу с ionice изначально, приоритет, я думаю, все же должен наследоваться


  1. zloddey
    02.05.2016 12:04
    +1

    Индикатор load при старте системы стал подниматься выше 11, а процессорная нагрузка стала состоять по большей части из wait.

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


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


    1. Revertis
      02.05.2016 12:09

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


      1. Highstaker
        02.05.2016 15:11

        Да, стало намного лучше.
        Видимо, deadline неправильно выставлял приоритеты процессам, и потоки dropbox в результате съедали много, просто за счёт их количества.

        Пришлось через ionice вручную сообщить системе, что все потоки dropbox должны иметь низкий приоритет. И всё полетело как в попу ужаленное ракета 8)


        1. a0fs
          03.05.2016 13:57

          А если занизить приоритет на выполнение самого демона dropbox. Не ввод-вывод а именно исполнение. Возможно будет тоже неплохо.


  1. vasilisc
    02.05.2016 13:34
    +12

    Во многих современных дистрибутивах используется новая система инициализации — systemd. Можно красиво и технично решить вашу задачу. Для Dropbox сделать стартовый юнит, что не сложно и указать там BlockIOReadBandwith
    Хорошие примеры
    http://0pointer.de/blog/projects/resources.html


    1. AVX
      03.05.2016 13:58

      В 14.04 и до 15.04 использовалась SysV init, в 15.04 можно было переключиться и использовать systemd, а в 15.10 уже systemd по умолчанию, как и во многих современных дистрибутивах (хотя немало споров вокруг этого идёт).


      1. Gendalph
        03.05.2016 17:17

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


  1. Self_Perfection
    02.05.2016 14:01
    +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.


    1. Highstaker
      02.05.2016 15:26
      +2

      ionice -c3 --pid $(ps -LC dropbox -o tid=)

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

      ionice -c3 dropbox не работает, потому что демон ДБ запускается через отдельный скрипт командой dropbox start -i. Я пытался запускать через ionice, эффекта не дало. Можно, конечно, порыться в этом скрипте…


  1. grayich
    02.05.2016 15:07

    Возможно чего-то не понял, но не проще ли было изначально запускать дропбокс через ionice?
    ionice -c3 dropbox


    1. Highstaker
      02.05.2016 15:07

      Пробовал и это. Эффекта нет 8(


  1. ValdikSS
    02.05.2016 16:42
    +1

    Пусть лучше pfactum нам расскажет про wb-buf-throttle


    1. pfactum
      02.05.2016 16:44

      Что именно?


      1. ValdikSS
        02.05.2016 16:45

        Все!


        1. pfactum
          02.05.2016 16:56

          Всё здесь: https://lwn.net/Articles/680989/


  1. Vitaljok
    02.05.2016 18:49
    +1

    А есть ли что-то подобное для Windows?
    Dropbox на пару с GoogleDrive на старте занимают лаптоп на добрых 10 минут.


    1. xforce
      02.05.2016 20:43
      +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
      


      1. MacIn
        02.05.2016 21:57

        В какой версии?


        1. xforce
          02.05.2016 22:00

          Это работает для любого ПО как минимум в win7 и старше. Возможно и в висте. Это фитча самой ОС.


          1. MacIn
            02.05.2016 22:17
            -1

            как минимум в win7 и старше. Возможно и в висте.

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

            Быть того не может!!!


      1. Vitaljok
        02.05.2016 22:07
        +1

        Спасибо, попробую.
        Это получается настройки берутся по названию exe-шки?
        А как быть если разные файлы с одним названием? Какие-нибудь launcher.exe для различных софтов, или вообще java.exe. Просто любопытно.


      1. IRainman
        03.05.2016 00:32
        +1

        Только IoPriority лучше ставить Low, а не VeryLow ибо при последнем клиент будет вынужденно писать и читать данные маленькими порциями, а это увеличит количество вызовов системных API что плохо скажется на производительности в случае синхронизации большого объёма данных.

        "IoPriority"=dword:00000001

        и для Vitaljok и MacIn

        P.S. да и в целом MS рекомендует выставлять только CPUPriority, который также называется BasePriority ибо IO выставляется тоже, вот тут разъяснения https://bitsum.com//pl_io_priority.php, но ссылки на msdn не приведу.


        1. IRainman
          03.05.2016 00:35

      1. Starche
        03.05.2016 10:21

        А что делать если ни в той ни в другой ветке у меня нету файла Dropbox.exe?


        1. Starche
          03.05.2016 10:28

          Собственно чё это я. Создал. Будем смотреть, как пойдёт


          1. MacIn
            03.05.2016 16:41

            ProcessExplorer — пишут — позволяет выставлять руками. Можно запустить и проверить, сработала ли установка через реестр.


    1. Ziptar
      04.05.2016 00:23
      -3

      Выкинуть cd-rom, воткнуть второй hdd, перенести хранилище туда, блаженствовать.
      На мой взгляд выносить хранилище таких вот штук на отдельный от системного физический носитель — лучшее из возможных решений. Если нет такой возможности — стартовать ручками по необходимости.


  1. nuklea
    02.05.2016 18:49

    Для периодических бэкапов с помощью rsnapshot использую стратегию Best-effort вместо Idle. Выглядит как `nice -n 19 ionice -c 2 -n 7 rsnapshot`. Почитал man ionice и так и не понял, в каких случаях лучше использовать Best-effort, а в каких Idle. Пробовал раньше использовать Idle, но производительность не устраивает в любом случае. Юниты с таймерами в systemd кажуются самым элегантным решением в сложившейся ситуации.


  1. Romiro_Orimor
    02.05.2016 18:50
    -1

    Не ради хейтерства, да и хочется понять, что это было и как с этим бороться:
    Летом 2015 года, после скачки и установки deb с сайта dropbox'а и после запуска: инсталлятор начал скачивать сам клиента dropbox, попутно выжрав во время! скачивания свободные 7/8гб оперативки, все 4гб свопа(больше 500мб не поднимался прежде) и устроив нагрузку процессора(4ядра) за 50 и не установившись в итоге.
    Следующая версия dropbox'а встала без проблем на тот же ноут с HDD и ubuntu 14.04 x64.
    Вот такое у меня было первое знакомство с ним, так и не прижился он у меня.


    1. Barafu
      03.05.2016 13:58

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


  1. sim0nsays
    02.05.2016 21:04
    +1

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


    1. Gendalph
      02.05.2016 21:28
      +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.


  1. Gendalph
    02.05.2016 21:19

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


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


  1. Grief
    02.05.2016 22:03
    -2

    Не в обиду никому, но приоритезация не ускорит I/O, а только переместит проблему в другое место, где ее будет не так сильно заметно. Почему бы не приобрести SSD (если бюджет скромный) или не замутить RAID (если деньги позволяют)?


    1. Highstaker
      02.05.2016 22:04

      SSD или RAID? На этом ноуте? Не вариант.


    1. FeNUMe
      03.05.2016 01:13
      +5

      SSD в данном случае не решение проблемы, а уход от нее. Сама же проблема в плохо спроектированном софте: дропбокс не учитывает разницу между ssd/hdd, спавнит дофига потоков которые долбят диск одновременными запросами, тем самым убивая производительность hdd. Можно ведь запросто сделать только один читающий поток и кучу обрабатывающих и это вероятнее всего почти полностью решит проблему, особенно если добиться чтобы индексы в фс всегда хранились без фрагментации(не знаю возможно ли).


  1. pansa
    02.05.2016 23:29

    Да, костылизм тот ещё.
    Если сделать предположение, что демон синхронизации дропбокса нагружает дисковое io синхронно с cpu (ну там же скорее всего диффы/хэши считаются в этот момент), то более красивым может быть решение с планировщиком процессов (не io планировщики, которые вы пытались крутить).
    Гуглить по SCHED_BATCH — процесс с таким правилом будет получать свои тики только когда все остальные отдыхают. Предположительно, это даст именно то, что хотите — дропбокс будет работать только когда вы, условно, «отошли от комьютера».


    1. Highstaker
      03.05.2016 08:43

      CPU нагружается процессом dropbox по минимуму. Бутылочное горлышко именно в I/O


      1. pansa
        03.05.2016 13:31

        Я понимаю, что проблема в io. Смысл в том, что процесс дропбокса не будет получать управление, когда работают другие процессы, а значит и обращений к диску не будет. Результат не гарантирован, но можно попробовать. Это более прямое решение.


        1. xforce
          03.05.2016 14:00

          Обычно проблема в том, что другие процессы работать не могут, т.к. сидят ждут чтения/записи диска в очереди. Именно поэтому я на ноуте с медленным диском аналогичным образом понижал приоритеты именно i/o некоторым процессам. Отзывчивость системы сильно повышается при этом.


          1. pansa
            03.05.2016 14:09

            Ну что вы мне третий раз одно и то же разными словами говорите? :) Понятно, что iowait. Но дропбокс плодит потоки и каждому нужно переназначать ionice, для чего в бесконечном цикле дёргается команда ionice. Работает, но выглядит костыльно, поэтому я предложил попробовать воркэраунд через планировщик процессов.


  1. nefelim4ag
    03.05.2016 08:53

    Для автовыставления приоритетов CPU и IO, правила можно дописывать самому, в частности для дробокса и пушить в апстрим.
    https://github.com/Nefelim4ag/Ananicy


  1. Lelik13a
    03.05.2016 10:57

    А что мешает ограничить процессы dropbox-а с помощью cgroups?


    1. SkazochNik
      04.05.2016 13:28

      Видимо потому что запускается от того же юзера.


      1. Lelik13a
        04.05.2016 15:24
        +1

        Возможности cgroups не настолько ограничены, а свойство наследования группы cgroup решит проблему с запуском дочерних процессов. Relationships Between Subsystems, Hierarchies, Control Groups and Tasks.
        В простейшей схеме, можно поместить запускаемый процесс в нужную группу запустив через cgexec.