2222import com .cloud .host .Host ;
2323import com .cloud .offering .ServiceOffering ;
2424import com .cloud .org .Cluster ;
25- import com .cloud .utils .Pair ;
2625import com .cloud .utils .Ternary ;
2726import com .cloud .utils .component .Adapter ;
2827import com .cloud .vm .VirtualMachine ;
28+ import org .apache .commons .collections .CollectionUtils ;
2929import org .apache .commons .math3 .stat .descriptive .moment .Mean ;
3030import org .apache .commons .math3 .stat .descriptive .moment .StandardDeviation ;
3131
4040
4141public interface ClusterDrsAlgorithm extends Adapter {
4242
43+ Mean MEAN_CALCULATOR = new Mean ();
44+ StandardDeviation STDDEV_CALCULATOR = new StandardDeviation (false );
45+
4346 /**
4447 * Determines whether a DRS operation is needed for a given cluster and host-VM
4548 * mapping.
@@ -59,79 +62,121 @@ public interface ClusterDrsAlgorithm extends Adapter {
5962 boolean needsDrs (Cluster cluster , List <Ternary <Long , Long , Long >> cpuList ,
6063 List <Ternary <Long , Long , Long >> memoryList ) throws ConfigurationException ;
6164
62-
6365 /**
64- * Determines the metrics for a given virtual machine and destination host in a DRS cluster.
65- *
66- * @param clusterId
67- * the ID of the cluster to check
68- * @param vm
69- * the virtual machine to check
70- * @param serviceOffering
71- * the service offering for the virtual machine
72- * @param destHost
73- * the destination host for the virtual machine
74- * @param hostCpuMap
75- * a map of host IDs to the Ternary of used, reserved and total CPU on each host
76- * @param hostMemoryMap
77- * a map of host IDs to the Ternary of used, reserved and total memory on each host
78- * @param requiresStorageMotion
79- * whether storage motion is required for the virtual machine
66+ * Calculates the metrics (improvement, cost, benefit) for migrating a VM to a destination host. Improvement is
67+ * calculated based on the change in cluster imbalance before and after the migration.
8068 *
69+ * @param cluster the cluster to check
70+ * @param vm the virtual machine to check
71+ * @param serviceOffering the service offering for the virtual machine
72+ * @param destHost the destination host for the virtual machine
73+ * @param hostCpuMap a map of host IDs to the Ternary of used, reserved and total CPU on each host
74+ * @param hostMemoryMap a map of host IDs to the Ternary of used, reserved and total memory on each host
75+ * @param requiresStorageMotion whether storage motion is required for the virtual machine
76+ * @param preImbalance the pre-calculated cluster imbalance before migration (null to calculate it)
77+ * @param baseMetricsArray pre-calculated array of all host metrics before migration
78+ * @param hostIdToIndexMap mapping from host ID to index in the metrics array
8179 * @return a ternary containing improvement, cost, benefit
8280 */
8381 Ternary <Double , Double , Double > getMetrics (Cluster cluster , VirtualMachine vm , ServiceOffering serviceOffering ,
8482 Host destHost , Map <Long , Ternary <Long , Long , Long >> hostCpuMap ,
8583 Map <Long , Ternary <Long , Long , Long >> hostMemoryMap ,
86- Boolean requiresStorageMotion ) throws ConfigurationException ;
84+ Boolean requiresStorageMotion , Double preImbalance ,
85+ double [] baseMetricsArray , Map <Long , Integer > hostIdToIndexMap ) throws ConfigurationException ;
8786
8887 /**
89- * Calculates the imbalance of the cluster after a virtual machine migration .
88+ * Calculates the cluster imbalance after migrating a VM to a destination host .
9089 *
91- * @param serviceOffering
92- * the service offering for the virtual machine
93- * @param vm
94- * the virtual machine being migrated
95- * @param destHost
96- * the destination host for the virtual machine
97- * @param hostCpuMap
98- * a map of host IDs to the Ternary of used, reserved and total CPU on each host
99- * @param hostMemoryMap
100- * a map of host IDs to the Ternary of used, reserved and total memory on each host
90+ * @param vm the virtual machine being migrated
91+ * @param destHost the destination host for the virtual machine
92+ * @param clusterId the cluster ID
93+ * @param vmMetric the VM's resource consumption metric
94+ * @param baseMetricsArray pre-calculated array of all host metrics before migration
95+ * @param hostIdToIndexMap mapping from host ID to index in the metrics array
96+ * @return the cluster imbalance after migration
97+ */
98+ default Double getImbalancePostMigration (VirtualMachine vm ,
99+ Host destHost , Long clusterId , long vmMetric , double [] baseMetricsArray ,
100+ Map <Long , Integer > hostIdToIndexMap , Map <Long , Ternary <Long , Long , Long >> hostCpuMap ,
101+ Map <Long , Ternary <Long , Long , Long >> hostMemoryMap ) {
102+ // Create a copy of the base array and adjust only the two affected hosts
103+ double [] adjustedMetrics = new double [baseMetricsArray .length ];
104+ System .arraycopy (baseMetricsArray , 0 , adjustedMetrics , 0 , baseMetricsArray .length );
105+
106+ long destHostId = destHost .getId ();
107+ long vmHostId = vm .getHostId ();
108+
109+ // Adjust source host (remove VM resources)
110+ Integer sourceIndex = hostIdToIndexMap .get (vmHostId );
111+ if (sourceIndex != null && sourceIndex < adjustedMetrics .length ) {
112+ Map <Long , Ternary <Long , Long , Long >> sourceMetricsMap = getClusterDrsMetric (clusterId ).equals ("cpu" ) ? hostCpuMap : hostMemoryMap ;
113+ Ternary <Long , Long , Long > sourceMetrics = sourceMetricsMap .get (vmHostId );
114+ if (sourceMetrics != null ) {
115+ adjustedMetrics [sourceIndex ] = getMetricValuePostMigration (clusterId , sourceMetrics , vmMetric , vmHostId , destHostId , vmHostId );
116+ }
117+ }
118+
119+ // Adjust destination host (add VM resources)
120+ Integer destIndex = hostIdToIndexMap .get (destHostId );
121+ if (destIndex != null && destIndex < adjustedMetrics .length ) {
122+ Map <Long , Ternary <Long , Long , Long >> destMetricsMap = getClusterDrsMetric (clusterId ).equals ("cpu" ) ? hostCpuMap : hostMemoryMap ;
123+ Ternary <Long , Long , Long > destMetrics = destMetricsMap .get (destHostId );
124+ if (destMetrics != null ) {
125+ adjustedMetrics [destIndex ] = getMetricValuePostMigration (clusterId , destMetrics , vmMetric , destHostId , destHostId , vmHostId );
126+ }
127+ }
128+
129+ return calculateImbalance (adjustedMetrics );
130+ }
131+
132+ /**
133+ * Calculate imbalance from an array of metric values.
134+ * Imbalance is defined as standard deviation divided by mean.
101135 *
102- * @return a pair containing the CPU and memory imbalance of the cluster after the migration
136+ * Uses reusable stateless calculator objects to avoid object creation overhead.
137+ * @param values array of metric values
138+ * @return calculated imbalance
103139 */
104- default Double getImbalancePostMigration (ServiceOffering serviceOffering , VirtualMachine vm ,
105- Host destHost , Map <Long , Ternary <Long , Long , Long >> hostCpuMap ,
106- Map <Long , Ternary <Long , Long , Long >> hostMemoryMap ) throws ConfigurationException {
107- Pair <Long , Map <Long , Ternary <Long , Long , Long >>> pair = getHostMetricsMapAndType (destHost .getClusterId (), serviceOffering , hostCpuMap , hostMemoryMap );
108- long vmMetric = pair .first ();
109- Map <Long , Ternary <Long , Long , Long >> hostMetricsMap = pair .second ();
140+ private static double calculateImbalance (double [] values ) {
141+ if (values == null || values .length == 0 ) {
142+ return 0.0 ;
143+ }
110144
111- List < Double > list = new ArrayList <>( );
112- for ( Long hostId : hostMetricsMap . keySet () ) {
113- list . add ( getMetricValuePostMigration ( destHost . getClusterId (), hostMetricsMap . get ( hostId ), vmMetric , hostId , destHost . getId (), vm . getHostId ()));
145+ double mean = MEAN_CALCULATOR . evaluate ( values );
146+ if ( mean == 0.0 ) {
147+ return 0.0 ; // Avoid division by zero
114148 }
115- return getImbalance (list );
149+ double stdDev = STDDEV_CALCULATOR .evaluate (values , mean );
150+ return stdDev / mean ;
116151 }
117152
118- private Pair <Long , Map <Long , Ternary <Long , Long , Long >>> getHostMetricsMapAndType (Long clusterId ,
119- ServiceOffering serviceOffering , Map <Long , Ternary <Long , Long , Long >> hostCpuMap ,
120- Map <Long , Ternary <Long , Long , Long >> hostMemoryMap ) throws ConfigurationException {
153+ /**
154+ * Helper method to get VM metric based on cluster configuration.
155+ */
156+ static long getVmMetric (ServiceOffering serviceOffering , Long clusterId ) throws ConfigurationException {
121157 String metric = getClusterDrsMetric (clusterId );
122- Pair <Long , Map <Long , Ternary <Long , Long , Long >>> pair ;
123158 switch (metric ) {
124159 case "cpu" :
125- pair = new Pair <>((long ) serviceOffering .getCpu () * serviceOffering .getSpeed (), hostCpuMap );
126- break ;
160+ return (long ) serviceOffering .getCpu () * serviceOffering .getSpeed ();
127161 case "memory" :
128- pair = new Pair <>(serviceOffering .getRamSize () * 1024L * 1024L , hostMemoryMap );
129- break ;
162+ return serviceOffering .getRamSize () * 1024L * 1024L ;
130163 default :
131164 throw new ConfigurationException (
132165 String .format ("Invalid metric: %s for cluster: %d" , metric , clusterId ));
133166 }
134- return pair ;
167+ }
168+
169+ /**
170+ * Helper method to calculate metrics from pre and post imbalance values.
171+ */
172+ default Ternary <Double , Double , Double > calculateMetricsFromImbalances (Double preImbalance , Double postImbalance ) {
173+ // This needs more research to determine the cost and benefit of a migration
174+ // TODO: Cost should be a factor of the VM size and the host capacity
175+ // TODO: Benefit should be a factor of the VM size and the host capacity and the number of VMs on the host
176+ final double improvement = preImbalance - postImbalance ;
177+ final double cost = 0.0 ;
178+ final double benefit = 1.0 ;
179+ return new Ternary <>(improvement , cost , benefit );
135180 }
136181
137182 private Double getMetricValuePostMigration (Long clusterId , Ternary <Long , Long , Long > metrics , long vmMetric ,
@@ -151,9 +196,26 @@ private Double getMetricValuePostMigration(Long clusterId, Ternary<Long, Long, L
151196 }
152197
153198 private static Double getImbalance (List <Double > metricList ) {
154- Double clusterMeanMetric = getClusterMeanMetric (metricList );
155- Double clusterStandardDeviation = getClusterStandardDeviation (metricList , clusterMeanMetric );
156- return clusterStandardDeviation / clusterMeanMetric ;
199+ if (CollectionUtils .isEmpty (metricList )) {
200+ return 0.0 ;
201+ }
202+ // Convert List<Double> to double[] once, avoiding repeated conversions
203+ double [] values = new double [metricList .size ()];
204+ int index = 0 ;
205+ for (Double value : metricList ) {
206+ if (value != null ) {
207+ values [index ++] = value ;
208+ }
209+ }
210+
211+ // Trim array if some values were null
212+ if (index < values .length ) {
213+ double [] trimmed = new double [index ];
214+ System .arraycopy (values , 0 , trimmed , 0 , index );
215+ values = trimmed ;
216+ }
217+
218+ return calculateImbalance (values );
157219 }
158220
159221 static String getClusterDrsMetric (long clusterId ) {
@@ -181,36 +243,6 @@ static Double getMetricValue(long clusterId, long used, long free, long total, F
181243 return null ;
182244 }
183245
184- /**
185- * Mean is the average of a collection or set of metrics. In context of a DRS
186- * cluster, the cluster metrics defined as the average metrics value for some
187- * metric (such as CPU, memory etc.) for every resource such as host.
188- * Cluster Mean Metric, mavg = (∑mi) / N, where mi is a measurable metric for a
189- * resource ‘i’ in a cluster with total N number of resources.
190- */
191- static Double getClusterMeanMetric (List <Double > metricList ) {
192- return new Mean ().evaluate (metricList .stream ().mapToDouble (i -> i ).toArray ());
193- }
194-
195- /**
196- * Standard deviation is defined as the square root of the absolute squared sum
197- * of difference of a metric from its mean for every resource divided by the
198- * total number of resources. In context of the DRS, the cluster standard
199- * deviation is the standard deviation based on a metric of resources in a
200- * cluster such as for the allocation or utilisation CPU/memory metric of hosts
201- * in a cluster.
202- * Cluster Standard Deviation, σc = sqrt((∑∣mi−mavg∣^2) / N), where mavg is the
203- * mean metric value and mi is a measurable metric for some resource ‘i’ in the
204- * cluster with total N number of resources.
205- */
206- static Double getClusterStandardDeviation (List <Double > metricList , Double mean ) {
207- if (mean != null ) {
208- return new StandardDeviation (false ).evaluate (metricList .stream ().mapToDouble (i -> i ).toArray (), mean );
209- } else {
210- return new StandardDeviation (false ).evaluate (metricList .stream ().mapToDouble (i -> i ).toArray ());
211- }
212- }
213-
214246 static boolean getDrsMetricUseRatio (long clusterId ) {
215247 return ClusterDrsMetricUseRatio .valueIn (clusterId );
216248 }
0 commit comments