openMP初试

openMP初试

openMP框架是用C、C++和Fortran进行并发编程的一种方法。相比较多线程,这种写法提供了一些非常简单的API,调用起来也更简单。

编写openMP代码进行测试之前,首先需要让你的编译器来支持openMP,就是增加一些编译时候的参数即可,一般加上-fopenmp和openmp链接库-lgomp -fopenmp就好了。否则可能看不到效果。

上手一个入门代码:

去掉#pragma omp parallel for执行时间明显增长,具体有几倍根据电脑的实际性能。我在本地虚拟机上跑的效果大概在2倍,因为用lscpu命令查看到只有2个核。如果是4个核或者更多,效果更明显。

通俗地解释openMP,就是当编译器发现#pragma omp parallel for后,自动将下面的for循环分成N份,(N为电脑CPU核数),然后把每份指派给一个核去执行,而且多核之间为并行执行。

private

openMP中变量默认为shared,如果需要多个并行块中的变量是私有的,可以写成#pragma omp parallel for private(j)

reduction

去掉#pargma…后结果肯定是55,但是加上之后可能值是一个小于55的值了。这因为加法操作不是原子操作。具体来说,当某线程A执行sum = sum + a[i]的同时,另一线程B正好在更新sum,而此时A还在用旧的sum做累加,于是出现了错误。

解决方法,除了开一个b[cpu_cores]的数组来分别存值,cpu_cores代表cpu核心数也就是openMP能够同时并发的线程数。比较好的方法就是用reduction,即#pragma omp parallel for reduction(+:tot),它的意思是告诉编译器:下面的for循环你要分成多个线程跑,但每个线程都要保存变量sum的拷贝,循环结束后,所有线程把自己的tot累加起来作为最后的输出。

reduction虽然很方便,但它只支持一些基本操作,比如+,-,*,&,|,&&,||等。有些情况下,我们既要避免race condition,但涉及到的操作又超出了reduction的能力范围,应该怎么办呢?这就要用到openMP的另一个工具,critical。来看下面的例子,该例中我们求数组a的最大值,将结果保存在max里。

critical

执行到critical里面时,要注意有没有其他线程正在里面执行,如果有的话,要等其他线程执行完再进去执行。这样就避免了进程同步问题,但显而易见,它的执行速度会变低,因为可能存在线程等待的情况。

关于线程安全

vector等STL库不是线程安全的!相当于两个线程在同时push_back一个vector,产生冲突。

下面的这段代码,去掉#pragma…就会crash。

如何解决vector的插入引入的不线程安全的问题呢,我们采用每一个线程都使用一个独立的vector,然后做完这个线程再合并到总的vector里面的方法。

插入是并发的,最后的合并到总的是并行的,因此最终在vec的几段连续的数值。例如250..375,0..124,125..250,375..500这个顺序。

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注