Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/145.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ OpenMP和OOP(分子动力学模拟)_C++_Parallel Processing_Openmp - Fatal编程技术网

C++ OpenMP和OOP(分子动力学模拟)

C++ OpenMP和OOP(分子动力学模拟),c++,parallel-processing,openmp,C++,Parallel Processing,Openmp,我正在进行一个分子动力学模拟,我一直在努力并行实现它,虽然我成功地完全加载了我的4线程处理器,但并行模式下的计算时间比串行模式下的计算时间要长 在研究每个线程开始和完成循环迭代的时间点时,我注意到了一种模式:好像不同的线程在等待彼此。 就在那时,我把注意力转向了我的课程结构。我有一个类,它的一个实例代表我的粒子系统,包含关于粒子的所有信息和一些使用这些信息的函数。我还有一个代表原子间势的类实例,包含势函数的参数和一些函数(其中一个函数计算两个给定粒子之间的力) 所以在我的程序中存在两个不同类的实

我正在进行一个分子动力学模拟,我一直在努力并行实现它,虽然我成功地完全加载了我的4线程处理器,但并行模式下的计算时间比串行模式下的计算时间要长

在研究每个线程开始和完成循环迭代的时间点时,我注意到了一种模式:好像不同的线程在等待彼此。 就在那时,我把注意力转向了我的课程结构。我有一个类,它的一个实例代表我的粒子系统,包含关于粒子的所有信息和一些使用这些信息的函数。我还有一个代表原子间势的类实例,包含势函数的参数和一些函数(其中一个函数计算两个给定粒子之间的力)

所以在我的程序中存在两个不同类的实例,它们相互作用:一个类的一些函数引用另一个类的实例。 我试图并行实现的块如下所示:

      void Run_simulation(Class_system &system, Class_potential &potential, some other arguments){
          #pragma omp parallel for
              for(…) 
      }
对于(…)是实际计算,使用来自
Class\u系统
类的
系统
实例的数据和来自
Class\u潜力
类的
潜力
实例的一些函数

我是不是说这个结构是我麻烦的根源


你能告诉我在这种情况下该怎么办吗?我必须以完全不同的方式重写我的程序吗?我应该使用一些不同的工具来并行实现我的程序吗?

没有关于您的模拟类型的更多详细信息,我只能推测,以下是我的推测

您研究过负载平衡问题吗?我猜循环会将粒子分布在线程之间,但如果有某种限制范围的势,则计算时间可能会因模拟体积不同区域中的粒子而异,具体取决于空间密度。这是分子动力学中一个非常常见的问题,在分布式内存(大多数情况下是MPI)代码中很难正确解决。幸运的是,使用OpenMP,您可以直接访问每个计算元素上的所有粒子,因此负载平衡更容易实现。它不仅更简单,而且是内置的,可以说-只需使用
schedule(dynamic,chunk)
子句更改
for
指令的调度,其中
chunk
是一个小数字,其最佳值可能因模拟而异。您可以将
chunk
作为程序输入数据的一部分,或者编写
schedule(runtime)
,然后通过将
OMP_schedule
环境变量设置为
“static”
“dynamic,1”
“dynamic,10”
“guided”等值来使用不同的调度类

性能下降的另一个可能原因是错误共享和真实共享。如果数据结构不适合并发修改,则会发生错误共享。例如,如果为每个粒子保留3D位置和速度信息(假设使用velocity Verlet integrator),给定IEEE 754双精度,每个坐标/速度三元组需要24个字节。这意味着64字节的单个缓存线可容纳2个完整的三元组和另一个三元组的2/3。这样做的结果是,无论您如何在线程之间分配粒子,总会有至少两个线程必须共享一条缓存线。假设这些线程运行在不同的物理内核上。如果一个线程写入其缓存线副本(例如,它更新粒子的位置),则将涉及缓存一致性协议,它将使另一个线程中的缓存线无效,而另一个线程则必须从较慢的缓存(甚至从主内存)重新读取缓存线。当第二个线程更新其粒子时,这将使第一个核心中的缓存线无效。这个问题的解决方案是适当的填充和块大小选择,这样就不会有两个线程共享一条缓存线。例如,如果添加表面第4维(可以使用它将粒子的势能存储在位置向量的第4元素中,将动能存储在速度向量的第4元素中)然后,每个位置/速度四极体将占用32个字节,并且正好两个粒子的信息将适合单个缓存线。如果然后在每个线程中分布偶数个粒子,则会自动消除可能的错误共享

当线程同时访问相同的数据结构,并且结构的各个部分之间存在重叠,并由不同的线程修改时,就会发生真正的共享。在分子动力学模拟中,这种情况经常发生,因为我们想要利用牛顿第三定律,以便在处理成对相互作用势时将计算时间缩短为两倍。当一个线程计算作用在粒子上的力时,
i
,同时枚举其相邻的
j
,计算
j
施加在
i
上的力会自动为您提供
i
施加在
j
上的力,以便将贡献添加到
j
上的总力中。但是
j
可能属于另一个线程,该线程可能会同时对其进行修改,因此原子操作必须用于两次更新(如果另一个线程的一个或多个粒子相邻,则这两次更新都可能会更新
i
)。x86上的原子更新是通过锁定指令实现的。这并不像通常所说的那么慢,但仍然比常规更新慢。它还包括相同的缓存线无效