4 ## Copyright (c) 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 # 12 bytes is the inter packet gap
33 # pre-amble is 7 bytes
34 # SFD (start of frame delimiter) is 1 byte
35 # Total of 20 bytes overhead per packet
36 return (pps_speed / 1000000.0 * 0.08 * (size+20))
39 def get_pps(speed,size):
40 # speed is given in % of 10Gb/s, returning Mpps
41 # 12 bytes is the inter packet gap
42 # pre-amble is 7 bytes
43 # SFD (start of frame delimiter) is 1 byte
44 # Total of 20 bytes overhead per packet
45 return (speed * 100.0 / (8*(size+20)))
48 def get_speed(packet_speed,size):
49 # return speed in Gb/s
50 # 12 bytes is the inter packet gap
51 # pre-amble is 7 bytes
52 # SFD (start of frame delimiter) is 1 byte
53 # Total of 20 bytes overhead per packet
54 return (packet_speed / 1000.0 * (8*(size+20)))
57 def set_background_flows(background_machines, number_of_flows):
58 for machine in background_machines:
59 _ = machine.set_flows(number_of_flows)
62 def set_background_speed(background_machines, speed):
63 for machine in background_machines:
64 machine.set_generator_speed(speed)
67 def set_background_size(background_machines, imix):
68 # imixs is a list of packet sizes
69 for machine in background_machines:
70 machine.set_udp_packet_size(imix)
73 def start_background_traffic(background_machines):
74 for machine in background_machines:
78 def stop_background_traffic(background_machines):
79 for machine in background_machines:
83 def report_result(flow_number, size, speed, pps_req_tx, pps_tx, pps_sut_tx,
84 pps_rx, lat_avg, lat_perc, lat_perc_max, lat_max, tx, rx, tot_drop,
85 elapsed_time,speed_prefix='', lat_avg_prefix='', lat_perc_prefix='',
86 lat_max_prefix='', abs_drop_rate_prefix='', drop_rate_prefix=''):
88 flow_number_str = '| ({:>4}) |'.format(abs(flow_number))
90 flow_number_str = '|{:>7} |'.format(flow_number)
91 if pps_req_tx is None:
92 pps_req_tx_str = '{0: >14}'.format(' NA |')
94 pps_req_tx_str = '{:>7.3f} Mpps |'.format(pps_req_tx)
96 pps_tx_str = '{0: >14}'.format(' NA |')
98 pps_tx_str = '{:>7.3f} Mpps |'.format(pps_tx)
99 if pps_sut_tx is None:
100 pps_sut_tx_str = '{0: >14}'.format(' NA |')
102 pps_sut_tx_str = '{:>7.3f} Mpps |'.format(pps_sut_tx)
104 pps_rx_str = '{0: >25}'.format('NA |')
106 pps_rx_str = bcolors.OKBLUE + '{:>4.1f} Gb/s |{:7.3f} Mpps {}|'.format(RapidTest.get_speed(pps_rx,size),pps_rx,bcolors.ENDC)
108 tot_drop_str = ' | NA | '
110 tot_drop_str = ' | {:>9.0f} | '.format(tot_drop)
112 lat_perc_str = ' |{:^10.10}|'.format('NA')
113 elif lat_perc_max == True:
114 lat_perc_str = '|>{}{:>5.0f} us{} |'.format(lat_perc_prefix,float(lat_perc), bcolors.ENDC)
116 lat_perc_str = '| {}{:>5.0f} us{} |'.format(lat_perc_prefix,float(lat_perc), bcolors.ENDC)
117 if elapsed_time is None:
118 elapsed_time_str = ' NA |'
120 elapsed_time_str = '{:>3.0f} |'.format(elapsed_time)
121 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)
123 def run_iteration(self, requested_duration, flow_number, size, speed):
124 BUCKET_SIZE_EXP = self.gen_machine.bucket_size_exp
125 LAT_PERCENTILE = self.test['lat_percentile']
128 while (r < self.test['maxr']):
129 time.sleep(sleep_time)
130 # 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
131 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()
132 t1_dp_rx = t1_rx - t1_non_dp_rx
133 t1_dp_tx = t1_tx - t1_non_dp_tx
134 self.gen_machine.start_gen_cores()
135 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.
136 if self.sut_machine!= None:
137 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()
138 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()
140 dp_tx = tx - (t2_non_dp_tx - t1_non_dp_tx )
141 dp_rx = t2_rx - t1_rx - (t2_non_dp_rx - t1_non_dp_rx)
142 tot_dp_drop = dp_tx - dp_rx
144 RapidLog.critical("TX = 0. Test interrupted since no packet has been sent.")
146 RapidLog.critical("Only non-dataplane packets (e.g. ARP) sent. Test interrupted since no packet has been sent.")
147 # Ask PROX to calibrate the bucket size once we have a PROX function to do this.
148 # Measure latency statistics per second
149 lat_min, lat_max, lat_avg, used_avg, t2_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
150 lat_samples = sum(buckets)
152 for sample_percentile, bucket in enumerate(buckets,start=1):
153 sample_count += bucket
154 if sample_count > (lat_samples * LAT_PERCENTILE):
156 percentile_max = (sample_percentile == len(buckets))
157 sample_percentile = sample_percentile * float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
158 if self.test['test'] == 'fixed_rate':
159 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))
160 tot_rx = tot_non_dp_rx = tot_tx = tot_non_dp_tx = tot_drop = 0
161 lat_avg = used_avg = 0
162 buckets_total = [0] * 128
164 tot_lat_measurement_duration = float(0)
165 tot_core_measurement_duration = float(0)
166 tot_sut_core_measurement_duration = float(0)
167 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
168 lat_avail = core_avail = sut_avail = False
169 while (tot_core_measurement_duration - float(requested_duration) <= 0.1) or (tot_lat_measurement_duration - float(requested_duration) <= 0.1):
171 lat_min_sample, lat_max_sample, lat_avg_sample, used_sample, t3_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
172 # Get statistics after some execution time
173 if t3_lat_tsc != t2_lat_tsc:
174 single_lat_measurement_duration = (t3_lat_tsc - t2_lat_tsc) * 1.0 / lat_hz # time difference between the 2 measurements, expressed in seconds.
175 # A second has passed in between to lat_stats requests. Hence we need to process the results
176 tot_lat_measurement_duration = tot_lat_measurement_duration + single_lat_measurement_duration
177 if lat_min > lat_min_sample:
178 lat_min = lat_min_sample
179 if lat_max < lat_max_sample:
180 lat_max = lat_max_sample
181 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
182 used_avg = used_avg + used_sample * single_lat_measurement_duration # and give it more weigth.
183 lat_samples = sum(buckets)
184 tot_lat_samples += lat_samples
186 for sample_percentile, bucket in enumerate(buckets,start=1):
187 sample_count += bucket
188 if sample_count > lat_samples * LAT_PERCENTILE:
190 percentile_max = (sample_percentile == len(buckets))
191 sample_percentile = sample_percentile * float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
192 buckets_total = [buckets_total[i] + buckets[i] for i in range(len(buckets_total))]
193 t2_lat_tsc = t3_lat_tsc
195 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()
197 single_core_measurement_duration = (t3_tsc - t2_tsc) * 1.0 / tsc_hz # time difference between the 2 measurements, expressed in seconds.
198 tot_core_measurement_duration = tot_core_measurement_duration + single_core_measurement_duration
199 delta_rx = t3_rx - t2_rx
201 delta_non_dp_rx = t3_non_dp_rx - t2_non_dp_rx
202 tot_non_dp_rx += delta_non_dp_rx
203 delta_tx = t3_tx - t2_tx
205 delta_non_dp_tx = t3_non_dp_tx - t2_non_dp_tx
206 tot_non_dp_tx += delta_non_dp_tx
207 delta_dp_tx = delta_tx -delta_non_dp_tx
208 delta_dp_rx = delta_rx -delta_non_dp_rx
209 delta_dp_drop = delta_dp_tx - delta_dp_rx
210 tot_dp_drop += delta_dp_drop
211 delta_drop = t3_drop - t2_drop
212 tot_drop += delta_drop
213 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
215 if self.sut_machine!=None:
216 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()
217 if t3_sut_tsc != t2_sut_tsc:
218 single_sut_core_measurement_duration = (t3_sut_tsc - t2_sut_tsc) * 1.0 / tsc_hz # time difference between the 2 measurements, expressed in seconds.
219 tot_sut_core_measurement_duration = tot_sut_core_measurement_duration + single_sut_core_measurement_duration
220 tot_sut_rx += t3_sut_rx - t2_sut_rx
221 tot_sut_non_dp_rx += t3_sut_non_dp_rx - t2_sut_non_dp_rx
222 delta_sut_tx = t3_sut_tx - t2_sut_tx
223 tot_sut_tx += delta_sut_tx
224 delta_sut_non_dp_tx = t3_sut_non_dp_tx - t2_sut_non_dp_tx
225 tot_sut_non_dp_tx += delta_sut_non_dp_tx
226 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
228 if self.test['test'] == 'fixed_rate':
229 if lat_avail == core_avail == True:
230 lat_avail = core_avail = False
231 pps_req_tx = (delta_tx + delta_drop - delta_rx)/single_core_measurement_duration/1000000
232 pps_tx = delta_tx/single_core_measurement_duration/1000000
233 if self.sut_machine != None and sut_avail:
234 pps_sut_tx = delta_sut_tx/single_sut_core_measurement_duration/1000000
238 pps_rx = delta_rx/single_core_measurement_duration/1000000
239 RapidLog.info(self.report_result(flow_number, size,
240 speed, pps_req_tx, pps_tx, pps_sut_tx, pps_rx,
241 lat_avg_sample, sample_percentile, percentile_max,
242 lat_max_sample, delta_dp_tx, delta_dp_rx,
243 tot_dp_drop, single_core_measurement_duration))
245 self.gen_machine.stop_gen_cores()
247 lat_avg = old_div(lat_avg, float(tot_lat_measurement_duration))
248 used_avg = old_div(used_avg, float(tot_lat_measurement_duration))
250 while t4_tsc == t2_tsc:
251 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()
252 if self.test['test'] == 'fixed_rate':
253 t4_lat_tsc = t2_lat_tsc
254 while t4_lat_tsc == t2_lat_tsc:
255 lat_min_sample, lat_max_sample, lat_avg_sample, used_sample, t4_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
257 lat_samples = sum(buckets)
258 for percentile, bucket in enumerate(buckets,start=1):
259 sample_count += bucket
260 if sample_count > lat_samples * LAT_PERCENTILE:
262 percentile_max = (percentile == len(buckets))
263 percentile = percentile * float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
264 lat_max = lat_max_sample
265 lat_avg = lat_avg_sample
266 delta_rx = t4_rx - t2_rx
267 delta_non_dp_rx = t4_non_dp_rx - t2_non_dp_rx
268 delta_tx = t4_tx - t2_tx
269 delta_non_dp_tx = t4_non_dp_tx - t2_non_dp_tx
270 delta_dp_tx = delta_tx -delta_non_dp_tx
271 delta_dp_rx = delta_rx -delta_non_dp_rx
274 tot_dp_drop += delta_dp_tx - delta_dp_rx
279 drop_rate = 100.0*(dp_tx-dp_rx)/dp_tx
280 tot_core_measurement_duration = None
281 break ## Not really needed since the while loop will stop when evaluating the value of r
284 for percentile, bucket in enumerate(buckets_total,start=1):
285 sample_count += bucket
286 if sample_count > tot_lat_samples * LAT_PERCENTILE:
288 percentile_max = (percentile == len(buckets_total))
289 percentile = percentile * float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
290 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
291 pps_tx = tot_tx/tot_core_measurement_duration/1000000.0 # tot_tx is all generated packets actually accepted by the interface
292 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
293 if self.sut_machine != None and sut_avail:
294 pps_sut_tx = tot_sut_tx / tot_sut_core_measurement_duration / 1000000.0
297 dp_tx = (t4_tx - t1_tx) - (t4_non_dp_tx - t1_non_dp_tx)
298 dp_rx = (t4_rx - t1_rx) - (t4_non_dp_rx - t1_non_dp_rx)
299 tot_dp_drop = dp_tx - dp_rx
300 drop_rate = 100.0*tot_dp_drop/dp_tx
301 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'])):
303 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)