OpenMp是一种并行编程模型,旨在简化多线程编程,它给开发人员提供了一种在共享内存系统中利用多个处理器并行执行任务。
OpenMp使用指令将一段串行代码标记为并行区域,告诉编译器这段代码需要并行执行。
OpenMp和PThread区别:
包含头文件omp.h
Windows下使用vs开发时,右键项目 -》属性 -》C/C++ -》语言 -》OpenMp支持 -》选择是
parallerl 指令用于创建一个并行域,告诉编译器这段代码我要并行执行
#include
#include
#include
void Func()
{
int length = 5;
// num_thread表示创建几个线程执行,如果不指定就创建最大线程数
// omp_get_thread_num()打印当前线程id
#pragma omp parallel num_threads(3)
{
for (int i = 0; i < length; i++)
std::cout << i << ": " << omp_get_thread_num() << std::endl;
}
}
int main()
{
auto start = std::chrono::high_resolution_clock::now();
Func();
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "执行时间:" << duration.count() << std::endl;
system("pause");
return 0;
}
输出:
0: 0
1: 0
2: 0
3: 0
4: 0
0: 1
1: 1
2: 1
3: 1
4: 1
0: 2
1: 2
2: 2
3: 2
4: 2
执行时间:3659
可以看到每个线程都执行了五次循环,耗时3659us
#pragma omp parallel for 告诉编译器接下来的for循环要并行执行,使用的时候需要满足以下四点:
#include
#include
#include
void Func()
{
int length = 5;
#pragma omp parallel for num_threads(3)
for (int i = 0; i < length; i++)
std::cout << i << ": " << omp_get_thread_num() << std::endl;
}
int main()
{
auto start = std::chrono::high_resolution_clock::now();
Func();
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "执行时间:" << duration.count() << std::endl;
system("pause");
return 0;
}
输出:
0: 0
1: 0
4: 2
2: 1
3: 1
执行时间:2817
可以看到3个线程共执行了五次循环,耗时2817us
它们用于给每个线程创建共享变量的副本,以确保每个线程都有自己的私有变量,不会产生竞争关系。
#include
#include
#include
void Func()
{
int length = 5;
std::cout << "----------------------private用法--------------------" << std::endl;
#pragma omp parallel private(length) num_threads(3)
{
int tid = omp_get_thread_num();
length *= 10 * tid;
std::cout << "线程" << tid << ": " << length << std::endl;
}
std::cout << "主线程: " << length << std::endl;
std::cout << "----------------------firstprivate用法--------------------" << std::endl;
#pragma omp parallel firstprivate(length) num_threads(3)
{
int tid = omp_get_thread_num();
length *= 10 * tid;
std::cout << "线程" << tid << ": " << length << std::endl;
}
std::cout << "主线程: " << length << std::endl;
std::cout << "----------------------lastprivate用法--------------------" << std::endl;
#pragma omp parallel for lastprivate(length) num_threads(3)
for(int i = 0;i < 5;i++)
{
length = 5;
length++;
std::cout << "线程" << omp_get_thread_num() << ": " << length << std::endl;
}
std::cout << "主线程: " << length << std::endl;
}
int main()
{
auto start = std::chrono::high_resolution_clock::now();
Func();
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "执行时间:" << duration.count() << std::endl;
system("pause");
return 0;
}
输出:
----------------------private用法--------------------
线程0: 0
线程1: 0
线程2: 0
主线程: 5
----------------------firstprivate用法--------------------
线程0: 0
线程2: 100
线程1: 50
主线程: 5
----------------------lastprivate用法--------------------
线程0: 6
线程0: 6
线程2: 6
线程1: 6
线程1: 6
主线程: 6
执行时间:4754
可以看到,private用法中length在线程中的初始化值为0。
firstprivate选项:
关于firstprivate的信息:
关于变量的拷贝:
lastprivate选项告诉编辑器
OpenMP 的 section 是在并行代码中实现任务划分和分配的一种机制。section 用于将操作划分为多个独立的部分,这些部分可以被并行执行。
#include
#include
#include
void Func()
{
int length = 5;
#pragma omp parallel num_threads(3)
{
#pragma omp sections
{
#pragma omp section
{
int threadID = omp_get_thread_num();
std::cout << "Thread " << threadID << ", Section 1" << std::endl;
}
#pragma omp section
{
int threadID = omp_get_thread_num();
std::cout << "Thread " << threadID << ", Section 2" << std::endl;
}
#pragma omp section
{
int threadID = omp_get_thread_num();
std::cout << "Thread " << threadID << ", Section 3" << std::endl;
}
}
}
}
int main()
{
auto start = std::chrono::high_resolution_clock::now();
Func();
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "执行时间:" << duration.count() << std::endl;
system("pause");
return 0;
}
输出:
Thread 0, Section 1
Thread 1, Section 3
Thread 2, Section 2
执行时间:4200
通过 sections 和 section 的组合,我们可以将任务细分为多个独立的部分,让不同的线程并行地执行这些部分。这种方式可以在并行计算中提高效率,并充分利用多核处理器的性能。注意,sections 中的工作的划分是静态的,编译时就确定了各个 section 的分配情况,而不会根据实际运行时的情况动态调整。
reduction 是一种用于对多个线程私有变量进行合并操作的机制。它可以方便地进行诸如求和、求积、求最大值、求最小值等归约操作。
#include
#include
#include
void Func()
{
int length = 5;
#pragma omp parallel num_threads(3) reduction(+:length)
{
#pragma omp sections
{
#pragma omp section
{
int threadID = omp_get_thread_num();
length = length + 10;
std::cout << "Thread " << threadID << ": " << length << std::endl;
}
#pragma omp section
{
int threadID = omp_get_thread_num();
length = length + 20;
std::cout << "Thread " << threadID << ": " << length << std::endl;
}
#pragma omp section
{
int threadID = omp_get_thread_num();
length = length + 30;
std::cout << "Thread " << threadID << ": " << length << std::endl;
}
}
}
std::cout << "主线程: " << length << std::endl;
}
int main()
{
auto start = std::chrono::high_resolution_clock::now();
Func();
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "执行时间:" << duration.count() << std::endl;
system("pause");
return 0;
}
输出:
Thread 0: 10
Thread 1: 30
Thread 2: 20
主线程: 65
执行时间:1534
reduction 还支持其他操作符,如 -、*、max、min 等。
single 是 OpenMP 指令中的一个子句,它用于指定只有一个线程执行 single 区域中的代码。这个区域中的代码在并行环境下只会被一个线程执行,其他线程会跳过这部分代码。
#include
#include
#include
void Func()
{
int length = 5;
#pragma omp parallel num_threads(3) firstprivate(length)
{
#pragma omp single
{
length = 1;
}
#pragma omp barrier
std::cout << "Thread " << omp_get_thread_num() << ": " << length << std::endl;
}
std::cout << "主线程: " << length << std::endl;
}
int main()
{
auto start = std::chrono::high_resolution_clock::now();
Func();
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "执行时间:" << duration.count() << std::endl;
system("pause");
return 0;
}
输出:
Thread 2: 5
Thread 0: 1
Thread 1: 5
主线程: 5
执行时间:2233
在这个例子中,single 子句定义了一个代码块,它只会由一个线程执行,在代码块中,这个线程将变量 x 的值设置为1。然后使用 barrier 指令同步所有线程,确保在输出 x 前,single 区域中的代码已经执行完毕。执行完后,每个线程输出变量 x 的值,这时只有一个线程的 x 值为 1,其他线程的 x 值为 0。
需要注意的是,single 指令只保证至多一个线程执行这部分代码,但并不能保证是哪个线程执行,也不能保证是同一个线程执行。如果需要指定具体的线程执行代码,可以使用 master 指令。
master子句告诉编译器接下来紧跟的下段代码将会会由主线程执行,它不会出现等待现象。
barrier 是一个 OpenMP 中的指令,用于同步并行区域中的线程。它的作用是确保在指定的并行区域内的所有线程都到达 barrier 指令之前不会继续执行下去,直到所有线程都到达 barrier 之后才会继续执行后面的代码。
barrier 指令可以用来处理需要等待其他线程执行完毕才能继续执行的情况,例如需要确保某些共享数据的一致性或者需要线程之间进行数据交换等。
在使用 barrier 指令时,需要注意以下几点:
1、barrier 指令必须在并行区域内使用,否则会被忽略。
2、所有参与并行区域的线程都必须到达 barrier 之前的位置,否则可能导致死锁或其他错误。
3、barrier 之后的代码只有在所有线程都到达 barrier 之后才会执行,因此应谨慎使用 barrier,避免出现线程之间的不平衡。
4、在嵌套的并行区域中,每个并行区域都有自己的 barrier,因此需要确保每个并行区域内的所有线程都正确地使用了 barrier。
#include
#include
#include
void Func()
{
int length = 5;
#pragma omp parallel num_threads(4)
{
int thread_num = omp_get_thread_num();
std::cout << "Thread " << thread_num << " before barrier" << std::endl;
#pragma omp barrier
std::cout << "Thread " << thread_num << " after barrier" << std::endl;
}
}
int main()
{
auto start = std::chrono::high_resolution_clock::now();
Func();
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "执行时间:" << duration.count() << std::endl;
system("pause");
return 0;
}
输出:
Thread 0 before barrier
Thread 2 before barrier
Thread 3 before barrier
Thread 1 before barrier
Thread 1 after barrier
Thread 0 after barrier
Thread 2 after barrier
Thread 3 after barrier
执行时间:2670
在这个示例中,我们创建了一个并行区域,使用了 num_threads(4) 指定了并行区域中的线程数量为4。在每个线程中,我们输出了线程号并在 barrier 前后进行了打印。运行这段代码可以看到,所有线程都会在 barrier 处等待,直到所有线程都到达 barrier,然后继续执行后面的代码。
函数名 | 函数作用 |
---|---|
omp_set_num_threads(n) | 设置 OpenMP 并行区域中的线程数量为 n |
omp_get_num_threads() | 获得正在运行的并行区域的线程数量。 |
omp_get_thread_num() | 获得当前线程的线程号。 |
omp_get_max_threads() | 获得可以用于并行执行的最大线程数。 |
omp_get_num_procs() | 获得当前计算机中可用于并行计算的处理器数目。 |
omp_set_dynamic(boolean) | 设置或取消运行时动态调整线程数量调度。 |
omp_set_nested(boolean) | 启用或禁用嵌套并行执行。 |
omp_set_num_threads | 设置后续并行域中的线程格式 |
omp_in_parallel | 判断当前是否在并行域中 |