我正在尝试并行化 (OpenMP) 一些科学 C++ 代码,其中大部分 (>95%) 的 CPU 时间花在计算 N 阶的讨厌(且不可避免)O(N^2) 交互上~ 200 种不同的颗粒。该计算重复 1e10 个时间步长。我用 OpenMP 尝试了各种不同的配置,每一个都比串行代码慢一些(至少一个数量级),并且随着额外的内核的添加,扩展性很差。
下面是相关代码的草图,具有代表性的虚拟数据层次结构 Tree->Branch->Leaf。每个 Leaf 对象存储自己的位置和当前和前三个时间步长的速度,等等。然后每个 Branch 存储一组 Leaf 对象,每个 Tree 存储一组 Branch 对象。这种数据结构非常适合复杂但 CPU 密集度较低的计算,这些计算也必须在每个时间步执行(需要几个月才能完善)。
#include <omp.h>
#pragma omp parallel num_threads(16) // also tried 2, 4 etc - little difference - hoping that placing this line here spawns the thread pool at the onset rather than at every step
{
while(i < t){
#pragma omp master
{
/* do other calculations on single core, output etc. */
Tree.PreProcessing()
/* PreProcessing can drastically change data for certain conditions, but only at 3 or 4 of the 1e10 time steps */
Tree.Output()
}
#pragma omp barrier
#pragma omp for schedule(static) nowait
for(int k=0; k < size; k++){
/* do O(N^2) calc that requires position of all other leaves */
Tree.CalculateInteraction(Branch[k])
}
/* return to single core to finish time step */
#pragma omp master
{
/* iterate forwards */
Tree.PropagatePositions()
i++
}
#pragma omp barrier
}
很简单,CPU-hog 函数就是这样做的:
void Tree::CalculateInteraction(Leaf* A){
// for all branches B in tree{
// for all leaves Q in B{
if(condition between A and Q){skip}
else{
// find displacement D of A and Q
// find displacement L of A and "A-1"
// take cross product of the two displacements
// add the cross-product to the velocity of leaf A
for(int j(0); j!=3; j++){
A->Vel[j] += constant * (D_cross_L)[j];
}
我的问题是,这种性能下降是由于 openMP 线程管理开销占主导地位,还是数据层次结构的设计没有考虑并行性?
我应该注意到,并行的每个步骤都比串行的时间长得多,这不是一些初始化开销问题;这两个版本已经针对需要 1 小时和 10 小时的计算进行了测试,并最终希望应用于可能需要 30 小时的串行计算(为此即使速度提高 2 倍也会非常有益)。此外,可能值得一提的是,我将 g++ 5.2.0 与 -fopenmp -march=native -m64 -mfpmath=sse -Ofast -funroll-loops 一起使用。
我是 OpenMP 的新手,所以非常感谢任何提示,如果有任何需要澄清的地方,请告诉我。
最佳答案
感谢您提供原始来源的链接!我已经能够在两个平台上编译并获得一些统计数据:带有 icpc 15.0 和 g++ 4.9.0 的 Xeon E5-2670;在 Core i7-4770 上,使用 g++ 4.8.4。
在 Xeon 上,icpc 和 g++ 生成的代码都随线程数缩放。我运行了一个缩短的(3e-7 秒)模拟,该模拟源自分发版中的 run.in 文件:
Xeon E5-2670 / icpc 15.0
threads time ipc
---------------------
1 17.5 2.17
2 13.0 1.53
4 6.81 1.53
8 3.81 1.52
Xeon E5-2670 / g++ 4.9.0
threads time ipc
---------------------
1 13.2 1.75
2 9.38 1.28
4 5.09 1.27
8 3.07 1.25
在 Core i7 上,我确实看到了您使用 g++ 4.8.4 观察到的丑陋缩放行为:
Core i7-4770 / g++ 4.8.4
threads time ipc
---------------------
1 8.48 2.41
2 11.5 0.97
4 12.6 0.73
第一个观察结果是某些特定于平台的因素会影响缩放。
我查看了 point.h和 velnl.cpp文件,并注意到您正在使用 vector<double>用于存储 3-d vector 数据的变量,包括许多临时变量。这些都将访问堆,并且是潜在的争用源。 Intel 的 openmp 实现使用线程本地堆来避免堆争用,也许 g++ 4.9 也这样做,而 g++-4.8.4 没有?
我 fork 了项目(github 上的 halfflat/vfmcppar)并修改了这些文件以使用 std::array<double,3>对于这些 3-d vector ;这恢复了缩放比例,并且还提供了更快的运行时间:
Core i7-4770 / g++ 4.8.4
std::array implementation
threads time ipc
---------------------
1 1.40 1.54
2 0.84 1.35
4 0.60 1.11
我没有在适当长度的模拟上运行这些测试,因此由于设置和 i/o 开销,一些缩放很可能会丢失。
要点是任何共享资源都会阻碍可扩展性,包括堆。
关于c++ - OpenMP 代码远比串行慢 - 内存或线程开销瓶颈?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31947412/
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru
ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序
我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur