SyoSil ApS UVM Scoreboard  1.0.3.0
cl_scb_test_queue_find_vs_search.svh
1 /// A test comparing the performance of using iterators vs using .find_first on a SV queue
2 //
3 // Preliminary results of average time [s] to find all M elements with M=50, num_iter=50
4 // N | find_first | iterator
5 // ------+------------+--------
6 // 100 | 0.024 | 0.037
7 // 1000 | 0.135 | 0.283
8 // 10000 | 1.216 | 2.560
9 // 50000 | 6.093 | 13.632
10 // Conclusion: find_first is better than using iterators
11 
12 
13 //Preliminary results of average time to find all M elements with M=50,
14 //and also respecting the value of max_search_window
15 //average times over a number of different queue sizes and values of msw
16 // iterator: 3.7 seconds
17 // find_first on full queue: 6.6 seconds
18 // find_first on subqueue: 2.7 seconds
19 class cl_scb_test_queue_find_vs_search extends cl_scb_test_single_scb;
20  //-------------------------------------
21  // Non randomizable variables
22  //-------------------------------------
23  int N = 1000; //Number of items to generate
24  int M = 50; //Number of items to find
25 
26 
27  // cl_tb_seq_item items[$]; //All N items that we generate
28  // cl_tb_seq_item search_items[$]; //The M items that we're searching for
29  // cl_syoscb_proxy_item_std proxy_items[$]; //Proxy items for the search items
30  uvm_comparer comparer;
31 
32 
33  //-------------------------------------
34  // Randomizable variables
35  //-------------------------------------
36  randc int indices[]; //Indices in 'items' of the search items to find
37 
38  //-------------------------------------
39  // UVM Macros
40  //-------------------------------------
41  `uvm_component_utils_begin(cl_scb_test_queue_find_vs_search)
42 
43  `uvm_component_utils_end
44 
45  //-------------------------------------
46  // Constraints
47  //-------------------------------------
48  constraint co_indices {
49  foreach(indices[i])
50  indices[i] inside{[0:N-1]};
51  indices.size() == M;
52  }
53 
54 
55  //-------------------------------------
56  // Constructor
57  //-------------------------------------
58  function new(string name = "cl_scb_test_queue_find_vs_search", uvm_component parent = null);
59  super.new(name, parent);
60 
61  this.comparer = new;
62  cl_syoscb_comparer_config::set_verbosity(this.comparer, UVM_FULL);
63  endfunction: new
64 
65  //-------------------------------------
66  // Functions
67  //-------------------------------------
68  extern virtual function void pre_build();
69  extern task run_phase(uvm_phase phase);
70 
71  extern function void compare_iterator_and_find_first();
72  extern function void compare_msw();
73  extern function void compare_msw_approaches(int n, int m, int msw, int num_iter, ref real results[$]);
74  extern function bit compare_items(uvm_object primary_item, uvm_object sec_item);
75  extern function void log_time(string fname);
76  extern function real get_time_diff(string file1, string file2);
77 
79 
80 function void cl_scb_test_queue_find_vs_search::pre_build();
81  super.pre_build();
82 
83  this.syoscb_cfgs.syoscb_cfg[0].set_enable_no_insert_check(1'b0);
84 endfunction: pre_build
85 
86 task cl_scb_test_queue_find_vs_search::run_phase(uvm_phase phase);
87  phase.raise_objection(this);
88  super.run_phase(phase);
89 
90  this.compare_iterator_and_find_first();
91  // Commented out on purpose: This test takes a long time to run,
92  // and does not contribute anything to regression testing.
93  // Uncomment and run manually where necessary
94  // this.compare_msw();
95 
96  phase.drop_objection(this);
97 endtask: run_phase
98 
99 //Gets current time, stores it in the file named "fname"
100 function void cl_scb_test_queue_find_vs_search::log_time(string fname);
101  $system($sformatf("date -Ins > %0s.time", fname));
102 endfunction: log_time
103 
104 // Wrapper around compare_msw_approaches for storing results between runs
105 function void cl_scb_test_queue_find_vs_search::compare_msw();
106 
107  //AA of all different N, M, MSW values
108  //Dim 1: N
109  //Dim 2: msw
110  //Dim 3: Approach
111  //For all tests: M=50, num_iter=50
112  real test_results[int][int][int];
113  int Nvals[];
114  int msw[];
115 
116  Nvals = '{100, 1000, 5000};
117  msw = '{2, 4, 6, 8, 10};
118 
119  foreach(Nvals[i]) begin
120  foreach(msw[j]) begin
121  real results[$];
122  compare_msw_approaches(Nvals[i], 50, Nvals[i]/msw[j], 20, results);
123  test_results[Nvals[i]][msw[j]][0] = results[0];
124  test_results[Nvals[i]][msw[j]][1] = results[1];
125  test_results[Nvals[i]][msw[j]][2] = results[2];
126  end
127  end
128 
129  foreach(test_results[i,j,k]) begin
130  $display("test_results[%0d][%0d][%0d]=%f", i, j, k, test_results[i][j][k]);
131  end
132 endfunction: compare_msw
133 
134 // Compares different approaches to finding items when max_search_window is non-zero
135 // Approach 1: Use an iterator to search over the queue instead of using find_first
136 // Approach 2: Use find_first, but if the found index is greater than msw, return null instead
137 // Approach 3: Copy the underlying queue, use SV queue-slicing to create a new queue with data from an old queue
138 function void cl_scb_test_queue_find_vs_search::compare_msw_approaches(int n, int m, int msw, int num_iter, ref real results[$]);
139  real sum_approach1, sum_approach2, sum_approach3; //sum of times for each approach
140  bit should_be_found[$]; //Indicates whether a given item should be found. Used for error checking
141  cl_syoscb_item items[$]; //All N items in the queue
142  cl_syoscb_item search_items[$]; //The M items we are searching for
143 
144  sum_approach1 = 0.0;
145  sum_approach2 = 0.0;
146  sum_approach3 = 0.0;
147 
148  //Reassigning to class variables to easily randomize indices
149  this.N = n;
150  this.M = m;
151 
152  $display("Testing MSW approaches with N=%0d, msw=%0d", n, msw);
153  for(int iter=0; iter<num_iter; iter++) begin
154  search_items.delete();
155  should_be_found.delete();
156  //items is not deleted since it points to the SV queue used in scoreboard
157 
158  //Generate the items to search for
159  for(int i=0; i<n; i++) begin
160  cl_tb_seq_item ctsi = cl_tb_seq_item::type_id::create($sformatf("ctsi_%0d", i));
161  if(!ctsi.randomize()) begin
162  `uvm_fatal("RAND", $sformatf("Unable to randomize ctsi %0d on iteration %0d", i, iter));
163  end
164  this.scb_env.syoscb[0].add_item("Q1", "P1", ctsi);
165  end
166 
167  //Get handle to the underlying queue
168  begin
169  cl_syoscb_queue_std queue_std;
170  cl_syoscb_queue_base queue_base;
171 
172  queue_base = this.syoscb_cfgs.syoscb_cfg[0].get_queue("Q1");
173  if(!$cast(queue_std, queue_base)) begin
174  `uvm_fatal("CAST", "Unable to cast Q1 to queue_std")
175  end
176  queue_std.get_native_queue(items);
177  end
178 
179  //Generate the M indices that we wish to search for, store those M items in search_items
180  //If that items index is outside of range, set should_be_found to 0
181  void'(randomize(indices));
182  foreach(indices[i]) begin
183  search_items.push_back(items[indices[i]]);
184  if(indices[i] < msw) begin
185  should_be_found.push_back(1'b1);
186  end else begin
187  should_be_found.push_back(1'b0);
188  end
189  end
190 
191  //Start approach 1: Generate an iterator, find all items if they are within msw
192  begin
195  cl_syoscb_item sec_item;
196 
197  iter = this.syoscb_cfgs.syoscb_cfg[0].get_queue("Q1").create_iterator();
198  log_time("a1_start");
199  foreach(search_items[i]) begin
200  void'(iter.first());
201 
202  while(iter.has_next() && iter.next_index() < msw) begin
203  proxy = iter.next();
204  sec_item = proxy.get_item();
205  if(this.compare_items(search_items[i], sec_item)) begin
206  //if match, verify that the code isn't sloppy. If not, break out, look for next item
207  if(!should_be_found[i]) begin
208  `uvm_fatal("APPROACH1", $sformatf("Iterator found item where it shouldn't. idx=%0d, msw=%0d", iter.previous_index(), msw))
209  end
210  break;
211  end
212  void'(iter.next());
213  end
214  end
215  log_time("a1_end");
216  void'(this.syoscb_cfgs.syoscb_cfg[0].get_queue("Q1").delete_iterator(iter));
217  end
218 
219  //Start approach 2. Use find_first_index, if index > msw, just discard it
220  begin
221  int found[$];
222  log_time("a2_start");
223  foreach(search_items[i]) begin
224  found = items.find_first_index(x) with (this.compare_items(x, search_items[i]));
225  if(found.size() != 1) begin
226  `uvm_fatal("APPROACH2", $sformatf("Unable to find match for search item %0d. found.size=%0d", i, found.size()))
227  end
228  //At this point, should return item or null depending on size of msw, but that does not matter here
229  end
230  log_time("a2_end");
231  end
232 
233  //Start approach 3. Copy the queue, search through that copy
234  //To be realistic, we must make the copy every time
235  begin
236  cl_syoscb_item subqueue[$];
237  int found[$];
238  log_time("a3_start");
239  foreach(search_items[i]) begin
240  subqueue = items[0:msw-1];
241  found = subqueue.find_first_index(x) with (this.compare_items(x, search_items[i]));
242  if(found.size() == 1 && !should_be_found[i]) begin
243  `uvm_fatal("APPROACH3", $sformatf("Found search_item[%0d] but was not supposed to find it. Iter=%0d, found.size()=%0d", i, iter, found.size()))
244  end else if (found.size() != 1 && should_be_found[i]) begin
245  `uvm_fatal("APPROACH3", $sformatf("Did not find search_item[%0d] but was supposed to find it. Iter=%0d, found.size=%0d", i, iter, found.size()))
246  end
247  end
248  log_time("a3_end");
249  end
250 
251  sum_approach1 += get_time_diff("a1_start", "a1_end");
252  sum_approach2 += get_time_diff("a2_start", "a2_end");
253  sum_approach3 += get_time_diff("a3_start", "a3_end");
254  this.scb_env.syoscb[0].flush_queues_all();
255  $display("Iteration %0d/%0d finished", iter+1, num_iter);
256  end
257 
258  results.delete();
259  results.push_back(sum_approach1);
260  results.push_back(sum_approach2);
261  results.push_back(sum_approach3);
262 endfunction: compare_msw_approaches
263 
264 //Compares the performance of using an iterator vs. using find_first when
265 //searching through an entire queue for an item
266 function void cl_scb_test_queue_find_vs_search::compare_iterator_and_find_first();
267  cl_tb_seq_item q[$]; //Queue for storing return value from find_first
268  real sum_findfirst; //total time used on find_first
269  real sum_iterator; //total time spent on iterator
270  int num_iter; //number of times to perform test
271  cl_tb_seq_item items[$]; //All N items that we generate
272  cl_tb_seq_item search_items[$]; //The M items that we're searching for
273  cl_syoscb_proxy_item_std proxy_items[$]; //Proxy items for the search items
274 
275  sum_findfirst = 0.0;
276  sum_iterator = 0.0;
277  num_iter = 10;
278 
279  $display("Comparing iterator and find_first performance. N=%0d", this.N);
280  for(int iter=0; iter<num_iter; iter++) begin
281  q.delete();
282  items.delete();
283  proxy_items.delete();
284  search_items.delete();
285 
286  //Generate N random seq. items
287  for(int i=0; i<N; i++) begin
288  cl_tb_seq_item ctsi = cl_tb_seq_item::type_id::create($sformatf("ctsi_%0d", i));
289  if(!ctsi.randomize()) begin
290  `uvm_fatal("RAND", $sformatf("Unable to randomize ctsi %0d", i))
291  end
292  //Store items in queue, insert all items into Q1 with P1 as producer
293  items.push_back(ctsi);
294  this.scb_env.syoscb[0].add_item("Q1", "P1", ctsi);
295  end
296 
297  //Generate M random indices which we wish to find, store items from those indices in a new array
298  void'(randomize(indices));
299  foreach(indices[i]) begin
300  cl_syoscb_proxy_item_std proxy = new;
301  proxy.idx = indices[i];
302  proxy.set_queue(this.syoscb_cfgs.syoscb_cfg[0].get_queue("Q1"));
303  //Create proxy items for the iterator, search items for the find_first implementation
304  proxy_items.push_back(proxy);
305  search_items.push_back(items[indices[i]]);
306  end
307 
308  //Time how long it takes using find_first
309  log_time("time1");
310  foreach(search_items[i]) begin
311  q = items.find_first with (this.compare_items(item, search_items[i]));
312  if(q.size() != 1) begin
313  `uvm_fatal("FIND_FIRST", $sformatf("find_first did not find the requested search item. q.size=%0d", q.size()))
314  end
315  end
316  log_time("time2");
317 
318  log_time("time3");
319  //Time how long it takes using an iterator
320  //We only create the iterator once
321  foreach(proxy_items[i]) begin
323  cl_syoscb_queue_base queue;
325  bit found;
326 
327  queue = this.syoscb_cfgs.syoscb_cfg[0].get_queue("Q1");
328  iter = queue.get_iterator("default");
329  if(iter == null) begin
330  iter = queue.create_iterator("default");
331  end
332  void'(iter.first());
333  found = 1'b0;
334 
335  while(iter.has_next()) begin
336  if(compare_items(proxy_items[i].get_item(), iter.next().get_item())) begin
337  found = 1'b1;
338  break;
339  end
340  void'(iter.next());
341  end
342  if(!found) begin
343  `uvm_fatal("ITERATOR", $sformatf("Unable to find match for proxy_items[%0d]", i))
344  end
345  end
346  log_time("time4");
347 
348  sum_findfirst += get_time_diff("time1", "time2");
349  sum_iterator += get_time_diff("time3", "time4");
350 
351  //Flush out all items to ensure that we don't have orphan errors
352  this.scb_env.syoscb[0].flush_queues_all();
353  $display("Iteration %0d/%0d finished", iter+1, num_iter);
354  end
355 
356  $display("num_iter: %0d, N=%0d, M=%0d", num_iter, this.N, this.M);
357  $display("Sum of FF times: %f. Avg of FF times: %f", sum_findfirst, sum_findfirst/real'(num_iter));
358  $display("Sum of iterator times: %f. Avg of iterator times: %f", sum_iterator, sum_iterator/real'(num_iter));
359  $display("\n\n");
360 endfunction: compare_iterator_and_find_first
361 
362 function real cl_scb_test_queue_find_vs_search::get_time_diff(string file1, string file2);
363  int f1, f2;
364  string s1, s2;
365  real t1, t2;
366  real a;
367 
368  f1 = $fopen($sformatf("%0s.time", file1), "r");
369  f2 = $fopen($sformatf("%0s.time", file2), "r");
370 
371  void'($fgets(s1, f1));
372  void'($fgets(s2, f2));
373  //date -Ins returns a string of the type
374  //2022-01-24T08:48:50,131570320+0100
375  //We wish to extract only the seconds-part of it, and replace the ',' with a '.'
376  s1 = s1.substr(17, 28);
377  s2 = s2.substr(17, 28);
378  s1.putc(2, ".");
379  s2.putc(2, ".");
380 
381  t1 = s1.atoreal();
382  t2 = s2.atoreal();
383 
384  if(t2 < t1) begin //means that we crossed a minute marker
385  a = 60.0 + t2 - t1;
386  end else begin
387  a = t2 - t1;
388  end
389 
390  $fclose(f1);
391  $fclose(f2);
392 
393  return a;
394 endfunction: get_time_diff
395 
396 /// Compares two sequence items using an implicit comparer
397 /// Returns 1'b1 if the comparison is true, 1'b0 otherwise
398 function bit cl_scb_test_queue_find_vs_search::compare_items(uvm_object primary_item, uvm_object sec_item);
399  if (primary_item.compare(sec_item, this.comparer)) begin
400  `uvm_info("DEBUG", $sformatf(" Secondary item found"), UVM_FULL);
401  return 1'b1;
402  end else begin
403  return 1'b0;
404  end
405 endfunction: compare_items
A test comparing the performance of using iterators vs using .find_first on a SV queue.
static void set_verbosity(uvm_comparer comparer, int unsigned cv=UVM_DEBUG)
Sets the verbosity level of a given comparer.
The UVM scoreboard item which wraps uvm_sequence_item .
Base class for all proxy items.
Queue iterator base class defining the iterator API used for iterating over queues.
Class which represents the base concept of a queue.
Proxy item implementation for standard queues.

Project: SyoSil ApS UVM Scoreboard, Revision: 1.0.3.0

Copyright 2014-2022 SyoSil ApS
All Rights Reserved Worldwide

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
doxygen
Doxygen Version: 1.8.14
Generated with IDV SV Filter Version: 2.6.3
Fri Sep 2 2022 14:38:27
Find a documentation bug? Report bugs to: scoreboard@syosil.com