目录
1 openMP并行编程
openMP针对共享内存并行编程的API。#include <omp.h>
1.1 常见命令
编译:g++ xxx.cpp -fopenmp
设置线程数:omp_set_num_threads(4) 不超过cpu核心数
查看每个物理CPU中core的核数:cat /proc/cpuinfo| grep "cpu cores"| uniq
查看逻辑CPU的个数:cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c
获取当前线程id号:omp_get_thread_num()
获取线程组中的线程数:omp_get_num_threads()
设置是否允许OpenMP进行嵌套并行:omp_set_nested(true),默认的设置为false
1.2 限制条件
- OpenMP只能并行化for循环,它不会并行while和do-while循环,而且只能并行循环次数在for循环外面就确定了的for循环。
- 循环变量只能是整型和指针类型(不能是浮点型)
- 循环语句只能是单入口单出口的。循环内部不能改变index,而且里面不能有goto、break、return。但是可以使用continue,因为它并不会减少循环次数。另外exit语句也是可以用的
1.3 循环结构并行
- for制导语句两种形式
#pragma omp parallel for
for()
#pragma omp parallel //并行块中可以出现多个for制导指令
{
#pragma omp for
for()
}
- 有主线程或选择一个线程执行
#pragma omp master/single
{}
- 数据同步,critical制导语句
#pragma omp critical //放在同步数据上一行
- 分段并行
#pragma omp sections //第1个sections
{
#pragma omp section
{}
}
- 规约子句,操作符包括:+,-,*,/,&,|,^,&&,||
#pragma omp parallel num_threads(num) reduction(+:global_result)
global_result += functionTest();
2 无锁编程__sync__
2.1 CAS原子操作实现无锁
无锁编程主要是使用原子操作替代锁来实现对共享资源的访问保护。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步。使用lock实现线程同步有很多缺点:产生竞争时,线程被阻塞等待,无法做到线程实时响应、dead lock、live lock、优先级翻转、使用不当,造成性能下降。
CAS是一组原语指令,用来实现多线程下的变量同步。CAS原语有三个参数:内存地址,期望值,新值。如果内存地址的值==期望值,表示该值未修改,此时可以修改成新值。使用CAS会造成ABA问题,特别是在使用指针操作一些并发数据结构时。解决方案:ABAʹ,添加额外的标记用来指示是否被修改,使用compare-and-swap(CAS)代替锁保证并发情况下数据的完整性。无锁算法广泛应用于操作系统与JVM中,比如线程和进程的调度、垃圾收集、实现锁和其他并发数据结构。
GCC4.1+版本中支持CAS的原子操作(完整的原子操作可参看 GCC Atomic Builtins)
2.2 原子操作函数
gcc内置的__sync_*函数提供了加减和逻辑运算的原子操作
先fetch,然后自加,返回的是自加以前的值:__sync_fetch_and_add()
先自加,再返回:__sync_add_and_fetch()
2.3 无锁函数
如果*ptr == oldval,就将newval写入*ptr
相等并写入的情况下返回true:bool __sync_bool_compare_and_swap (type*ptr, type oldval, type newval, ...)
返回操作之前的值:type __sync_val_compare_and_swap (type *ptr, type oldval, type newval, ...)
将*ptr设为value并返回*ptr操作之前的值:type __sync_lock_test_and_set (type *ptr, type value, ...)
将*ptr置0:void __sync_lock_release (type *ptr, ...)