2013-11-12 2 views
1

Я использую OpenMP для петли, как это:Я нарушаю другие программы с помощью OpenMP?

#pragma omp parallel for 
for (int out = 1; out <= matrix.rows; out++) 
{ 
    ... 
} 

Я делаю много вычислений на машине с 64 процессорами. Это работает довольно хорошо, но мой вопрос: Я беспокою других пользователей на этой машине? Обычно они запускают только однопоточные программы. Будут ли они все еще работать на 100%? Очевидно, что я буду беспокоить другие многопоточные программы, но буду ли я мешать программам с одним потоком? Если да, могу ли я превзойти это? Я думаю, что можно установить максимальное количество процессоров с omp_set_num_threads. Я могу установить это на 60, но я не думаю, что это лучшее решение. Идеальное решение не помешает никаким другим программам с одним потоком, а займет как можно больше процессоров.

+0

Вам не нужно вызывать 'omp_set_num_threads()' для управления количеством потоков. Просто установите переменную среды OMP_NUM_THREADS' в нужное максимальное количество потоков. –

ответ

0

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

+0

Хорошо, это звучит хорошо. Но если появится другой пользователь с 64 потоками, мой процесс придется ждать. Возможно, это хорошая практика, чтобы рассказать всем пользователям, использующим несколько потоков, поставить их на низкий приоритет. И только процессы с одним потоком будут использовать обычный приоритет? – casaro

1

У каждой многозадачной ОС есть что-то, называемое планировщиком процессов. Это компонент ОС, который решает, где и когда запускать каждый процесс. Планировщики обычно довольно упрямы в принимаемых ими решениях, но на них часто могут влиять различные политики и подсказки, предоставленные пользователем. Конфигурация по умолчанию для почти любого планировщика заключается в попытке распространить нагрузку на все доступные процессоры, что часто приводит к миграции процессов с одного процессора на другой. К счастью, любая современная ОС, кроме «самой передовой настольной ОС» (a.k.a. OS X), поддерживает что-то, называемое процессорным сродством. Каждый процесс имеет набор процессоров, на которых ему разрешено выполнять - так называемый набор аффинности процессора этого процесса. Путем настройки непересекающихся наборов аффинности на различные процессы они могут выполняться одновременно без кражи процессорного времени друг от друга. Явное совместимость с процессором поддерживается в Linux, FreeBSD (с планировщиком ULE), Windows NT (это также включает все настольные версии с Windows XP) и, возможно, другие ОС (но не OS X). Каждая ОС затем предоставляет набор вызовов ядра для манипулирования связью, а также инструмент для этого, не создавая специальной программы. В Linux это делается с использованием системного вызова sched_setaffinity(2) и инструмента командной строки taskset. Affinity можно также контролировать, создав экземпляр cpuset. В Windows используется SetProcessAffinityMask() и/или SetThreadAffinityMask(), а в диспетчере задач из контекстного меню для заданного процесса можно установить аффинности. Также можно указать желаемую маску сродства в качестве параметра команды оболочки START при запуске новых процессов.

Что это все связано с OpenMP, так это то, что большинство запусков OpenMP для перечисленных ОС поддерживают в той или иной форме, чтобы указать желаемое отношение к процессору для каждого потока OpenMP. Простейшим элементом управления является переменная среды OMP_PROC_BIND. Это простой переключатель - при установке на TRUE он инструктирует среду выполнения OpenMP «привязать» к каждому потоку, т. Е. Дать ему набор аффинити, включающий только один процессор. Фактическое размещение потоков в ЦП зависит от реализации, и каждая реализация обеспечивает свой собственный способ управления им. Например, среда выполнения GNU OpenMP (libgomp) считывает переменную среды GOMP_CPU_AFFINITY, а среда Intel OpenMP (с открытым исходным кодом с недавнего времени) читает переменную среды KMP_AFFINITY.

Обоснование заключается в том, что вы можете ограничить сродство своей программы таким образом, чтобы использовать только подмножество всех доступных процессоров. Затем оставшиеся процессы получат доступ к остальным процессорам, но это гарантируется только в том случае, если вы вручную установили их близость (что возможно только при наличии доступа root/Administrator, поскольку в противном случае вы можете изменить сродство только процессов, которые у вас есть).

Следует отметить, что часто (но не всегда) нет смысла запускать больше потоков, чем количество процессоров в наборе аффинити. Например, если вы ограничите свою программу на 60 процессоров, то использование 64 потоков приведет к тому, что некоторые процессоры будут переписываться и в режиме разговора между потоками. Это заставит некоторые потоки работать медленнее, чем другие. Планирование по умолчанию для большинства рабочих циклов OpenMP равно schedule(static), и поэтому общее время выполнения параллельной области определяется временем выполнения самого медленного потока. Если один поток раздается с другим, то оба потока будут выполняться медленнее, чем те потоки, которые не занимаются таймером, и вся параллельная область будет задерживаться. Мало того, что это уменьшает параллельную производительность, но также приводит к растратам, поскольку более быстрые потоки просто просто ничего не предпринимают (возможно, заняты петлями на неявном барьере в конце параллельной области). Решение состоит в том, чтобы использовать динамическое планирование, т.е .:

#pragma omp parallel for schedule(dynamic,chunk_size) 
for (int out = 1; out <= matrix.rows; out++) 
{ 
    ... 
} 

, где chunk_size является размером итерационного фрагмента, который получает каждый поток. Все итерационное пространство делится на куски chunk_size итераций и передается рабочим потокам по принципу «первым пришел-первым-обслужен». Размер блока является важным параметром. Если он слишком низок (по умолчанию - 1), тогда из среды выполнения OpenMP может возникнуть огромная нагрузка, управляющая динамическим планированием. Если он слишком высок, то может быть недостаточно работы для каждого потока. Нет смысла иметь размер куска больше, чем maxtrix.rows/#threads.

Динамическое планирование позволяет вашей программе адаптироваться к доступным ресурсам ЦП, даже если они не являются однородными, например. если есть другие процессы, выполняющие и разделяющие время с текущим. Но он поставляется с уловкой: большая система, такая как ваш 64-ядерный, обычно представляет собой системы ccNUMA (кэш-когерентные неравномерные системы доступа к памяти), что означает, что каждый процессор имеет свой собственный блок памяти и доступ к блоку (-ам) памяти другие CPU (ы) являются дорогостоящими (например, занимает больше времени и/или обеспечивает меньшую пропускную способность). Динамическое планирование имеет тенденцию разрушать местоположение данных, поскольку нельзя быть уверенным, что блок памяти, который находится на одном NUMA, не будет использоваться потоком, запущенным на другом узле NUMA. Это особенно важно, когда наборы данных большие и не вписываются в кэши CPU. Поэтому YMMV.

Смежные вопросы