001/*
002 * JPPF.
003 * Copyright (C) 2005-2018 JPPF Team.
004 * http://www.jppf.org
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.jppf.load.balancer.impl;
019
020import java.util.Random;
021import java.util.concurrent.atomic.AtomicReference;
022
023import org.jppf.load.balancer.AbstractLoadBalancingProfile;
024import org.jppf.utils.TypedProperties;
025
026/**
027 * This class implements the basis of a profile based on simulated annealing
028 * jppf.load.balancing.profile. The possible move from the best known solution get smaller each
029 * time it make a move.
030 * This strategy let the algorithm explore the universe of bundle size with
031 * an almost known end. Check method getDecreaseRatio about the maximum number
032 * of changes.
033 * 
034 * @author Domingos Creado
035 */
036public class AnnealingTuneProfile extends AbstractLoadBalancingProfile {
037  /**
038   * Explicit serialVersionUID.
039   */
040  private static final long serialVersionUID = 1L;
041  /**
042   * A default profile with default parameter values.
043   */
044  private static AtomicReference<AnnealingTuneProfile> defaultProfile = new AtomicReference<>(new AnnealingTuneProfile());
045  /**
046   * The initial bundle size to start from.
047   */
048  protected int size = 5;
049  /**
050   * The minimum number of samples that must be collected before an analysis is triggered.
051   */
052  protected long minSamplesToAnalyse = 500L;
053  /**
054   * The minimum number of samples to be collected before checking if the performance profile has changed.
055   */
056  protected long minSamplesToCheckConvergence = 300L;
057  /**
058   * The percentage of deviation of the current mean to the mean
059   * when the system was considered stable.
060   */
061  protected double maxDeviation = 0.2d;
062  /**
063   * The maximum number of guesses of number generated that were already tested
064   * for the algorithm to consider the current best solution stable.
065   */
066  protected int maxGuessToStable = 10;
067  /**
068   * This parameter defines the multiplicity used to define the range available to
069   * random generator, as the maximum.
070   */
071  protected float sizeRatioDeviation = 1.5f;
072  /**
073   * This parameter defines how fast does it will stop generating random numbers.
074   * This is essential to define what is the size of the universe will be explored.
075   * Greater numbers make the algorithm stop sooner.
076   * Just as example, if the best solution is between 0-100, the following might
077   * occur:
078   * <ul style="list-style-type: none; text-indent: -20px">
079   * <li>1 => 5 max guesses</li>
080   * <li>2 => 2 max guesses</li>
081   * <li>0.5 => 9 max guesses</li>
082   * <li>0.1 => 46 max guesses</li>
083   * <li>0.05 => 96 max guesses</li>
084   * </ul>
085   * This expected number of guesses might not occur if the number of getMaxGuessToStable()
086   * is short.
087   */
088  protected float decreaseRatio = 0.2f;
089
090  /**
091   * Initialize this profile with default values.
092   */
093  public AnnealingTuneProfile() {
094  }
095
096  /**
097   * Initialize this profile with values read from the configuration file.
098   * @param config contains a mapping of the profile parameters to their value.
099   */
100  public AnnealingTuneProfile(final TypedProperties config) {
101    size = config.getInt("size", 5);
102    minSamplesToAnalyse = config.getInt("minSamplesToAnalyse", 500);
103    minSamplesToCheckConvergence = config.getInt("minSamplesToCheckConvergence", 300);
104    maxDeviation = config.getDouble("maxDeviation", 0.2d);
105    maxGuessToStable = config.getInt("maxGuessToStable", 10);
106    sizeRatioDeviation = config.getFloat("sizeRatioDeviation", 1.5f);
107    decreaseRatio = config.getFloat("decreaseRatio", 0.2f);
108    
109  }
110
111  /**
112   * Get the multiplicity used to define the range available to
113   * random generator, as the maximum.
114   * @return the multiplicity as a float value.
115   */
116  public float getSizeRatioDeviation() {
117    return sizeRatioDeviation;
118  }
119
120  /**
121   * Set the multiplicity used to define the range available to
122   * random generator, as the maximum.
123   * @param sizeRatioDeviation the multiplicity as a float value.
124   */
125  public void setSizeRatioDeviation(final float sizeRatioDeviation) {
126    this.sizeRatioDeviation = sizeRatioDeviation;
127  }
128
129  /**
130   * Get the decrease rate for this profile.
131   * @return the decrease rate as a float value.
132   */
133  public float getDecreaseRatio() {
134    return decreaseRatio;
135  }
136
137  /**
138   * Set the decrease rate for this profile.
139   * @param decreaseRatio the decrease rate as a float value.
140   */
141  public void setDecreaseRatio(final float decreaseRatio) {
142    this.decreaseRatio = decreaseRatio;
143  }
144
145  /**
146   * Generate a difference to be applied to the best known bundle size.
147   * @param bestSize the known best size of bundle.
148   * @param collectedSamples the number of samples that were already collected.
149   * @param rnd a pseudo-random number generator.
150   * @return an always positive diff to be applied to bundle size
151   */
152  public int createDiff(final int bestSize, final int collectedSamples, final Random rnd) {
153    final double max = Math.max(Math.round(bestSize * (getSizeRatioDeviation() - 1.0f)), 1);
154    if (max < 1.0d) return 1;
155    return rnd.nextInt((int) max) + 1;
156  }
157
158  /**
159   * This method implements the always decreasing policy of the algorithm.
160   * The ratio define how fast this instance will stop generating random
161   * numbers.
162   * The calculation is performed as max * exp(-x * getDecreaseRatio()).
163   * 
164   * @param max the maximum value this algorithm will generate.
165   * @param x a randomly generated bundle size increment.
166   * @return an int value.
167   */
168  protected double expDist(final long max, final long x) {
169    //return max * Math.exp(-x * getDecreaseRatio());
170    return (double) max / (double) (x * decreaseRatio);
171  }
172
173  /**
174   * Get the minimum number of samples that must be collected before an analysis is triggered.
175   * @return the number of samples as a long value.
176   */
177  public long getMinSamplesToAnalyse() {
178    return minSamplesToAnalyse;
179  }
180
181  /**
182   * Set the minimum number of samples that must be collected before an analysis is triggered.
183   * @param minSamplesToAnalyse the number of samples as a long value.
184   */
185  public void setMinSamplesToAnalyse(final long minSamplesToAnalyse) {
186    this.minSamplesToAnalyse = minSamplesToAnalyse;
187  }
188
189  /**
190   * Get the the minimum number of samples to be collected before
191   * checking if the performance profile has changed.
192   * @return the number of samples as a long value.
193   */
194  public long getMinSamplesToCheckConvergence() {
195    return minSamplesToCheckConvergence;
196  }
197
198  /**
199   * Set the the minimum number of samples to be collected before
200   * checking if the performance profile has changed.
201   * @param minSamplesToCheckConvergence the number of samples as a long value.
202   */
203  public void setMinSamplesToCheckConvergence(final long minSamplesToCheckConvergence) {
204    this.minSamplesToCheckConvergence = minSamplesToCheckConvergence;
205  }
206
207  /**
208   * Get the percentage of deviation of the current mean to the mean
209   * when the system was considered stable.
210   * @return the percentage of deviation as a double value.
211   */
212  public double getMaxDeviation() {
213    return maxDeviation;
214  }
215
216  /**
217   * Set the percentage of deviation of the current mean to the mean
218   * when the system was considered stable.
219   * @param maxDeviation the percentage of deviation as a double value.
220   */
221  public void setMaxDeviation(final double maxDeviation) {
222    this.maxDeviation = maxDeviation;
223  }
224
225  /**
226   * Get the maximum number of guesses of number generated that were already tested
227   * for the algorithm to consider the current best solution stable.
228   * @return the number of guesses as an int value.
229   */
230  public int getMaxGuessToStable() {
231    return maxGuessToStable;
232  }
233
234  /**
235   * Set the maximum number of guesses of number generated that were already tested
236   * for the algorithm to consider the current best solution stable.
237   * @param maxGuessToStable the number of guesses as an int value.
238   */
239  public void setMaxGuessToStable(final int maxGuessToStable) {
240    this.maxGuessToStable = maxGuessToStable;
241  }
242
243  /**
244   * Get the default profile with default parameter values.
245   * @return a <code>AnnealingTuneProfile</code> singleton instance.
246   */
247  public static AnnealingTuneProfile getDefaultProfile() {
248    return defaultProfile.get();
249  }
250}