Multicore + Hyperthreading - как распределяются потоки?

Я читал обзор нового Intel Atom 330, где они отметили, что диспетчер задач показывает 4 ядра - два физических ядра и еще два, моделируемые Hyperthreading.

Предположим, у вас есть программа с двумя потоками. Предположим также, что это единственные потоки, выполняющие какую-либо работу на ПК, все остальное простаивает. Какова вероятность того, что ОС разместит оба потока на одном ядре? Это имеет огромное значение для производительности программы.

Если ответ отличается от 0%, существуют ли какие-либо стратегии смягчения, кроме создания дополнительных потоков?

Я ожидаю, что для Windows, Linux и Mac OS X будут разные ответы.


Использование ответ sk как корм для Google, затем, пройдя по ссылкам, я нашел GetLogicalProcessorInformation в Windows. Здесь говорится о «логических процессорах, которые совместно используют ресурсы. Примером такого типа совместного использования ресурсов могут быть сценарии с гиперпоточностью». Это означает, что jalf верен, но это не совсем окончательный ответ. .


person Mark Ransom    schedule 11.12.2008    source источник
comment
Я просто хотел бы отметить, что оптимальная политика не всегда заключается в том, чтобы запускать две задачи на разных ядрах; например, если у вас есть две задачи, которые совместно используют память и выполняют множество неперекрывающихся операций, их выполнение на одном ядре может обеспечить более высокую производительность, поскольку сокращение пропусков в кэше компенсирует немного более медленное время выполнения, из-за которого иногда приходится совместно использовать процессор (помните, в этом сценарии оба потока обычно работают параллельно даже на одном ядре, потому что они используют разные логические единицы).   -  person Borealid    schedule 29.07.2010
comment
Просто к сведению: если вы ищете чистую производительность, вы можете отключить гиперпоточность. Если только это не Intel, наконец, заставила его работать хорошо. В прошлом (последнее, что я измерял, было на 2-х процессорной P4 Xeon коробке с гиперпоточностью (что дает 4 логических процессора для ОС), чистая производительность при выполнении 4 вычислительно интенсивных потоков с включенной гиперпоточностью давала более низкую чистую производительность, чем выполнение двух потоков с гиперпоточностью. отключен. Очевидно, вы захотите проверить это самостоятельно на новейшем оборудовании - это может быть уже не так. Но имейте в виду ...   -  person Nathan Ernst    schedule 29.07.2010
comment
Иногда выполнение потоков на одном ядре - это ТОЧНО то, что вам нужно. Например, если вы используете структуры данных без блокировок; когда у вас есть потоки на отдельных физических ядрах, перестановка строк кэша между ядрами СНИЖАЕТ производительность.   -  person    schedule 27.04.2011


Ответы (8)


В Linux есть довольно сложный планировщик потоков, поддерживающий HT. Некоторые из его стратегий включают:

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

Активная балансировка нагрузки: если есть 3 задачи, 2 на одном физическом процессоре и 1 на другом, когда второй физический процессор простаивает, планировщик попытается перенести на него одну из задач.

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

Итак, чтобы ответить на ваш вопрос (по крайней мере, в Linux); учитывая 2 потока на двухъядерном гиперпоточном компьютере, каждый поток будет работать на своем собственном физическом ядре.

person joshperry    schedule 11.12.2008
comment
Я не вижу, чтобы это происходило на моей машине. Запуская stress -c 2 на моем i5-2520M, он иногда планирует (и сохраняет) два потока на ядрах HT 1 и 2, которые сопоставляются с одним и тем же физическим ядром. Даже если в противном случае система простаивает. (Я нашел HT- ›назначение физического ядра с помощью egrep "processor|physical id|core id" /proc/cpuinfo | sed 's/^processor/\nprocessor/g'.) - person nh2; 02.04.2015
comment
Я сделал эту проблему более конкретной с помощью этот вопрос. - person nh2; 02.04.2015

Нормальная ОС будет пытаться планировать ресурсоемкие задачи на своих собственных ядрах, но проблемы возникают, когда вы начинаете переключать их контекст. Современные ОС по-прежнему имеют тенденцию планировать вещи на ядрах, где нет работы по планированию времени, но это может привести к тому, что процессы в параллельных приложениях будут довольно свободно переключаться с ядра на ядро. Для параллельных приложений вы этого не хотите, потому что вы теряете данные, которые процесс мог использовать в кэшах своего ядра. Люди используют привязку к процессору, чтобы контролировать это, но в Linux семантика sched_affinity () может сильно различаться между дистрибутивами / ядрами / поставщиками и т. Д.

Если вы работаете в Linux, вы можете переносно контролировать соответствие процессоров с помощью Portable Linux Processor Affinity Library ( PLPA). Это то, что OpenMPI использует для внутренних целей, чтобы гарантировать, что процессы будут запланированы на их собственные ядра в многоядерных и мультисокетных системах. ; они только что выделили модуль как отдельный проект. OpenMPI используется в Лос-Аламосе и в других местах, так что это хорошо протестированный код. Я не уверен, что есть аналог под Windows.

person Todd Gamblin    schedule 11.12.2008

Я искал ответы на вопросы о планировании потоков в Windows, и у меня есть эмпирическая информация, которую я опубликую здесь для всех, кто может наткнуться на этот пост в будущем.

Я написал простую программу на C #, запускающую два потока. На моем четырехъядерном компьютере с Windows 7 я увидел несколько удивительных результатов.

Когда я не устанавливал привязку, Windows распределяла рабочую нагрузку двух потоков по всем четырем ядрам. Закомментированы две строки кода - одна, которая связывает поток с ЦП, а вторая предлагает идеальный ЦП. Предложение казалось безрезультатным, но установка сходства потоков заставляла Windows запускать каждый поток на своем собственном ядре.

Чтобы получить наилучшие результаты, скомпилируйте этот код с помощью свободно доступного компилятора csc.exe, который поставляется с клиентом .NET Framework 4.0, и запустите его на машине с несколькими ядрами. С закомментированной строкой соответствия процессоров диспетчер задач показал, что потоки распределены по всем четырем ядрам, каждое из которых работает примерно на 50%. При установленном сродстве два потока максимально использовали два ядра на 100%, а два других ядра работали в режиме ожидания (это то, что я ожидал увидеть перед запуском этого теста).

РЕДАКТИРОВАТЬ: Сначала я обнаружил некоторые различия в производительности с этими двумя конфигурациями. Однако мне не удалось воспроизвести их, поэтому я отредактировал этот пост, чтобы отразить это. Мне все еще показалось интересным сходство потоков, поскольку это было не то, что я ожидал.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

class Program
{
    [DllImport("kernel32")]
    static extern int GetCurrentThreadId();

    static void Main(string[] args)
    {
        Task task1 = Task.Factory.StartNew(() => ThreadFunc(1));
        Task task2 = Task.Factory.StartNew(() => ThreadFunc(2));
        Stopwatch time = Stopwatch.StartNew();
        Task.WaitAll(task1, task2);
        Console.WriteLine(time.Elapsed);
    }

    static void ThreadFunc(int cpu)
    {
        int cur = GetCurrentThreadId();
        var me = Process.GetCurrentProcess().Threads.Cast<ProcessThread>().Where(t => t.Id == cur).Single();
        //me.ProcessorAffinity = (IntPtr)cpu;     //using this line of code binds a thread to each core
        //me.IdealProcessor = cpu;                //seems to have no effect

        //do some CPU / memory bound work
        List<int> ls = new List<int>();
        ls.Add(10);
        for (int j = 1; j != 30000; ++j)
        {
            ls.Add((int)ls.Average());
        }
    }
}
person bart    schedule 28.07.2010
comment
Вы должны знать, что если вы используете диспетчер задач для проверки использования, сам диспетчер задач может сильно нарушить работу системы, поскольку обычно он работает с повышенным приоритетом. Попробуйте установить для диспетчера задач низкий приоритет и посмотрите, не изменится ли шаблон. - person Zan Lynx; 29.07.2010
comment
Можете ли вы поделиться своим временем работы в разных конфигурациях? - person Mark Ransom; 29.07.2010

Вероятность того, что ОС не будет использовать максимально возможное количество физических ядер, составляет практически 0%. Ваша ОС не глупа. Его задача - планировать все, и он хорошо знает, какие ядра у него есть. Если он видит два потока, интенсивно использующих ЦП, он гарантирует, что они работают на двух физических ядрах.

Редактировать Чтобы немного уточнить, для высокопроизводительных вещей, как только вы попадете в MPI или другие серьезные фреймворки распараллеливания, вы определенно захотите контролировать то, что выполняется на каждом ядре.

ОС сделает своего рода попытку максимально использовать все ядра, но у нее нет долгосрочной информации, которая есть у вас, о том, что «этот поток будет работать очень долго» или что «мы столько потоков будет выполняться параллельно ». Таким образом, он не может принимать идеальные решения, а это означает, что ваш поток время от времени будет назначаться новому ядру, что означает, что вы столкнетесь с промахами в кеше и т.п., что отнимет немного времени. Для большинства целей этого достаточно, и вы даже не заметите разницы в производительности. И это также хорошо работает с остальной частью системы, если это имеет значение. (На чьей-то настольной системе это, вероятно, довольно важно. В сетке с несколькими тысячами процессоров, выделенных для этой задачи, вы не особенно хотите хорошо играть, вы просто хотите использовать каждый доступный тактовый цикл).

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

person jalf    schedule 11.12.2008
comment
Я тоже хотел бы в это верить, но было бы полезно привести небольшое доказательство. - person Mark Ransom; 11.12.2008
comment
Доказательства чего? Создайте программу, которая запускает два потока в бесконечном цикле, и проверьте использование ЦП. Вы обнаружите, что любая разумная ОС назначает поток каждому ядру. Как вы думаете, это проблема, которую не рассматривали разработчики ОС? Конечно, нет. Это фундаментальная проблема, с которой ОС должна справиться. - person jalf; 11.12.2008
comment
У меня нет под рукой такой системы для тестирования, в остальном это неплохой совет. - person Mark Ransom; 11.12.2008
comment
jaff: все еще есть проблемы с производительностью, когда эти вещи переключают контекст и манипулируют. Мы видим это в национальных лабораториях, и все среды выполнения на параллельных машинах устанавливают соответствие, чтобы процессы оставались на своих ядрах. См. open-mpi.org/projects/plpa и мой ответ ниже. - person Todd Gamblin; 11.12.2008
comment
Да, я знаю, что это не на 100% оптимально, но для большинства целей это достаточно близко. Я просто хотел сказать, что ОС не настолько глупа, чтобы пытаться планировать все потоки, нагружающие ЦП, на одном ядре, оставляя другие полностью неиспользованными. Конечно, для MPI или подобного, да, вам нужен полный контроль. :) - person jalf; 11.12.2008
comment
Комментарии удалены, см. Сводку ниже в моем [альтернативном] ответе. Вы, ребята, в любом случае довольно много рассказали об этом в своих комментариях; планировщик не может быть идеальным во всех случаях, потому что он не знает о потоках, которые он планирует. Оптимизация обычно возможна в определенных ситуациях. - person dyasta; 26.11.2010
comment
Если он видит два потока, интенсивно использующих ЦП, он гарантирует, что они работают на двух физических ядрах. Это ни в коем случае не всегда оптимально. Если потоки работают с одной и той же памятью, они ОЧЕНЬ выиграют, если будут на одном физическом ядре. Кроме того, существует разница между интенсивным использованием ЦП и интенсивным использованием кеша. Если потоки интенсивно используют кеш и работают с другой памятью, ТО они получают выгоду от того, что они находятся на отдельных ядрах (то есть, действительно, на отдельных кешах). - person ; 27.04.2011
comment
@Blank: да? Как это противоречит тому, что я сказал? где я утверждал, что ОС будет планировать оптимально? И помимо этого, я должен сказать, что я скептически отношусь к вашему ОГРОМНОМУ заявлению. Если вы не заставляете свою ОС слишком часто переключать контекст (в этом случае у вас есть более серьезные проблемы), потоки будут работать на одном ядре достаточно долго для попадания в перфоманс из-за промахов кеша, когда поток перемещается в другое ядро ​​должно быть довольно второстепенным. Вы не увидите утроение производительности только из-за принудительной привязки потоков. - person jalf; 27.04.2011
comment
@Jalf: вариант использования, который я имел в виду для слова «чрезвычайно», - это структуры данных без блокировок. Вы видите, как производительность резко падает, как только вы начинаете работать на отдельных физических ядрах - все строки кэша меняются местами, поскольку каждая запись CAS делает недействительными строки кеша для каждого другого физического ядра. Переключение контекста - не проблема. - person ; 27.04.2011

