为什么OMP_SET_DYNAMIC(1)切勿调整线程数(在Visual C 中)
Why does omp_set_dynamic(1) never adjust the number of threads (in Visual C++)?
如果我们查看omp_set_dynamic
的Visual C 文档,则实际上是从OMP 2.0标准(第39页的3.1.7节(复制的:
如果[函数参数]评估为非零值,则可以自动调整用于执行即将到来的并行区域的线程数量,以自动调整运行时环境以最好地使用系统资源。结果,用户指定的线程数是最大线程计数。执行平行区域的团队中的线程数固定在该并行区域的持续时间内,并由
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,该程序将对omp_set_dynamic(1)
打印的averageThreadNum
明显低于omp_set_dynamic(0)
。但是在MSVC上,尽管性能差异30%(170s vs 230s(,在两种情况下都显示了相同的值。
如何解释?
在Visual C 中,执行循环的线程数在此示例中使用omp_set_dynamic(1)
减少了,这解释了性能差异。
但是,与标准(和Visual C 文档(的任何善意解释相反, omp_get_num_threads
不报告此减少。
弄清楚多少个线程MSVC 实际使用多少个线程的方法是每个并行区域使用 em 循环迭代(或并行任务(的omp_get_thread_num
。以下是在很少的环内性能开销中进行的一种方法:
// std::hardware_destructive_interference_size is not available in gcc or clang, also see comments by Peter Cordes:
// https://stackoverflow.com/questions/39680206/understanding-stdhardware-destructive-interference-size-and-stdhardware-cons
struct alignas(2 * std::hardware_destructive_interference_size) NoFalseSharing
{
int flagValue = 0;
};
void foo()
{
std::vector<NoFalseSharing> flags(omp_get_max_threads());
#pragma omp parallel for
for (int j = 0; j < count; ++j)
{
flags[omp_get_thread_num()].flagValue = 1;
// Your real loop body
}
int realOmpNumThreads = 0;
for (auto flag : flags)
realOmpNumThreads += flag.flagValue;
}
的确,您会发现realOmpNumThreads
与在Visual C 上使用omp_set_dynamic(1)
内的omp_get_num_threads()
产生明显不同的值。
一个人可以说技术上
- " 团队中的线程数执行并行区域"和
- " 使用的线程数用于执行即将到来的并行区域"
实际上并不相同。
这是我认为对标准的毫无意义的解释,因为意图很明确,并且没有理由说" "团队中执行并行区域的线程数量停留固定在该并行区域的持续时间内,如果此数字与 但是,可能是MSVC决定将线程数保持在> Team 中,而不受影响,只有在 无论情况如何:在Visual C 中不信任omp_set_dynamic
的功能无关,则omp_get_num_threads
函数 在本节中报告了。omp_set_dynamic(1)
下,将执行的无循环迭代到其中的一个子集易于实施。omp_get_num_threads
。
- Visual Studio 发布模式阻止在调试模式下执行的代码.使用 WinHTTP 和多线程
- Visual Studio Debug 只在一个线程中
- 尝试在我的基类中启动线程时,Visual Studio 由于调试错误而中止我的程序
- 在 Visual Studio 中的调试中断时设置默认线程,C++
- 使用 Visual c++ 进行多线程同步不起作用
- 为什么OMP_SET_DYNAMIC(1)切勿调整线程数(在Visual C 中)
- 当并行线程访问同一数据结构的其他成员时,正确的方法可以在Visual Studio上的OpenMP上并行循环
- 从 Visual C++ 2010 中的单独线程调用消息框
- 是否使用符合标准的线程池进行 std::async 的 Visual C++ 实现
- 如何在Visual Studio中设置C /C代码仅在一个线程中运行以测试运行时间
- Visual Studio 2012错误C2248在STD ::线程中
- Visual创建一个独立于C 父母的子线程
- 我如何同步Visual C 2010中的线程
- 带有C 11和线程支持的Visual Express C
- 将函子传递给boost ::线程在Visual Studio 2010中失败
- visual C++-传递给线程的数据是否应该是易失性的
- Visual Studio使用多线程DLL发布应用程序文件
- Visual Studio 2012 C++使用 "parallel_for_each" 的多线程
- Visual Studio 2013标准::线程
- 无法链接到 Window 7 上的 Visual Studio 2013 中的 boost 1.60 线程库