Причина непроизвольного переключения контекста

Я пытаюсь профилировать многопоточную программу, которую написал на довольно большой машине (32 ядра, 256 ГБ ОЗУ). Я заметил, что между запусками производительность программы может сильно различаться (70-80%). Кажется, я не могу найти причину такой огромной разницы в производительности программы, но, проанализировав результат использования утилиты time на большом количестве запусков, я заметил, что количество непроизвольных переключений контекста сильно коррелирует с производительность программы (очевидно, что меньшее количество переключений контекста приводит к повышению производительности и наоборот).

Есть ли хороший способ определить, что вызывает это переключение контекста? Если я смогу обнаружить виновника, то, возможно, я смогу попытаться решить проблему. Однако у меня есть несколько особых ограничений на инструменты, которые я могу использовать. Во-первых, у меня нет привилегий root на машине, поэтому инструменты, требующие таких привилегий, отсутствуют. Во-вторых, это довольно старое ядро ​​(RHEL5, ядро ​​2.6.18), поэтому некоторые стандартные элементы обработки событий могут отсутствовать. В любом случае, мы будем очень благодарны за любые предложения о том, как глубже изучить причину этого переключения контекста.

обновление: я решил протестировать свою программу на другом (и меньшем) компьютере. Другая машина представляет собой 4-ядерный (с гипертекстовым заголовком) Linux-бокс с 8 ГБ ОЗУ и гораздо более новым ядром - 3.2.0 против 2.6.18 на другой машине. На новой машине я не могу воспроизвести двухмодальный профиль производительности. Это заставляет меня думать, что проблема связана либо с проблемой оборудования (как было предложено в комментариях), либо с особенно патологическим случаем на уровне ядра, который с тех пор был исправлен. Моя текущая лучшая гипотеза состоит в том, что это может быть результатом того факта, что новая машина имеет ядро ​​с полностью справедливым планировщиком (CFS), а старая машина - нет. Есть ли способ проверить эту гипотезу (указать новой машине использовать другой / более старый планировщик) без необходимости перекомпилировать старую версию ядра для новой машины?


