View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2024.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.potential.nonbonded;
39  
40  import edu.rit.pj.IntegerSchedule;
41  import edu.rit.util.Range;
42  
43  /**
44   * A fixed schedule that load balances work chunks across threads.
45   *
46   * @author Michael J. Schnieders
47   * @since 1.0
48   */
49  public class SpatialDensitySchedule extends IntegerSchedule {
50  
51    private final int[] atomsPerChunk;
52    private final double loadBalancePercentage;
53    private int nThreads;
54    private Range chunkRange;
55    private boolean[] threadDone;
56    private Range[] ranges;
57  
58    /**
59     * Constructor for SpatialDensitySchedule.
60     *
61     * @param nThreads a int.
62     * @param nAtoms a int.
63     * @param atomsPerChunk an array of int.
64     * @param loadBalancePercentage a double.
65     */
66    SpatialDensitySchedule(
67        int nThreads, int nAtoms, int[] atomsPerChunk, double loadBalancePercentage) {
68      this.atomsPerChunk = atomsPerChunk;
69      this.nThreads = nThreads;
70  
71      threadDone = new boolean[nThreads];
72      ranges = new Range[nThreads];
73  
74      if (loadBalancePercentage > 0.01 && loadBalancePercentage <= 1.0) {
75        this.loadBalancePercentage = loadBalancePercentage;
76      } else {
77        this.loadBalancePercentage = 1.0;
78      }
79    }
80  
81    /**
82     * {@inheritDoc}
83     *
84     * <p>This is a fixed schedule.
85     */
86    @Override
87    public boolean isFixedSchedule() {
88      return true;
89    }
90  
91    /** {@inheritDoc} */
92    @Override
93    public Range next(int threadID) {
94      if (!threadDone[threadID]) {
95        threadDone[threadID] = true;
96        return ranges[threadID];
97      }
98      return null;
99    }
100 
101   /** {@inheritDoc} */
102   @Override
103   public void start(int nThreads, Range chunkRange) {
104     this.nThreads = nThreads;
105     this.chunkRange = chunkRange;
106     if (nThreads != threadDone.length) {
107       threadDone = new boolean[nThreads];
108     }
109     for (int i = 0; i < nThreads; i++) {
110       threadDone[i] = false;
111     }
112     if (nThreads != ranges.length) {
113       ranges = new Range[nThreads];
114     }
115 
116     defineRanges();
117   }
118 
119   private void defineRanges() {
120     int lb = chunkRange.lb();
121     int ub = chunkRange.ub();
122     int thread = 0;
123     int start = 0;
124     int total = 0;
125 
126     // Calculate the total number of atoms that will be place on the grid.
127     for (int value : atomsPerChunk) {
128       total += value;
129     }
130     int goal = (int) ((total * loadBalancePercentage) / nThreads);
131 
132     total = 0;
133     for (int i = lb; i <= ub; i++) {
134       int chunksLeft = ub - i + 1;
135       int threadsLeft = nThreads - thread;
136       // Count the number of atoms in each work chunk.
137       total += atomsPerChunk[i];
138       // Check if the load balancing goal has been reached.
139       if (total > goal || chunksLeft <= threadsLeft) {
140         int stop = i;
141         // Define the range for this thread.
142         Range current = ranges[thread];
143         if (current == null || current.lb() != start || current.ub() != stop) {
144           ranges[thread] = new Range(start, stop);
145         }
146 
147         // Initialization for the next thread.
148         thread++;
149         start = i + 1;
150         total = 0;
151         // The last thread gets the rest of the work chunks.
152         if (thread == nThreads - 1) {
153           stop = ub;
154           current = ranges[thread];
155           if (current == null || current.lb() != start || current.ub() != stop) {
156             ranges[thread] = new Range(start, stop);
157           }
158           break;
159         }
160       } else if (i == ub) {
161         // The final range may not meet the goal.
162         int stop = i;
163         Range current = ranges[thread];
164         if (current == null || current.lb() != start || current.ub() != stop) {
165           ranges[thread] = new Range(start, stop);
166         }
167       }
168     }
169 
170     // No work for remaining threads.
171     for (int i = thread + 1; i < nThreads; i++) {
172       ranges[i] = null;
173     }
174   }
175 }