Как оптимизировать для двух-, четырехъядерных процессоров и более высоких процессоров?
Люди, я программировал высокоскоростное программное обеспечение более 20 лет и знаю практически каждый трюк в книге из микро-скамьи, делающей совместную работу, профилирование, многозадачность пользовательского режима, рекурсию хвоста, вы называете ее очень высокоэффективной Linux, Windows и многое другое.
Проблема заключается в том, что я нахожусь в недоумении от того, что происходит, когда несколько потоков интенсивной работы с ЦП подвергаются воздействию многоядерных процессоров.
Результаты производительности в микро-тестах различных способов обмена датами между потоками (на разных ядрах), похоже, не следуют логике.
Ясно, что между ядрами существует некоторое "скрытое взаимодействие", которое не очевидно из моего собственного кода программирования. Я слышал о кеше L1 и других проблемах, но это непрозрачно для меня.
Вопрос: где я могу узнать этот материал? Я ищу подробный справочник о том, как работают многоядерные процессоры, как программировать для использования кэшей памяти или другой аппаратной архитектуры, а не наказывать их.
Любые советы или отличные сайты или книги? После большого Googling, я прихожу пустым.
С уважением,
Wayne
Ответы
Ответ 1
Эта книга научила меня многим вопросам такого рода, почему необработанная мощность процессора не является единственной вещью, на которую следует обратить внимание. Я использовал его в аспирантуре несколько лет назад, но я думаю, что все принципы все еще применяются:
http://www.amazon.com/Computer-Architecture-Quantitative-Approach-4th/dp/0123704901
По сути, основной проблемой в многопроцессорных конфигурациях является синхронизация доступа к основной памяти, и если вы не сделаете это правильно, это может стать реальным узким местом в производительности. Это довольно сложно с кешами, которые нужно синхронизировать.
Ответ 2
мой собственный вопрос с ответом на дочернем сайте stackoverflow: https://softwareengineering.stackexchange.com/questions/126986/where-can-i-find-an-overview-of-known-multithreading-design-patterns/126993# 126993
Я скопирую ответ, чтобы избежать необходимости переходов по ссылкам:
Цитата Борис:
Параллельное программирование в Microsoft.NET: шаблоны проектирования для декомпозиции и координации на многоядерных архитектурах https://rads.stackoverflow.com/amzn/click/0735651590
Это книга, я рекомендую всем сердцем.
Это:
Новое - опубликовано в прошлом году. Означает, что вы не читаете несколько устаревших практик.
Коротко о страницах 200+, насыщенных информацией. В наши дни слишком много для чтения и слишком мало времени для чтения 1000+ страниц книг.
Легко читается - он не только очень хорошо написан, но и вводит понятия, которые трудно понять, в действительно простом для чтения виде.
Предназначен для обучения - каждая глава дает упражнения для выполнения. Я знаю, что это всегда полезно, но редко делаю. Эта книга дает очень интересные и интересные задачи. Удивительно, но я сделал большинство из них и получил удовольствие от них.
Кроме того, если вы хотите узнать больше о низкоуровневых деталях, это лучший ресурс, который я нашел: " Искусство многопроцессорного программирования ". Он написан с использованием java в качестве примеров кода, что прекрасно сочетается с моим фоном С#.
PS: у меня около 5 лет опыта работы в параллельном программировании на основе "хардкор" (хотя и с использованием С#), поэтому надеюсь, что вы можете мне доверять, когда я скажу, что " Искусство многопроцессорного программирования " рушится
Ответ 3
Ответ 4
Одной из конкретных причин неожиданных плохих результатов в параллельном коде является ложное использование, вы не увидите, что это произойдет, если вы не знаете, что происходит там (я этого не делал). Вот две статьи, в которых обсуждаются причины и способы устранения .Net:
http://msdn.microsoft.com/en-us/magazine/cc872851.aspx
http://www.codeproject.com/KB/threads/FalseSharing.aspx
Rgds GJ
Ответ 5
Существуют различные аспекты многопоточности, требующие разных подходов.
На веб-сервере, например, использование потоков-пулов широко используется, поскольку оно якобы "хорошо для" производительности. Такие пулы могут содержать сотни потоков, ожидающих выхода на работу. Использование этого большого количества потоков приведет к тому, что планировщик будет работать сверхурочно, что вредно для производительности, но в Linux-системах этого не избежать. Для Windows выбранным методом является механизм IOCP, который рекомендует несколько потоков, не превышающих количество установленных ядер. Это заставляет приложение запускать событие ввода-вывода (I/O), что означает, что никакие циклы не теряются при опросе. Несколько потоков привели к минимуму работы планировщика.
Если объект должен реализовать масштабируемую функциональность (больше ядер <= > более высокая производительность), то основной проблемой будет насыщение шины памяти. Насыщение будет происходить из-за выбора кода, чтения данных и записи данных. Некорректно реализованный код будет работать медленнее с двумя потоками, чем с одним. Единственный способ уменьшить нагрузку на шину памяти:
- адаптация кода к минимальному объему памяти (= соответствует кешу кода) и который не вызывает другие функции или не перескакивает повсюду.
- портняжная память считывает и записывает минимальный размер.
- информирует механизм предварительной выборки текущей памяти.
- адаптируя работу таким образом, чтобы отношение работы, выполняемой внутри основного кэша (L1 и L2), было максимально возможным по отношению к работе вне их (L3 и ОЗУ).
Поставить это по-другому: подберите применимый код и фрагменты данных как можно меньше строк кеша (по 64 байта), потому что в конечном итоге это будет определять масштабность. Если система кэша/памяти способна выполнять операции кэширования x каждую секунду, ваш код будет работать быстрее, если его требования - пять строк кэша на единицу работы (= > x/5), а не одиннадцать (x/11) или пятьдесят два (х/52).
Достижение этого не является тривиальным, поскольку оно требует более или менее уникального решения каждый раз. Некоторые компиляторы хорошо справляются с инструкциями по обучению, чтобы воспользоваться конвейерной обработкой хост-процессора. Это не обязательно означает, что это будет хороший порядок для нескольких ядер.
Эффективная реализация масштабируемого кода не обязательно будет довольно хорошей. Рекомендуемые методы кодирования и стили могут, в конце концов, препятствовать выполнению кода.
Мой совет - проверить, как это работает, написав простое многопоточное приложение на низкоуровневом языке (например, C), которое может быть настроено для запуска в одно- или многопоточном режиме, а затем профилирование кода для разных режимах. Вам необходимо проанализировать код на уровне инструкций. Затем вы экспериментируете с использованием различных конструкций кода (C), организации данных и т.д. Возможно, вам придется подумать нестандартно и переосмыслить алгоритм, чтобы сделать его более удобным для кэширования.
В первый раз потребуется много работы. Вы не узнаете, что будет работать для всех многопоточных решений, но вы, возможно, получите представление о том, что не делать и какие указания искать при анализе профилированного кода.
Ответ 6
Я нашел эту ссылку, которая специально объясняет проблемы
многоядерная обработка кеша на процессорах, которые влияли на мои
многопоточная программа.
http://www.multicoreinfo.com/research/intel/mem-issues.pdf
На сайте multicoreinfo.com есть много хорошего
информация и ссылки о многоядерном программировании.