4 ## Copyright (c) 2010-2020 Intel Corporation
6 ## Licensed under the Apache License, Version 2.0 (the "License");
7 ## you may not use this file except in compliance with the License.
8 ## You may obtain a copy of the License at
11 ## http://www.apache.org/licenses/LICENSE-2.0
13 ## Unless required by applicable law or agreed to in writing, software
14 ## distributed under the License is distributed on an "AS IS" BASIS,
15 ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 ## See the License for the specific language governing permissions and
17 ## limitations under the License.
21 from past.utils import old_div
22 from rapid_log import RapidLog
23 from rapid_log import bcolors
25 class RapidTest(object):
27 Class to manage the flowsizetesting
30 def get_percentageof10Gbps(pps_speed,size):
31 # speed is given in pps, returning % of 10Gb/s
32 return (pps_speed / 1000000.0 * 0.08 * (size+28))
35 def get_pps(speed,size):
36 # speed is given in % of 10Gb/s, returning Mpps
37 return (speed * 100.0 / (8*(size+28)))
40 def get_speed(packet_speed,size):
41 # return speed in Gb/s
42 return (packet_speed / 1000.0 * (8*(size+28)))
45 def set_background_flows(background_machines, number_of_flows):
46 for machine in background_machines:
47 machine.set_flows(number_of_flows)
50 def set_background_speed(background_machines, speed):
51 for machine in background_machines:
52 machine.set_generator_speed(speed)
55 def set_background_size(background_machines, size):
56 for machine in background_machines:
57 machine.set_udp_packet_size(size)
60 def start_background_traffic(background_machines):
61 for machine in background_machines:
65 def stop_background_traffic(background_machines):
66 for machine in background_machines:
70 def report_result(flow_number, size, speed, pps_req_tx, pps_tx, pps_sut_tx,
71 pps_rx, lat_avg, lat_perc, lat_perc_max, lat_max, tx, rx, tot_drop,
72 elapsed_time,speed_prefix='', lat_avg_prefix='', lat_perc_prefix='',
73 lat_max_prefix='', abs_drop_rate_prefix='', drop_rate_prefix=''):
75 flow_number_str = '| ({:>4}) |'.format(abs(flow_number))
77 flow_number_str = '|{:>7} |'.format(flow_number)
78 if pps_req_tx is None:
79 pps_req_tx_str = '{0: >14}'.format(' NA |')
81 pps_req_tx_str = '{:>7.3f} Mpps |'.format(pps_req_tx)
83 pps_tx_str = '{0: >14}'.format(' NA |')
85 pps_tx_str = '{:>7.3f} Mpps |'.format(pps_tx)
86 if pps_sut_tx is None:
87 pps_sut_tx_str = '{0: >14}'.format(' NA |')
89 pps_sut_tx_str = '{:>7.3f} Mpps |'.format(pps_sut_tx)
91 pps_rx_str = '{0: >25}'.format('NA |')
93 pps_rx_str = bcolors.OKBLUE + '{:>4.1f} Gb/s |{:7.3f} Mpps {}|'.format(RapidTest.get_speed(pps_rx,size),pps_rx,bcolors.ENDC)
95 tot_drop_str = ' | NA | '
97 tot_drop_str = ' | {:>9.0f} | '.format(tot_drop)
99 lat_perc_str = ' |{:^10.10}|'.format('NA')
100 elif lat_perc_max == True:
101 lat_perc_str = '|>{}{:>5.0f} us{} |'.format(lat_perc_prefix,float(lat_perc), bcolors.ENDC)
103 lat_perc_str = '| {}{:>5.0f} us{} |'.format(lat_perc_prefix,float(lat_perc), bcolors.ENDC)
104 if elapsed_time is None:
105 elapsed_time_str = ' NA |'
107 elapsed_time_str = '{:>3.0f} |'.format(elapsed_time)
108 return(flow_number_str + '{:>5.1f}'.format(speed) + '% '+speed_prefix +'{:>6.3f}'.format(RapidTest.get_pps(speed,size)) + ' Mpps|'+ pps_req_tx_str + pps_tx_str + bcolors.ENDC + pps_sut_tx_str + pps_rx_str +lat_avg_prefix+ ' {:>6.0f}'.format(lat_avg)+' us'+lat_perc_str+lat_max_prefix+'{:>6.0f}'.format(lat_max)+' us | ' + '{:>9.0f}'.format(tx) + ' | {:>9.0f}'.format(rx) + ' | '+ abs_drop_rate_prefix+ '{:>9.0f}'.format(tx-rx) + tot_drop_str +drop_rate_prefix+ '{:>5.2f}'.format(old_div(float(tx-rx),tx)) +bcolors.ENDC+' |' + elapsed_time_str)
110 def run_iteration(self, requested_duration, flow_number, size, speed):
111 BUCKET_SIZE_EXP = self.gen_machine.bucket_size_exp
112 LAT_PERCENTILE = self.test['lat_percentile']
115 while (r < self.test['maxr']):
116 time.sleep(sleep_time)
117 # Sleep_time is needed to be able to do accurate measurements to check for packet loss. We need to make this time large enough so that we do not take the first measurement while some packets from the previous tests migth still be in flight
118 t1_rx, t1_non_dp_rx, t1_tx, t1_non_dp_tx, t1_drop, t1_tx_fail, t1_tsc, abs_tsc_hz = self.gen_machine.core_stats()
119 t1_dp_rx = t1_rx - t1_non_dp_rx
120 t1_dp_tx = t1_tx - t1_non_dp_tx
121 self.gen_machine.start_gen_cores()
122 time.sleep(2) ## Needs to be 2 seconds since this 1 sec is the time that PROX uses to refresh the stats. Note that this can be changed in PROX!! Don't do it.
123 if self.sut_machine!= None:
124 t2_sut_rx, t2_sut_non_dp_rx, t2_sut_tx, t2_sut_non_dp_tx, t2_sut_drop, t2_sut_tx_fail, t2_sut_tsc, sut_tsc_hz = self.sut_machine.core_stats()
125 t2_rx, t2_non_dp_rx, t2_tx, t2_non_dp_tx, t2_drop, t2_tx_fail, t2_tsc, tsc_hz = self.gen_machine.core_stats()
127 dp_tx = tx - (t2_non_dp_tx - t1_non_dp_tx )
128 dp_rx = t2_rx - t1_rx - (t2_non_dp_rx - t1_non_dp_rx)
129 tot_dp_drop = dp_tx - dp_rx
131 RapidLog.critical("TX = 0. Test interrupted since no packet has been sent.")
133 RapidLog.critical("Only non-dataplane packets (e.g. ARP) sent. Test interrupted since no packet has been sent.")
134 # Ask PROX to calibrate the bucket size once we have a PROX function to do this.
135 # Measure latency statistics per second
136 lat_min, lat_max, lat_avg, used_avg, t2_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
137 lat_samples = sum(buckets)
139 for sample_percentile, bucket in enumerate(buckets,start=1):
140 sample_count += bucket
141 if sample_count > (lat_samples * LAT_PERCENTILE):
143 percentile_max = (sample_percentile == len(buckets))
144 sample_percentile = sample_percentile * float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
145 if self.test['test'] == 'fixed_rate':
146 RapidLog.info(self.report_result(flow_number,size,speed,None,None,None,None,lat_avg,sample_percentile,percentile_max,lat_max, dp_tx, dp_rx , None, None))
147 tot_rx = tot_non_dp_rx = tot_tx = tot_non_dp_tx = tot_drop = 0
148 lat_avg = used_avg = 0
149 buckets_total = [0] * 128
151 tot_lat_measurement_duration = float(0)
152 tot_core_measurement_duration = float(0)
153 tot_sut_core_measurement_duration = float(0)
154 tot_sut_rx = tot_sut_non_dp_rx = tot_sut_tx = tot_sut_non_dp_tx = tot_sut_drop = tot_sut_tx_fail = tot_sut_tsc = 0
155 lat_avail = core_avail = sut_avail = False
156 while (tot_core_measurement_duration - float(requested_duration) <= 0.1) or (tot_lat_measurement_duration - float(requested_duration) <= 0.1):
158 lat_min_sample, lat_max_sample, lat_avg_sample, used_sample, t3_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
159 # Get statistics after some execution time
160 if t3_lat_tsc != t2_lat_tsc:
161 single_lat_measurement_duration = (t3_lat_tsc - t2_lat_tsc) * 1.0 / lat_hz # time difference between the 2 measurements, expressed in seconds.
162 # A second has passed in between to lat_stats requests. Hence we need to process the results
163 tot_lat_measurement_duration = tot_lat_measurement_duration + single_lat_measurement_duration
164 if lat_min > lat_min_sample:
165 lat_min = lat_min_sample
166 if lat_max < lat_max_sample:
167 lat_max = lat_max_sample
168 lat_avg = lat_avg + lat_avg_sample * single_lat_measurement_duration # Sometimes, There is more than 1 second between 2 lat_stats. Hence we will take the latest measurement
169 used_avg = used_avg + used_sample * single_lat_measurement_duration # and give it more weigth.
170 lat_samples = sum(buckets)
171 tot_lat_samples += lat_samples
173 for sample_percentile, bucket in enumerate(buckets,start=1):
174 sample_count += bucket
175 if sample_count > lat_samples * LAT_PERCENTILE:
177 percentile_max = (sample_percentile == len(buckets))
178 sample_percentile = sample_percentile * float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
179 buckets_total = [buckets_total[i] + buckets[i] for i in range(len(buckets_total))]
180 t2_lat_tsc = t3_lat_tsc
182 t3_rx, t3_non_dp_rx, t3_tx, t3_non_dp_tx, t3_drop, t3_tx_fail, t3_tsc, tsc_hz = self.gen_machine.core_stats()
184 single_core_measurement_duration = (t3_tsc - t2_tsc) * 1.0 / tsc_hz # time difference between the 2 measurements, expressed in seconds.
185 tot_core_measurement_duration = tot_core_measurement_duration + single_core_measurement_duration
186 delta_rx = t3_rx - t2_rx
188 delta_non_dp_rx = t3_non_dp_rx - t2_non_dp_rx
189 tot_non_dp_rx += delta_non_dp_rx
190 delta_tx = t3_tx - t2_tx
192 delta_non_dp_tx = t3_non_dp_tx - t2_non_dp_tx
193 tot_non_dp_tx += delta_non_dp_tx
194 delta_dp_tx = delta_tx -delta_non_dp_tx
195 delta_dp_rx = delta_rx -delta_non_dp_rx
196 delta_dp_drop = delta_dp_tx - delta_dp_rx
197 tot_dp_drop += delta_dp_drop
198 delta_drop = t3_drop - t2_drop
199 tot_drop += delta_drop
200 t2_rx, t2_non_dp_rx, t2_tx, t2_non_dp_tx, t2_drop, t2_tx_fail, t2_tsc = t3_rx, t3_non_dp_rx, t3_tx, t3_non_dp_tx, t3_drop, t3_tx_fail, t3_tsc
202 if self.sut_machine!=None:
203 t3_sut_rx, t3_sut_non_dp_rx, t3_sut_tx, t3_sut_non_dp_tx, t3_sut_drop, t3_sut_tx_fail, t3_sut_tsc, sut_tsc_hz = self.sut_machine.core_stats()
204 if t3_sut_tsc != t2_sut_tsc:
205 single_sut_core_measurement_duration = (t3_sut_tsc - t2_sut_tsc) * 1.0 / tsc_hz # time difference between the 2 measurements, expressed in seconds.
206 tot_sut_core_measurement_duration = tot_sut_core_measurement_duration + single_sut_core_measurement_duration
207 tot_sut_rx += t3_sut_rx - t2_sut_rx
208 tot_sut_non_dp_rx += t3_sut_non_dp_rx - t2_sut_non_dp_rx
209 delta_sut_tx = t3_sut_tx - t2_sut_tx
210 tot_sut_tx += delta_sut_tx
211 delta_sut_non_dp_tx = t3_sut_non_dp_tx - t2_sut_non_dp_tx
212 tot_sut_non_dp_tx += delta_sut_non_dp_tx
213 t2_sut_rx, t2_sut_non_dp_rx, t2_sut_tx, t2_sut_non_dp_tx, t2_sut_drop, t2_sut_tx_fail, t2_sut_tsc = t3_sut_rx, t3_sut_non_dp_rx, t3_sut_tx, t3_sut_non_dp_tx, t3_sut_drop, t3_sut_tx_fail, t3_sut_tsc
215 if self.test['test'] == 'fixed_rate':
216 if lat_avail == core_avail == True:
217 lat_avail = core_avail = False
218 pps_req_tx = (delta_tx + delta_drop - delta_rx)/single_core_measurement_duration/1000000
219 pps_tx = delta_tx/single_core_measurement_duration/1000000
220 if self.sut_machine != None and sut_avail:
221 pps_sut_tx = delta_sut_tx/single_sut_core_measurement_duration/1000000
225 pps_rx = delta_rx/single_core_measurement_duration/1000000
226 RapidLog.info(self.report_result(flow_number, size,
227 speed, pps_req_tx, pps_tx, pps_sut_tx, pps_rx,
228 lat_avg_sample, sample_percentile, percentile_max,
229 lat_max_sample, delta_dp_tx, delta_dp_rx,
230 tot_dp_drop, single_core_measurement_duration))
232 self.gen_machine.stop_gen_cores()
234 lat_avg = old_div(lat_avg, float(tot_lat_measurement_duration))
235 used_avg = old_div(used_avg, float(tot_lat_measurement_duration))
237 while t4_tsc == t2_tsc:
238 t4_rx, t4_non_dp_rx, t4_tx, t4_non_dp_tx, t4_drop, t4_tx_fail, t4_tsc, abs_tsc_hz = self.gen_machine.core_stats()
239 if self.test['test'] == 'fixed_rate':
240 t4_lat_tsc = t2_lat_tsc
241 while t4_lat_tsc == t2_lat_tsc:
242 lat_min_sample, lat_max_sample, lat_avg_sample, used_sample, t4_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
244 lat_samples = sum(buckets)
245 for percentile, bucket in enumerate(buckets,start=1):
246 sample_count += bucket
247 if sample_count > lat_samples * LAT_PERCENTILE:
249 percentile_max = (percentile == len(buckets))
250 percentile = percentile * float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
251 lat_max = lat_max_sample
252 lat_avg = lat_avg_sample
253 delta_rx = t4_rx - t2_rx
254 delta_non_dp_rx = t4_non_dp_rx - t2_non_dp_rx
255 delta_tx = t4_tx - t2_tx
256 delta_non_dp_tx = t4_non_dp_tx - t2_non_dp_tx
257 delta_dp_tx = delta_tx -delta_non_dp_tx
258 delta_dp_rx = delta_rx -delta_non_dp_rx
261 tot_dp_drop += delta_dp_tx - delta_dp_rx
266 drop_rate = 100.0*(dp_tx-dp_rx)/dp_tx
267 tot_core_measurement_duration = None
268 break ## Not really needed since the while loop will stop when evaluating the value of r
271 for percentile, bucket in enumerate(buckets_total,start=1):
272 sample_count += bucket
273 if sample_count > tot_lat_samples * LAT_PERCENTILE:
275 percentile_max = (percentile == len(buckets_total))
276 percentile = percentile * float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
277 pps_req_tx = (tot_tx + tot_drop - tot_rx)/tot_core_measurement_duration/1000000.0 # tot_drop is all packets dropped by all tasks. This includes packets dropped at the generator task + packets dropped by the nop task. In steady state, this equals to the number of packets received by this VM
278 pps_tx = tot_tx/tot_core_measurement_duration/1000000.0 # tot_tx is all generated packets actually accepted by the interface
279 pps_rx = tot_rx/tot_core_measurement_duration/1000000.0 # tot_rx is all packets received by the nop task = all packets received in the gen VM
280 if self.sut_machine != None and sut_avail:
281 pps_sut_tx = tot_sut_tx / tot_sut_core_measurement_duration / 1000000.0
284 dp_tx = (t4_tx - t1_tx) - (t4_non_dp_tx - t1_non_dp_tx)
285 dp_rx = (t4_rx - t1_rx) - (t4_non_dp_rx - t1_non_dp_rx)
286 tot_dp_drop = dp_tx - dp_rx
287 drop_rate = 100.0*tot_dp_drop/dp_tx
288 if ((drop_rate < self.test['drop_rate_threshold']) or (tot_dp_drop == self.test['drop_rate_threshold'] ==0) or (tot_dp_drop > self.test['maxz'])):
290 return(pps_req_tx,pps_tx,pps_sut_tx,pps_rx,lat_avg,percentile,percentile_max,lat_max,dp_tx,dp_rx,tot_dp_drop,(t4_tx_fail - t1_tx_fail),drop_rate,lat_min,used_avg,r,tot_core_measurement_duration)