person nomad    schedule 23.06.2013    source источник
comment
Под непроизвольными переключениями контекста вы имеете в виду, что какой-то другой процесс хотел запустить, или МОЙ процесс сделал что-то, что заставило систему остановить его, пока система завершила некоторую работу, например, ожидание загрузки некоторых файловых данных с диска или сети?   -  person Mats Petersson    schedule 24.06.2013
comment
Вы знаете о pthread_cond_t?   -  person Varvarigos Emmanouil    schedule 24.06.2013
comment
@MatsPetersson: да - «Во-первых, у меня нет прав суперпользователя на машине» не предполагает его исключительного использования.   -  person Martin James    schedule 24.06.2013
comment
Спросите системного администратора, у которого есть привилегии, чтобы узнать.   -  person Martin James    schedule 24.06.2013
comment
То, что вы не одиноки, не обязательно означает, что на компьютере недостаточно ресурсов для выполнения вашей задачи. Но если машину используют другие люди, это сложно - если, конечно, вы не можете попросить администраторов дать вам какое-то право повысить приоритет ваших задач над задачами других людей или что-то еще в этом роде. Если на машине есть конкурирующие задачи, машина будет разделять ресурсы ЦП между задачами - так работает многопользовательская система ...   -  person Mats Petersson    schedule 24.06.2013
comment
Хотя у меня нет прав администратора, я один на машине (по крайней мере, когда я делал свое предыдущее профилирование). Другие пользователи, имеющие доступ к машине, являются членами нашей довольно небольшой исследовательской группы, поэтому, если мне нужно время, чтобы провести анализ производительности, я могу попросить их использовать машину в одиночку в течение некоторого времени. Тот факт, что это происходит, когда я один на машине, предполагает, что, возможно, есть прерывистый системный процесс, который вызывает прерывания, но я не знаю, как это проверить наверняка.   -  person nomad    schedule 24.06.2013
comment
Под непроизвольным переключением контекста я подразумеваю, что, согласно ОС, это не результат выполнения или ожидания моей обработки, а скорее результат того, что ОС принудительно прерывает мой процесс для какой-то другой цели.   -  person nomad    schedule 24.06.2013
comment
Как вы узнали, что не уступаете и не ждете? Выделяет ли ваша программа память, выполняет системные вызовы или использует какую-либо библиотеку, которая их выполняет? Ограничена ли память вашей программы вводом-выводом?   -  person DanielKO    schedule 24.06.2013
comment
Вы пробовали использовать сквиз в top во время работы вашей программы? Что бы вы ни вытесняли, вероятно, вы используете много ЦП ....   -  person Tony Delroy    schedule 24.06.2013
comment
@DanielKO - Утилита time фактически разбивает переключение контекста на добровольный / непроизвольный. Под непроизвольным переключением контекста понимается случай, когда операционная система опережает ваш процесс по какой-либо причине, кроме добровольного отказа от управления (например, уступка / ожидание). Это может произойти, когда истечет его временной интервал и есть процесс с более высоким приоритетом, который должен быть запущен, и, предположительно, также при ряде других условий.   -  person nomad    schedule 24.06.2013
comment
О, я забыл, что у GNU time есть эти лишние вещи. Какое количество мелких ошибок страниц? Кроме того, я спросил о других вещах.   -  person DanielKO    schedule 24.06.2013
comment
Я предполагаю - другие пользователи запланировали большие задания cron.   -  person Martin James    schedule 24.06.2013
comment
@DanielKO - программа не распределяет память динамически, но заранее выделяет фиксированное количество памяти. Он считывает файл с диска и подсчитывает количество вхождений фиксированных подслов, которые встречаются в файле, поэтому выполняется значительный объем операций ввода-вывода. Однако я тестировал часть программы ввода-вывода изолированно, и это не является узким местом (я могу читать больше подслов, чем могу обработать). Кроме того, производительность кажется одинаковой, когда я просто выполняю ввод-вывод (т.е. я не вижу большого количества непроизвольных переключений контекста, приводящих к снижению производительности).   -  person nomad    schedule 24.06.2013
comment
@TonyD - Ага; Я смотрел «верх» во время работы программы, и странно то, что не похоже, что в системе происходит что-то еще; в основном это просто вещи системного уровня (и т. д. dbus-daemon, greceptor (он запускает программное обеспечение кластера ROCKS) и некоторые другие различные вещи с низкой нагрузкой).   -  person nomad    schedule 24.06.2013
comment
@DanielKO - Программа сообщает о ~ 3920000 незначительных сбоях страницы за запуск (разница здесь крошечная) и 0 серьезных сбоях страницы за запуск. Кажется, что количество ошибок страниц не зависит от того, получаю я хорошую или плохую производительность.   -  person nomad    schedule 24.06.2013
comment
Выполняете атомарную операцию? Это не дешево для многих процессоров. В остальном эта проблема остается слишком абстрактной. Вам следует преобразовать вашу программу в минималистичный образец кода и опубликовать его. Вы либо сами найдете проблему, либо люди обнаружат фундаментальный недостаток в конструкции алгоритма.   -  person DanielKO    schedule 24.06.2013
comment
@DanielKO - Вообще-то да, я выполняю атомарные операции. Подсчет подслов хранится в большом массиве атомарных целых чисел (std :: atomic ‹uint32_t›). Я не понимаю, почему иногда это может вызывать проблемы с производительностью, но не другие. Профиль производительности на самом деле странный, потому что есть два режима; быстрое и медленное, и каждое выполнение программы попадает в одну из них (то есть никогда не выполняется на уровне между быстрым и медленным временем выполнения). Есть ли способ более глубоко изучить атомы, чтобы увидеть, не вызывают ли они проблемы?   -  person nomad    schedule 24.06.2013
comment
В первую очередь следует остерегаться ложного обмена. Я не могу особо подробно рассказать об этом месте, но посмотрите stackoverflow.com/questions/10143676/ и глава 4 книги perfbook kernel.org/pub/linux/kernel/people/paulmck/perfbook/. Кроме того, это может быть аппаратная проблема; Однажды я работал с 264-ядерной машиной SGI UV, которая время от времени замедлялась из-за аппаратного сбоя, как в вашем сценарии с двумя режимами.   -  person DanielKO    schedule 24.06.2013
comment
Интересно, что я вижу очень похожий профиль производительности, если удаляю из кода фактические атомарные записи; поэтому программа просто считывает файл и выполняет некоторую обработку подслов (например, вычисляет хеш-функцию). Очень странно.   -  person nomad    schedule 24.06.2013
comment
Жаль, что вас беспокоит Linux, а не Solaris / Mac OS / FreeBSD - DTrace быстро справится с подобным расследованием. Подробнее см. В этой серии сообщений в блоге: pt1, pt2, pt3, pt4   -  person Dan    schedule 01.07.2013
comment
Спасибо @ gerty3000! На самом деле меня тоже беспокоит производительность Mac OS, но у меня просто нет доступа к такому количеству машин для тестирования на этой платформе. На самом деле, было бы интересно посмотреть, возникает ли эта проблема даже в OSX, поскольку я предполагаю, что планировщик совершенно другой, и, если это источник проблемы, в OSX может не возникнуть никаких проблем.   -  person nomad    schedule 01.07.2013
comment
Как часто он запускается? Возможно ли, что иногда файл кешируется, а иногда нет? Вы вручную читаете файл в память или используете mmap?   -  person kfsone    schedule 01.07.2013
comment
Расхождения, о которых я сообщил, составили много повторных прогонов. Однако производительность чередуется между быстрым и медленным режимами. Файл анализируется потоком чтения, который заполняет двустороннюю параллельную очередь, а затем данные извлекаются из очереди другими потоками для обработки. Однако я не думаю, что это связано с кешированием файлов, поскольку режимы производительности, кажется, исчезают, когда не выполняется обработка в потоках обработки данных (т.е. когда я просто читаю файл).   -  person nomad    schedule 01.07.2013