Это очень хороший и актуальный вопрос. Как мы все знаем, гиперпоточное ядро ​​- это не настоящий процессор / ядро. Вместо этого это виртуальный ЦП / ядро ​​(с этого момента я буду называть ядро). Планировщик ЦП Windows, начиная с Windows XP, должен уметь отличать гиперпоточные (виртуальные) ядра от реальных ядер. Тогда вы можете представить, что в этом идеальном мире он обращается с ними «как надо», и это не проблема. Вы ошибаетесь.

Собственная рекомендация Microsoft по оптимизации сервера Windows 2008 BizTalk рекомендует отключить HyperThreading. На мой взгляд, это говорит о том, что обработка ядер с гиперпоточностью не идеальна, и иногда потоки получают квант времени на ядре с гиперпоточностью и страдают от штрафа (часть производительности реального ядра, 10% I ''. гадаю, а Microsoft предполагает 20-30%).

Ссылка на статью Microsoft, в которой предлагается отключить HyperThreading для повышения эффективности сервера: http://msdn.microsoft.com/en-us/library/cc615012(BTS.10).aspx

Это ВТОРАЯ рекомендация после обновления BIOS, поэтому они считают ее важной. Они говорят:

ОТ МАЙКРОСОФТ:

"Отключить гиперпоточность на компьютерах BizTalk Server и SQL Server

