Operator= 减慢模拟速度

Operator= slowing down simulation

我正在运行宁Monte Carlo聚合物模拟。系统当前状态的整个配置由名为 Grid 的对象给出。这是我对 Grid:

的定义
class Grid{

public:
    std::vector <Polymer> PolymersInGrid;                    // all the polymers in the grid 
    int x;                                                   // length of x-edge of grid 
    int y;                                                   // length of y-edge of grid 
    int z;                                                   // length of z-edge of grid 
    double kT;                                               // energy factor 
    double Emm_n ;                                           // monomer-solvent when Not aligned 
    double Emm_a ;                                           // monomer-solvent when Aligned
    double Ems;                                              // monomer-solvent interaction
    double Energy;                                           // energy of grid 
    std::map <std::vector <int>, Particle> OccupancyMap;     // a map that gives the particle given the location
    

    Grid(int xlen, int ylen, int zlen, double kT_, double Emm_a_, double Emm_n_, double Ems_): x (xlen), y (ylen), z (zlen), kT (kT_), Emm_n(Emm_n_), Emm_a (Emm_a_), Ems (Ems_) {        // Constructor of class
        // this->instantiateOccupancyMap(); 
    };


    // Destructor of class 
    ~Grid(){                                    

    }; 

    // assignment operator that allows for a correct transfer of properties. Important to functioning of program. 
    Grid& operator=(Grid other){
        std::swap(PolymersInGrid, other.PolymersInGrid); 
        std::swap(Energy, other.Energy); 
        std::swap(OccupancyMap, other.OccupancyMap);
        return *this; 
    } 
.
.
.
}

如果需要,我可以详细介绍对象 PolymerParticle

在我的驱动程序代码中,这就是我要做的: 定义最大迭代次数。

  1. 定义完整的网格G
  2. 正在创建名为 G_G 副本。
  3. 我正在扰乱 G_ 的配置。
  4. 如果根据 Metropolis 标准接受 G_ 上的扰动,我将 G_ 分配给 G (G=G_)。
  5. 重复步骤 1-4 直到达到最大迭代次数。

这是我的驱动代码:

auto start = std::chrono::high_resolution_clock::now(); 
Grid G_ (G); 
    int acceptance_count = 0; 
    for (int i{1}; i< (Nmov+1); i++){

        // choose a move 
        G_ = MoveChooser(G, v);  

        if ( MetropolisAcceptance (G.Energy, G_.Energy, G.kT) ) {
            // accepted
            // replace old config with new config

            acceptance_count++; 
            std::cout << "Number of acceptances is " << acceptance_count << std::endl;
            G = G_;
        }


        else {
            // continue;
        }

        if (i % dfreq == 0){
            G.dumpPositionsOfPolymers (i, dfile) ;
            G.dumpEnergyOfGrid(i, efile, call) ; 
        }
        // G.PolymersInGrid.at(0).printChainCoords();


    }
    
    
    
    auto stop = std::chrono::high_resolution_clock::now(); 
    
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds> (stop-start); 

    std::cout << "\n\nTime taken for simulation: " << duration.count() << " milliseconds" << std::endl;

这是有趣的部分:如果我 运行 使用没有很多“接受”(低温、不良溶剂)的条件进行模拟,则模拟 运行 非常快。然而,如果有大量的接受,模拟就会变得非常慢。 我的假设是我的赋值运算符 = 正在减慢我的模拟速度。 我 运行 一些测试:

接受数 = 25365,挂钟时间 = 717770 毫秒 (!)

接受次数 = 2165,挂钟时间 = 64412 毫秒

接受次数 = 3000,挂钟时间 = 75550 毫秒

而且这个趋势还在继续。 谁能建议我如何提高效率?有没有办法绕过我正在经历的减速,我认为,由于 = 运算符?

如果您能给我任何建议,我将不胜感激!

你当然可以做的一件事来提高性能是强制移动 _G 而不是将其应对到 G:

 G = std::move(G_);

毕竟,在这个阶段你不再需要G_了。

旁注。您不需要复制 operator= 中的所有成员数据这一事实表明您对 Grid 的设计远非完美,但是,如果程序很小并且您确定,请保留它你控制一切。无论如何,而不是使用 operator=,您应该定义和使用具有有意义名称的成员函数,例如“fast_and_dirty_swap”等 :-) 然后您可以按照@建议的方式定义 operator= Jarod42,即使用= default.


我在 C++11 之前使用的另一种方法是对指针进行操作。在这种情况下,一个人会有两个 Grids,一个是“真实的”,一个被视为缓冲区或沙箱,并且在接受时将简单地交换指针,以便“缓冲区”充满 MoveChooser 将成为真实的当前 Grid.

一个伪代码:

  • 创建两个缓冲区,previouscurrent,每个缓冲区都能够存储模拟状态
  • 初始化current
  • 创建两个指针,p_prev = &previousp_curr = &currenrt
  • 步数不限
    • 根据*p_curr计算下一个状态并将其存储在*p_prev中(例如monte_carlo_step(p_curr, p_prev)
    • 交换指针:现在当前系统状态为 p_curr,之前为 p_prev
  • 分析存储在*p_curr
  • 的结果