// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// HillClimbing.h
//
// Defines classes for the HillClimbing concurrency-optimization algorithm.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#pragma once
namespace Concurrency
{
namespace details
{
///
/// An enum representing all possible hill climbing transition moves.
///
enum HillClimbingStateTransition
{
Warmup,
ContinueInitializing,
CompletedInitialization,
DoClimbing,
ChangePoint,
ContinueLookingForClimb,
Undefined,
};
///
/// A class responsible for hill climbing.
///
class HillClimbing
{
public:
///
/// Creates a new instance of hill climbing.
///
///
/// Scheduler id.
///
///
/// Number that represents the maximum resources available on the machine.
///
///
/// The scheduler proxy that controls this hill climbing instance.
///
HillClimbing(unsigned int id, unsigned int numberOfCores, SchedulerProxy * pSchedulerProxy);
///
/// External call passing statistical information to hill climbing. Based on these
/// statistics, hill climbing will give a recommendation on the number of resources to be used.
///
///
/// The control setting used in this period of time.
///
///
/// The number of completed units or work in that period of time.
///
///
/// The number of incoming units or work in that period of time.
///
///
/// The total length of the work queue.
///
///
/// The recommended number of resources to be used.
///
unsigned int Update(unsigned int currentControlSetting, unsigned int completionRate, unsigned int arrivalRate, unsigned int queueLength);
private:
///
/// A class responsible for keeping hill climbing history measurements.
///
class MeasuredHistory
{
public:
///
/// Creates a new measurement history.
///
MeasuredHistory();
///
/// Adds a new history data point.
///
///
/// The value representing throughput in this invocation.
///
///
/// The value representing the total number of samples for this history, including invalid samples and samples for previous settings.
///
void Add(const double data, unsigned int totalSampleCount);
///
/// Clears the history values for this control setting.
///
///
/// The control setting to reset.
///
void Clear(unsigned int controlSetting);
///
/// Gets the count for this history measurement.
///
///
/// The count.
///
int Count();
///
/// Gets the count at the last data point for this history measurement.
///
///
/// The last data point count.
///
unsigned int LastDataPointCount();
///
/// Gets the control setting for this history measurement.
///
///
/// The control setting.
///
int ControlSetting();
///
/// Computes the mean for a given history.
///
///
/// The mean.
///
double Mean();
///
/// Computes the coefficient of variation for a given history.
///
///
/// The coefficient of variation.
///
double CoefficientOfVariation();
///
/// Computes the mean of coefficients of variation for a given history.
///
///
/// The mean of coefficients of variation.
///
double CoefficientOfVariationMean();
///
/// Computes the variance for a given history.
///
///
/// The variance.
///
double Variance();
///
/// Computes the mean of variances for a given history.
///
///
/// The mean of variances.
///
double VarianceMean();
///
/// Computes the standard deviation for a given history.
///
///
/// The standard deviation.
///
double StandardDeviation();
///
/// Computes the mean of standard deviations for a given history.
///
///
/// The mean of standard deviations.
///
double StandardDeviationMean();
///
/// Tests if the difference between two measurement histories is statistically significant to
/// make a hill climbing decision.
///
///
/// A two sided test is used.
///
///
/// The value representing the second history.
///
///
/// The significance level in percent. Accepts 1 through 10.
///
///
/// The value representing the total number of samples for this history, including invalid samples and samples for previous settings.
///
///
/// -1 - second history is larger than this history
/// 0 - statistically identical
/// 1 - this history is larger than second history
///
int SignificanceTest(double value, const int significanceLevel, unsigned int totalSampleCount);
///
/// Tests if the difference between two measurement histories is statistically significant to
/// make a hill climbing decision.
///
///
/// A two sided test is used.
///
///
/// The pointer to second measurement history.
///
///
/// The significance level in percent. Accepts 1 through 10.
///
///
/// -1 - second history is larger than this history
/// 0 - statistically identical
/// 1 - this history is larger than second history
///
int SignificanceTest(MeasuredHistory * pMeasuredHistory, const int significanceLevel);
private:
// Running sum of throughputs
double m_sum;
// Sum of throughput squares
double m_sumOfSquares;
// Count of measurements in this history
int m_count;
// An integer representing the control setting for this history measurement
int m_controlSetting;
// Last count value when a measurement was taken (used for relevance test)
unsigned int m_lastDataPointCount;
};
///
/// Makes a pseudo-random hill climbing move by alternating between up and down.
///
///
/// The random move.
///
int GetRandomMove();
///
/// Recommends NewControlSetting to be used.
///
///
/// The control setting to be established.
///
///
/// New control setting to be used.
///
unsigned RecommendControlSetting(unsigned int newControlSetting);
///
/// Establishes control setting as current. This is the only method that updates the control settings.
///
///
/// The control setting to be established.
///
void EstablishControlSetting(unsigned int newControlSetting);
///
/// Determines whether a given history measurement is stable enough to make a hill climbing move.
///
///
/// True if history measurement is stable.
///
bool IsStableHistory(MeasuredHistory * pMeasuredHistory);
///
/// Calculates the throughput based on the input parameters.
///
///
/// The number of sample points in this measurement, including invalid ones.
///
///
/// The number of completed units or work in that period of time.
///
///
/// The number of incoming units or work in that period of time.
///
///
/// The total length of the work queue.
///
///
/// The calculated throughput.
///
double CalculateThroughput(unsigned int numberOfSamples, unsigned int completionRate, unsigned int arrivalRate, unsigned int queueLength);
///
/// Calculates the throughput gradient given two history measurements.
///
///
/// The control setting to move from.
///
///
/// The control setting to move to.
///
///
/// A value representing a gradient between two measurements.
///
double CalculateThroughputGradient(int fromSetting, int toSetting);
///
/// Flushes all measurement histories that are no longer relevant.
///
void FlushHistories();
///
/// Clears all measurement histories.
///
void ClearHistories();
///
/// Gets the history measurement for a given control setting.
///
///
/// The history measurement.
///
MeasuredHistory * GetHistory(unsigned int controlSetting);
// The maximum number of histories to keep
static const unsigned int MaxHistoryCount = 64;
// The array where history data is kept
MeasuredHistory m_histories[MaxHistoryCount];
// Scheduler proxy to which this hill climbing instance belongs
SchedulerProxy * m_pSchedulerProxy;
// Used to determine the magnitude of moves, in units of (coefficient of variation)/(thread count)
double m_controlGain;
// Maximum number of resources that can be changed in one transition
unsigned int m_maxControlSettingChange;
// The current amount of resources allocated in this hill climbing instance
unsigned int m_currentControlSetting;
// The amount of resources allocated in this hill climbing instance before the last move
unsigned int m_lastControlSetting;
// Scheduler id
unsigned int m_id;
// Number of samples collected
unsigned long m_sampleCount;
// Number of samples collected including invalid samples, across settings
unsigned long m_totalSampleCount;
// Number of consecutive invalid samples
unsigned long m_invalidCount;
// Save sum of completions for consecutive invalid samples
unsigned int m_saveCompleted;
// Save sum of arrivals for consecutive invalid samples
unsigned int m_saveIncoming;
// Determines where the next random move is going
bool m_nextRandomMoveIsUp;
#if defined(CONCRT_TRACING)
///
/// Logs the hill climbing decision by constructing a CSV dump of data.
///
///
/// The control setting to be established.
///
///
/// The transition that is recommended by hill climbing.
///
///
/// The number of sample points in this measurement, including invalid ones.
///
///
/// The number of completed units or work in that period of time.
///
///
/// The number of incoming units or work in that period of time.
///
///
/// The total length of the work queue.
///
///
/// The throughput of the given instance.
///
void LogData(unsigned int recommendedSetting, HillClimbingStateTransition transition, unsigned int numberOfSamples,
unsigned int completionRate, unsigned int arrivalRate, unsigned int queueLength, double throughput);
#endif
};
} // namespace details
} // namespace Concurrency