Ответы (6)


Вы упомянули, что существует 32 ядра, но какова точная компоновка оборудования? Например. сколько пакетов у машины, сколько ядер, как распределяется кеш и т. д. Для обмена такой информацией лично мне нравится делиться выводом likwid-topology -g.

В любом случае, в вашем прогоне есть один элемент недетерминизма: сходство потоков. Операционная система назначает потоки SW для работы в определенных потоках HW каким-то образом, не принимая во внимание знания о том, как потоки взаимодействуют (просто потому, что у него нет этих знаний). Это может вызвать всевозможные эффекты, поэтому для воспроизводимых прогонов рекомендуется каким-то образом закрепить потоки SW к потокам HW (может быть и оптимальный способ, но пока что я я как раз про детерминизм).

Для закрепления (также известного как affinity) вы можете использовать явные вызовы Pthread или попробовать другой инструмент из пакета Likwid под названием likwid-pin - см. здесь.

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

person Alexey Alexandrov    schedule 02.07.2013
comment
Спасибо за предложение. Я действительно пытался установить сродство моих потоков напрямую, используя pthreads (и это работает), но я все еще вижу изменчивость производительности. Это заставляет меня думать, что проблема, скорее всего, связана со старым ядром / планировщиком. Однако я не знал об инструментах likwid, и они кажутся очень полезными. Кроме того, я убедил администратора создать группу sudoers, чтобы я действительно мог использовать эти инструменты профилирования. Я думаю, что само по себе это предложение стоит награды. - person nomad; 03.07.2013
comment
Спасибо за награду! Интересно, что сродство не улучшило воспроизводимость. Далее я бы попытался увидеть (несмотря на использование профилировщика), зависит ли изменчивость от времени пользователя или времени ядра. Для этого должно хватить простой утилиты «время». Чтобы глубже погрузиться в проблемы планирования, я бы использовал VTune с расширенными точками доступа с включенными стеками (которые собирают информацию о переключении контекста) или с возможностями трассировки расписания ftrace. Одна важная вещь, которую я должен был спросить, - это сколько времени занимает ваша рабочая нагрузка. Вы сказали, что вариабельность составляет 80% - но это 5 секунд против 9 секунд или 5 мс против 9 мс? - person Alexey Alexandrov; 03.07.2013
comment
Изменчивость находится в довольно большом временном масштабе (например, 120 против 200 секунд). Если бы это происходило только при очень высоких разрешениях (например, 5 мс против 9 мс), я бы, вероятно, не так беспокоился об этом и не был бы уверен, что это была не просто ошибка измерения. - person nomad; 04.07.2013
comment
120 против 200 секунд действительно интересно. Есть ли у вас зависимость от пропускной способности сети в вашей рабочей нагрузке? Это может быть так сложно, как чтение большого количества данных из общего сетевого ресурса. В зависимости от загрузки сети вы можете получить разную скорость. Но это не дало бы вам бимодального результата. В любом случае, когда вы найдете основную причину, было бы очень полезно узнать, что это было - держите нас в курсе, если у вас будет возможность! :) - person Alexey Alexandrov; 04.07.2013

Я считаю, что ваша проблема связана с расписанием.

Нельзя избежать вытеснения одного процесса из ЦП, но проблема в том, что если поток вытесняется, а затем на его следующем кванте оказывается на другом ЦП, или, если быть более конкретным, на ЦП с другим L2 cache, то все обращения к памяти приведут к промахам кеша и вызову выборки данных из памяти. С другой стороны, если поток планируется на одном и том же процессоре, вполне вероятно, что его данные будут по-прежнему доступны на cahce, например, обеспечивая гораздо более быстрый доступ к памяти.

Обратите внимание, что такое поведение, скорее всего, произойдет, когда у вас будет все больше и больше ядер. И поскольку это своего рода «случайность», когда ваш поток завершится на следующем такте, то это объясняет случайность производительности.

