快报:Visual Studio改进了对OpenMP的支持
官宣
随着多核/处理器时代的来临,越来越多的编程语言开始支持任务并行的概念,允许开发者将多个任务划分到不同的处理器上并行运行。OpenMP最初发布于1990年代,其主要的目标就是为C/C++/Fortran等语言提供这种任务并行执行的能力,今天,随着标准的持续演进,OpenMP新增了一些新的特性,例如将任务卸载到其他设备执行,以及在线程级别对任务执行精细控制。
Visual Studio自2005年就开始支持OpenMP 2.0版本,在Visual Studio 2019最初的发布版本中,我们添加了一个新的编译器开关”openmp:experimental”,以启用对OpenMP SIMD指令集的支持,OpenMP SIMD指令集是在OpenMP 4.0版本中首次加入。
我们的OpenMP支持计划
从Visual Studio 2019 v16.9版本开始,我们就一直尝试以一种更加系统化的方式添加对新版本的OpenMP标准的支持。第一步,我们添加了一种新的代码生成方式,以在amd64架构上生成与LLVM OpenMP运行时库(libomp)兼容的代码。然后,我们就可以借助LLVM的OpenMP运行时库来增加对额外的一些OpenMP特性的支持。当我们发现LLVM OpenMP运行时的Windows版本有Bug时,我们会进行修复并回馈给LLVM社区。
接下里,我们的下一个目标是:添加面向x86,amd64和arm64架构的OpenMP 3.1标准的支持。然后,我们还将支持OpenMP 4.5标准中的不包含任务卸载的pragmas和clauses特性。
总体来说,我们准备添加的新特性取决于用户的使用反馈。你可以告诉我们你最希望添加那些OpenMP特性,这样我们就可以为这些特性的开发设定优先级。
新的编译器开关
通过使用新的编译器开关”-openmp:llvm”,程序代码可以面向LLVM OpenMP运行时库进行编译。在Visual Studio v16.9版本中,”-openmp:llvm”编译器开关只能运行在amd64架构上。它当前支持所有OpenMP2.0的指令集,就像之前的”-openmp”开关一样,另外,它还支持OpenMP3.0中的”unsigned integer indices in parallel for loops”。在接下来的新版本中,我们会添加更多的指令集。”-openmp:llvm”开关兼容”openmp:experimental”开关支持的所有SIMD指令集。
使用”-openmp:llvm”编译器开关编译一个可执行文件会自动添加对应的libomp DLL的动态链接。为了使可执行文件顺利运行,它需要能访问到140d.x86_64.dll(如果是编译为Debug模式)或者libomp140.x86_64.dll。这些DLL文件可以在Visual Studio 的安装目录下(VC\Redist\MSVC\\debug_nonredist\x64\Microsoft.VC142.OpenMP.LLVM)找到,同时它们会自动地添加到PATH环境变量中。
由于”-openmp:llvm”开关目前仍处于实验特性,所以release模式和debug模式都启用了断言,这可以更加容易的检测不正确的行为,但是会稍微降低执行性能。这些DLL使用了”CMAKE_BUILD_TYPE=RelWithDebInfo”和”LLVM_ENABLE_ASSERTIONS=ON”进行编译。将来的libomp DLL可能不会向后兼容,并且当前版本的这些DLL是不可分发的。
另外,请注意:”-openmp:llvm”和/clr或者/ZW开关不兼容。
-openmp:llvm带来的改进
通过使用”-openmp:llvm”,可以修正程序中的一些问题。在Visual Studio 2019 v16.9 预览版3中,#pragma omp sections中的lastprivate语句可以被正确地处理了。与使用sections时,lastprivate语句保证在退出一个sections block时,该子句中列出的变量将被设置为等于最后一部分中该变量的私有版本。举个例子,当下面的代码执行完毕后,x的值会是6。
Visual Studio 2019 v16.9 预览版4中也修复了一些优化器的Bug以正确处理OpenMP的构造过程。
MSVC现在会避免在隐式或显式的刷新边界上移动写操作。让我们看看下面这个例子,它使用了#pragma omp flush语句:
在某些情况下,编译器的旧版本可能会通过如下的代码修改,错误地优化潜在的重复写操作,如下图所示:
但是,此优化不遵守#pragma omp flush的保证。通过原始代码,因为omp_get_thread_num对于组中的一个线程恰好返回0,所以只有该线程将在刷新点之后写入x,而x将是10。因为在优化之后,其他线程可以在刷新点之后写入x。并创建竞争条件,那么优化是不合法的。
优化器还将正确识别出,即使函数本地的变量也可以被OpenMP并行区域内的其他线程更改。例如,在以下代码中,”x > shared”判断中的shared值不能替换为-1,因为自初始赋值以来可能已经有另一个线程写入了shared:
-openmp:llvm带来的新特性
除了正确性修复之外,新的”-openmp:llvm”开关已经支持OpenMP 3.0标准中添加的一些功能。
并行for循环现在可以使用无符号整数作为索引。
增加了对#pragma omp任务的有限支持,但是不能保证有关任务pragma的子句可以正常工作。由于此时#pragma omp任务有很多限制,因此仅在”-openmp:experimental”开关下支持pragma。
OpenMP简介
(以下内容来自:OSCHINA的openMP多核编程框架简介)
OpenMP是由OpenMP Architecture Review Board牵头提出的,并已被广泛接受的,用于共享内存并行系统的多线程程序设计的一套编译指令 (Compiler Directive)。OpenMP支持的编程语言包括C语言、C++和Fortran;而支持OpenMP的编译器包括Sun Compiler,GNU Compiler和Intel Compiler等。OpenMP提供了对并行算法的高层的抽象描述,程序员通过在源代码中加入专用的pragma来指明自己的意图,由此编译器可以自动将程序进行并行化,并在必要之处加入同步互斥以及通信。当选择忽略这些pragma,或者编译器不支持OpenMP 时,程序又可退化为通常的程序(一般为串行),代码仍然可以正常运作,只是不能利用多线程来加速程序执行。
OpenMP提供的这种对于并行描述的高层抽象降低了并行编程的难度和复杂度,这样程序员可以把更多的精力投入到并行算法本身,而非其具体实现细节。对基于数据分集的多线程程序设计,OpenMP是一个很好的选择。同时,使用OpenMP也提供了更强的灵活性,可以较容易的适 应不同的并行系统配置。线程粒度和负载平衡等是传统多线程程序设计中的难题,但在OpenMP中,OpenMP库从程序员手中接管了部分这两方面的工作。
但是,作为高层抽象,OpenMP并不适合需要复杂的线程间同步和互斥的场合。
OpenMP的另一个缺点是不能在非共享内存系统(如计算机集群)上使用。在这样的系统上,MPI使用较多。
总结
你愿不愿意,将任务的执行管控交给素不相识的OpenMP框架?
还是,你想自己来管理这些任务?
最后
Microsoft Visual C++团队的博客是我非常喜欢的博客之一,里面有很多关于Visual C++的知识和最新开发进展。大浪淘沙,如果你对Visual C++这门古老的技术还是那么感兴趣,则可以经常去他们那(或者我这)逛逛。
本文来自:《Improved OpenMP Support for C++ in Visual Studio》
- 下一篇: Visual Studio Code C++扩展更新
- 上一篇: 如何获取鼠标下的文本
相关推荐
- 小技巧:加快VS2010编译速度
- Posted on 07月07日
- 说说LocalAlloc和GlobalAlloc的区别
- Posted on 12月11日
- 为什么我不能去掉”仅用于测试”的标志?
- Posted on 12月30日
- 进度更新:VS2019 v16.8中对C++20模块的支持
- Posted on 09月16日
评论已关闭。