Для компьютеров BizTalk Server необходимо отключить критически важную гиперпоточность. Это параметр BIOS, который обычно находится в настройках процессора в программе настройки BIOS. Гиперпоточность заставляет сервер иметь больше процессоров / процессорных ядер, чем есть на самом деле; однако гиперпоточные процессоры обычно обеспечивают от 20 до 30% производительности физического процессора / ядра процессора. Когда BizTalk Server подсчитывает количество процессоров для настройки своих алгоритмов самонастройки; гиперпоточные процессоры вызывают искажение этих настроек, что пагубно сказывается на общей производительности. "

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

Вместо полного отключения HyperThreading вы можете использовать такие программы, как Process Lasso (бесплатно), чтобы установить соответствие ЦП по умолчанию для критических процессов, чтобы их потоки никогда не выделялись виртуальным ЦП.

Итак ... Я не думаю, что кто-то действительно знает, насколько хорошо Планировщик ЦП Windows обрабатывает виртуальные ЦП, но я думаю, можно с уверенностью сказать, что XP справляется с этим хуже, и с тех пор они постепенно улучшили его, но это все еще не идеален. Фактически, он НИКОГДА не может быть идеальным, потому что ОС не знает, какие потоки лучше всего использовать на этих более медленных виртуальных ядрах. Это может быть проблемой, и почему Microsoft рекомендует отключить HyperThreading в серверных средах.