Существуют инструменты профилирования, которые позволяют вам регистрировать, где запланированы ваши потоки, например perf для Linux. Обычно эти инструменты относятся к топологии, в которой вы выполняете свою программу. К сожалению, сейчас мне в голову не приходит ничего другого. Есть также способы указать ОС, чтобы она планировала потоки на одних и тех же (или соседних) процессорах, чтобы они выиграли от меньшего количества промахов в кеше. Для этого вы можете проверить этот вопрос SO

Я бы посоветовал вам спросить своего администратора, с какими инструментами вы можете рассчитывать, чтобы вы могли правильно профилировать и назначать свое планирование потоков.

person cgledezma    schedule 01.07.2013

Вы упоминаете о двухмодальном профиле производительности, который вы видите на одной машине, а не на другой. Ужасно, но это нормально даже для однопоточных приложений.

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

Linux - это не система реального времени. Он просто пытается быть максимально эффективным в среднем случае.

Чтобы свести к минимуму разницу в производительности, вы можете сделать несколько вещей:

Уменьшите количество потоков до минимально необходимого. Не разбивайте различные аспекты вашей проблемы на темы. Просто разбейте их на потоки, когда это действительно необходимо, например, чтобы накормить ЦП независимыми (!) Заданиями по вычислению чисел. Постарайтесь выполнить как можно больше причинно-следственной работы в одном потоке. Вы должны как можно реже общаться друг с другом. В частности, у вас не должно быть шаблонов запроса / ответа между потоками, в которых складываются задержки.

Предположим, ваша ОС может выполнять только около 1000 переключений контекста между потоками / процессами в секунду. Это означает пару 100 транзакций запроса / ответа в секунду. Если вы проводите тест в Linux и обнаруживаете, что можете сделать гораздо больше, не обращайте на это внимания.

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

person Johannes Overmann    schedule 01.07.2013

Вы можете отслеживать источник переключения контекста в ядре, используя ftrace (используя трассировщик sched_switch). Кроме того, использование perf может помочь вам сузить круг других показателей (промахи в кэше, и т.д.).

Как изменяется изменчивость производительности при увеличении вашей программы с 1 потока до 32 (и, возможно, выше)?

Может быть, ваша программа имеет общие данные (или какой-либо другой ресурс), за которые борются отдельные потоки? Таким образом, они испытывают состояние гонки, которое усиливается при большем количестве параллельного выполнения (отсюда и переменная производительность)? За какие ресурсы помимо процессорного времени борются потоки вашей программы?

Я думаю, вам придется скомпилировать старое ядро ​​на своей 4-ядерной машине. Это было бы не только хорошим учебным упражнением, если вы еще не сделали этого, но и полезным для выделения переменных в этой задаче. Если бы вы сделали это, я бы также попытался сопоставить точный дистрибутив, поскольку ваша проблема может быть ограничена не только ядром. Это также может быть что-то в пользовательском пространстве или, возможно, просто в параметрах компиляции для ядра (которые, следовательно, будут соответствовать в том же выпуске дистрибутива).

Вы можете изучить возможность использования профилировщика, такого как gprof. Это может дать вам представление о (потенциальном) узком месте. Например, если вы ждете системного вызова write или чего-то подобного.

Вот несколько статей, которые могут помочь вызвать идею или две:

http://halobates.de/lk09-scalability.pdf

http://pdos.csail.mit.edu/papers/linux:osdi10.pdf

Дополнительный источник: Почему одно недобровольное переключение контекста в секунду?

person Homer6    schedule 28.06.2013
comment
Наконец, вы всегда можете подать прошение об обновлении ядра. :-) - person Homer6; 28.06.2013
comment
Спасибо за ссылки. Я думаю, что вы правы, что мне, возможно, действительно придется перекомпилировать старое ядро ​​для другой машины. - person nomad; 03.07.2013
comment
Кроме того, в этих видеолекциях есть много хороших инструментов и техник, которые могут быть полезны в вашем случае: ocw.mit.edu/courses/electrical-engineering-and-computer-science/ - person Homer6; 04.07.2013
comment
@nomad Я обновил это, чтобы сослаться на инструмент ftrace, который даст вам более полное представление о переключателях контекста. - person Homer6; 13.07.2013

В многопоточной программе прикрепите поток к определенному номеру ЦП и проверьте, заметили ли вы улучшение производительности.

person Anand Rathi    schedule 02.07.2013

Когда дело доходит до изменения расписания, лучше всего использовать профилировщик ядра / регистратор Ftrace. Это должно показать вам, какие потоки вытесняются другими потоками. К сожалению, обработчики прерываний в нижней половине не имеют должной маркировки, поэтому их может быть сложно расшифровать.

person doron    schedule 02.07.2013