Если мы посмотрим на документацию Visual C ++ из omp_set_dynamic
, она буквально копируется из стандарта OMP 2.0 (раздел 3.1.7 на стр. 39):
Если [аргумент функции] оценивается как ненулевое значение, число потоков, используемых для выполнения предстоящих параллельных областей, может автоматически регулироваться средой выполнения для наилучшего использования системных ресурсов.Как следствие, количество потоков, указанное пользователем, является максимальным количеством потоков.Количество потоков в команде, выполняющей параллельную область, остается фиксированным на протяжении этой параллельной области и сообщается функцией omp_get_num_threads
.
Кажется очевидным, что omp_set_dynamic(1)
позволяет реализациииспользовать меньшее, чем текущее, максимальное количество потоков для параллельной области (предположительно, для предотвращения переподписки при высоких нагрузках).Любое разумное прочтение этого абзаца предполагает, что указанное сокращение должно наблюдаться путем запроса omp_get_num_threads
внутри параллельных областей.
(обе документации также показывают подпись как void omp_set_dynamic(int dynamic_threads);
. Похоже, что «числопотоков, указанных пользователем "не относится к dynamic_threads
, а вместо этого означает" все, что пользователь указал с помощью оставшегося интерфейса OpenMP ").
Однако, независимо от того, насколько высоко я увеличиваю нагрузку на системупри omp_set_dynamic(1)
возвращаемое значение omp_get_num_threads
(запрашиваемое внутри параллельных областей) никогда не меняется от максимума в моей тестовой программе.Тем не менее, я все еще могу наблюдать явные различия в производительности между omp_set_dynamic(1)
и omp_set_dynamic(0)
.
Вот пример программы для воспроизведения проблемы:
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
#include <cstdlib>
#include <cmath>
#include <omp.h>
#define UNDER_LOAD true
const int SET_DYNAMIC_TO = 1;
const int REPEATS = 3000;
const unsigned MAXCOUNT = 1000000;
std::size_t threadNumSum = 0;
std::size_t threadNumCount = 0;
void oneRegion(int i)
{
// Pesudo-randomize the number of iterations.
unsigned ui = static_cast<unsigned>(i);
int count = static_cast<int>(((MAXCOUNT + 37) * (ui + 7) * ui) % MAXCOUNT);
#pragma omp parallel for schedule(guided, 512)
for (int j = 0; j < count; ++j)
{
if (j == 0)
{
threadNumSum += omp_get_num_threads();
threadNumCount++;
}
if ((j + i + count) % 16 != 0)
continue;
// Do some floating point math.
double a = j + i;
for (int k = 0; k < 10; ++k)
a = std::sin(i * (std::cos(a) * j + std::log(std::abs(a + count) + 1)));
volatile double out = a;
}
}
int main()
{
omp_set_dynamic(SET_DYNAMIC_TO);
#if UNDER_LOAD
for (int i = 0; i < 10; ++i)
{
std::thread([]()
{
unsigned x = 0;
float y = static_cast<float>(std::sqrt(2));
while (true)
{
//#pragma omp parallel for
for (int i = 0; i < 100000; ++i)
{
x = x * 7 + 13;
y = 4 * y * (1 - y);
}
volatile unsigned xx = x;
volatile float yy = y;
}
}).detach();
}
#endif
std::chrono::high_resolution_clock clk;
auto start = clk.now();
for (int i = 0; i < REPEATS; ++i)
oneRegion(i);
std::cout << (clk.now() - start).count() / 1000ull / 1000ull << " ms for " << REPEATS << " iterations" << std::endl;
double averageThreadNum = double(threadNumSum) / threadNumCount;
std::cout << "Entered " << threadNumCount << " parallel regions with " << averageThreadNum << " threads each on average." << std::endl;
std::getchar();
return 0;
}
Версия компилятора: Microsoft (R) CОптимизирующий компилятор / C ++ версии 19.16.27024.1 для x64
Например, на gcc эта программа напечатает averageThreadNum
значительно ниже omp_set_dynamic(1)
, чем для omp_set_dynamic(0)
.Но на MSVC в обоих случаях отображается одно и то же значение, несмотря на разницу в производительности на 30% (170 с против 230 с).
Чем это можно объяснить?