Также помните, что даже БЕЗ HyperThreading существует проблема «перегрузки ядра». Если вы можете сохранить поток на одном ядре, это хорошо, поскольку это снижает штрафы за изменение ядра.

person dyasta    schedule 26.11.2010

Вы можете убедиться, что оба потока запланированы для одних и тех же исполнительных модулей, назначив им привязку к процессору. Это можно сделать как в Windows, так и в Unix, либо через API (чтобы программа могла запрашивать его), либо через административные интерфейсы (чтобы администратор мог установить это). Например. в WinXP вы можете использовать диспетчер задач, чтобы ограничить логические процессоры, на которых может выполняться процесс.

В противном случае планирование будет по существу случайным, и вы можете ожидать 25% использования каждого логического процессора.

person sk.    schedule 11.12.2008
comment
Хотя я никогда не был тем, кто любит оставлять все на усмотрение ОС, установка маски схожести потоков может нанести ущерб производительности, если что-то будет загружено. Будет ли SetThreadIdealProcessor () лучшим вариантом? - person NTDLS; 21.02.2009

Я не знаю о других платформах, но в случае Intel они публикуют много информацию о потоках в их Intel Software Network. У них также есть бесплатный информационный бюллетень (Intel Software Dispatch), на который вы можете подписаться по электронной почте, и в последнее время появилось много таких статей.

person Jim Anderson    schedule 11.12.2008

Вероятность того, что ОС отправит 2 активных потока в одно и то же ядро, равна нулю, если потоки не были привязаны к определенному ядру (сходство потоков).

Причины этого в основном связаны с HW:

  • ОС (и ЦП) хотят использовать как можно меньше энергии, чтобы выполнять задачи с максимальной эффективностью, чтобы как можно скорее перейти в состояние низкого энергопотребления.
  • Запуск всего на одном ядре заставит его нагреваться намного быстрее. В патологических условиях процессор может перегреться и снизить тактовую частоту до остывания. Избыточный нагрев также заставляет вентиляторы процессора вращаться быстрее (например, ноутбуки) и создавать больше шума.
  • Система никогда не простаивает. ISR и DPC запускаются каждые мс (в большинстве современных ОС).
  • Снижение производительности из-за переключения потоков от ядра к ядру незначительно в 99,99% рабочих нагрузок.
  • Во всех современных процессорах используется общий кеш последнего уровня, поэтому переключение ядер не так уж и плохо.
  • Для систем с несколькими сокетами (Numa) ОС минимизирует переключение от сокета к сокету, чтобы процесс оставался «рядом» со своим контроллером памяти. Это сложная область при оптимизации для таких систем (десятки / сотни ядер).

Кстати, ОС знает топологию ЦП через ACPI - интерфейс, предоставляемый BIOS.

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

person egur    schedule 06.01.2014
comment
Я не спрашивал список причин почему этого не следует, я думаю, мы все можем согласиться с этим. Я спрашивал, достаточно ли в ОС информации, чтобы предотвратить это, и достаточно ли умены планировщики, чтобы использовать эту информацию. Единственная часть вашего ответа, имеющая отношение к этому, - это упоминание ACPI. - person Mark Ransom; 07.01.2014
comment
В моем ответе было указано, почему и как планировщики ведут себя именно так, а также есть ли у них эта информация. Вы ищете фрагменты кода из ядра в качестве ответа? Если так, то ядра Linux и Darwin имеют открытый исходный код ... - person egur; 07.01.2014