A Discrete-Event Network Simulator
API
bench-scheduler.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2006 INRIA
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
18 */
19
20#include "ns3/core-module.h"
21
22#include <cmath> // sqrt
23#include <fstream>
24#include <iomanip>
25#include <iostream>
26#include <string.h>
27#include <vector>
28
29using namespace ns3;
30
32bool g_debug = false;
33
35std::string g_me;
37#define LOG(x) std::cout << x << std::endl
39#define LOGME(x) LOG(g_me << x)
41#define DEB(x) \
42 if (g_debug) \
43 { \
44 LOGME(x); \
45 }
46
48int g_fwidth = 6;
49
58class Bench
59{
60 public:
66 Bench(const uint64_t population, const uint64_t total)
67 : m_population(population),
68 m_total(total),
69 m_count(0)
70 {
71 }
72
80 {
81 m_rand = stream;
82 }
83
89 void SetPopulation(const uint64_t population)
90 {
91 m_population = population;
92 }
93
98 void SetTotal(const uint64_t total)
99 {
100 m_total = total;
101 }
102
104 struct Result
105 {
106 double init;
107 double simu;
108 uint64_t pop;
109 uint64_t events;
110 };
111
117 Result Run();
118
119 private:
124 void Cb();
125
127 uint64_t m_population;
128 uint64_t m_total;
129 uint64_t m_count;
131}; // class Bench
132
135{
136 SystemWallClockMs timer;
137 double init;
138 double simu;
139
140 DEB("initializing");
141 m_count = 0;
142
143 timer.Start();
144 for (uint64_t i = 0; i < m_population; ++i)
145 {
147 Simulator::Schedule(at, &Bench::Cb, this);
148 }
149 init = timer.End() / 1000.0;
150 DEB("initialization took " << init << "s");
151
152 DEB("running");
153 timer.Start();
154 Simulator::Run();
155 simu = timer.End() / 1000.0;
156 DEB("run took " << simu << "s");
157
158 Simulator::Destroy();
159
160 return Result{init, simu, m_population, m_count};
161}
162
163void
165{
166 if (m_count >= m_total)
167 {
168 Simulator::Stop();
169 return;
170 }
171 DEB("event at " << Simulator::Now().GetSeconds() << "s");
172
173 Time after = NanoSeconds(m_rand->GetValue());
174 Simulator::Schedule(after, &Bench::Cb, this);
175 ++m_count;
176}
177
180{
181 public:
197 BenchSuite(ObjectFactory& factory,
198 uint64_t pop,
199 uint64_t total,
200 uint64_t runs,
201 Ptr<RandomVariableStream> eventStream,
202 bool calRev);
203
205 void Log() const;
206
207 private:
209 void Header() const;
210
213 {
214 double time;
215 double rate;
216 double period;
217 };
218
220 struct Result
221 {
230 static Result Bench(Bench::Result r);
231
238 template <typename T>
239 void Log(T label) const;
240 }; // struct Result
241
242 std::string m_scheduler;
243 std::vector<Result> m_results;
245}; // BenchSuite
246
247/* static */
250{
251 return Result{{r.init, r.pop / r.init, r.init / r.pop},
252 {r.simu, r.events / r.simu, r.simu / r.events}};
253}
254
255template <typename T>
256void
258{
259 // Need std::left for string labels
260
261 LOG(std::left << std::setw(g_fwidth) << label << std::setw(g_fwidth) << init.time
262 << std::setw(g_fwidth) << init.rate << std::setw(g_fwidth) << init.period
263 << std::setw(g_fwidth) << run.time << std::setw(g_fwidth) << run.rate
264 << std::setw(g_fwidth) << run.period);
265}
266
268 uint64_t pop,
269 uint64_t total,
270 uint64_t runs,
271 Ptr<RandomVariableStream> eventStream,
272 bool calRev)
273{
274 Simulator::SetScheduler(factory);
275
276 m_scheduler = factory.GetTypeId().GetName();
277 if (m_scheduler == "ns3::CalendarScheduler")
278 {
279 m_scheduler += ": insertion order: " + std::string(calRev ? "reverse" : "normal");
280 }
281 if (m_scheduler == "ns3::MapScheduler")
282 {
283 m_scheduler += " (default)";
284 }
285
286 Bench bench(pop, total);
287 bench.SetRandomStream(eventStream);
288 bench.SetPopulation(pop);
289 bench.SetTotal(total);
290
291 m_results.reserve(runs);
292 Header();
293
294 // Prime
295 DEB("priming");
296 auto prime = bench.Run();
297 Result::Bench(prime).Log("prime");
298
299 // Perform the actual runs
300 for (uint64_t i = 0; i < runs; i++)
301 {
302 auto run = bench.Run();
303 m_results.push_back(Result::Bench(run));
304 m_results.back().Log(i);
305 }
306
307 Simulator::Destroy();
308
309} // BenchSuite::Run
310
311void
313{
314 // table header
315 LOG("");
317 LOG(std::left << std::setw(g_fwidth) << "Run #" << std::left << std::setw(3 * g_fwidth)
318 << "Initialization:" << std::left << "Simulation:");
319 LOG(std::left << std::setw(g_fwidth) << "" << std::left << std::setw(g_fwidth) << "Time (s)"
320 << std::left << std::setw(g_fwidth) << "Rate (ev/s)" << std::left
321 << std::setw(g_fwidth) << "Per (s/ev)" << std::left << std::setw(g_fwidth)
322 << "Time (s)" << std::left << std::setw(g_fwidth) << "Rate (ev/s)" << std::left
323 << "Per (s/ev)");
324 LOG(std::setfill('-') << std::right << std::setw(g_fwidth) << " " << std::right
325 << std::setw(g_fwidth) << " " << std::right << std::setw(g_fwidth) << " "
326 << std::right << std::setw(g_fwidth) << " " << std::right
327 << std::setw(g_fwidth) << " " << std::right << std::setw(g_fwidth) << " "
328 << std::right << std::setw(g_fwidth) << " " << std::setfill(' '));
329}
330
331void
333{
334 if (m_results.size() < 2)
335 {
336 LOG("");
337 return;
338 }
339
340 // Average the results
341
342 // See Welford's online algorithm for these expressions,
343 // which avoid subtracting large numbers.
344 // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
345
346 uint64_t n{0}; // number of samples
347 Result average{m_results[0]}; // average
348 Result moment2{{0, 0, 0}, // 2nd moment, to calculate stdev
349 {0, 0, 0}};
350
351 for (; n < m_results.size(); ++n)
352 {
353 double deltaPre;
354 double deltaPost;
355 const auto& run = m_results[n];
356 uint64_t count = n + 1;
357
358#define ACCUMULATE(phase, field) \
359 deltaPre = run.phase.field - average.phase.field; \
360 average.phase.field += deltaPre / count; \
361 deltaPost = run.phase.field - average.phase.field; \
362 moment2.phase.field += deltaPre * deltaPost
363
364 ACCUMULATE(init, time);
365 ACCUMULATE(init, rate);
366 ACCUMULATE(init, period);
367 ACCUMULATE(run, time);
368 ACCUMULATE(run, rate);
369 ACCUMULATE(run, period);
370
371#undef ACCUMULATE
372 }
373
374 auto stdev = Result{{std::sqrt(moment2.init.time / n),
375 std::sqrt(moment2.init.rate / n),
376 std::sqrt(moment2.init.period / n)},
377 {std::sqrt(moment2.run.time / n),
378 std::sqrt(moment2.run.rate / n),
379 std::sqrt(moment2.run.period / n)}};
380
381 average.Log("average");
382 stdev.Log("stdev");
383
384 LOG("");
385
386} // BenchSuite::Log()
387
400GetRandomStream(std::string filename)
401{
402 Ptr<RandomVariableStream> stream = nullptr;
403
404 if (filename == "")
405 {
406 LOG(" Event time distribution: default exponential");
407 auto erv = CreateObject<ExponentialRandomVariable>();
408 erv->SetAttribute("Mean", DoubleValue(100));
409 stream = erv;
410 }
411 else
412 {
413 std::istream* input;
414
415 if (filename == "-")
416 {
417 LOG(" Event time distribution: from stdin");
418 input = &std::cin;
419 }
420 else
421 {
422 LOG(" Event time distribution: from " << filename);
423 input = new std::ifstream(filename);
424 }
425
426 double value;
427 std::vector<double> nsValues;
428
429 while (!input->eof())
430 {
431 if (*input >> value)
432 {
433 uint64_t ns = (uint64_t)(value * 1000000000);
434 nsValues.push_back(ns);
435 }
436 else
437 {
438 input->clear();
439 std::string line;
440 *input >> line;
441 }
442 }
443 LOG(" Found " << nsValues.size() << " entries");
444 auto drv = CreateObject<DeterministicRandomVariable>();
445 drv->SetValueArray(&nsValues[0], nsValues.size());
446 stream = drv;
447 }
448
449 return stream;
450}
451
452int
453main(int argc, char* argv[])
454{
455 bool allSched = false;
456 bool schedCal = false;
457 bool schedHeap = false;
458 bool schedList = false;
459 bool schedMap = false; // default scheduler
460 bool schedPQ = false;
461
462 uint64_t pop = 100000;
463 uint64_t total = 1000000;
464 uint64_t runs = 1;
465 std::string filename = "";
466 bool calRev = false;
467
468 CommandLine cmd(__FILE__);
469 cmd.Usage("Benchmark the simulator scheduler.\n"
470 "\n"
471 "Event intervals are taken from one of:\n"
472 " an exponential distribution, with mean 100 ns,\n"
473 " an ascii file, given by the --file=\"<filename>\" argument,\n"
474 " or standard input, by the argument --file=\"-\"\n"
475 "In the case of either --file form, the input is expected\n"
476 "to be ascii, giving the relative event times in ns.\n"
477 "\n"
478 "If no scheduler is specified the MapScheduler will be run.");
479 cmd.AddValue("all", "use all schedulers", allSched);
480 cmd.AddValue("cal", "use CalendarSheduler", schedCal);
481 cmd.AddValue("calrev", "reverse ordering in the CalendarScheduler", calRev);
482 cmd.AddValue("heap", "use HeapScheduler", schedHeap);
483 cmd.AddValue("list", "use ListSheduler", schedList);
484 cmd.AddValue("map", "use MapScheduler (default)", schedMap);
485 cmd.AddValue("pri", "use PriorityQueue", schedPQ);
486 cmd.AddValue("debug", "enable debugging output", g_debug);
487 cmd.AddValue("pop", "event population size", pop);
488 cmd.AddValue("total", "total number of events to run", total);
489 cmd.AddValue("runs", "number of runs", runs);
490 cmd.AddValue("file", "file of relative event times", filename);
491 cmd.AddValue("prec", "printed output precision", g_fwidth);
492 cmd.Parse(argc, argv);
493
494 g_me = cmd.GetName() + ": ";
495 g_fwidth += 6; // 5 extra chars in '2.000002e+07 ': . e+0 _
496
497 LOG(std::setprecision(g_fwidth - 6)); // prints blank line
498 LOGME(" Benchmark the simulator scheduler");
499 LOG(" Event population size: " << pop);
500 LOG(" Total events per run: " << total);
501 LOG(" Number of runs per scheduler: " << runs);
502 DEB("debugging is ON");
503
504 if (allSched)
505 {
506 schedCal = schedHeap = schedList = schedMap = schedPQ = true;
507 }
508 // Set the default case if nothing else is set
509 if (!(schedCal || schedHeap || schedList || schedMap || schedPQ))
510 {
511 schedMap = true;
512 }
513
514 auto eventStream = GetRandomStream(filename);
515
516 ObjectFactory factory("ns3::MapScheduler");
517 if (schedCal)
518 {
519 factory.SetTypeId("ns3::CalendarScheduler");
520 factory.Set("Reverse", BooleanValue(calRev));
521 BenchSuite(factory, pop, total, runs, eventStream, calRev).Log();
522 if (allSched)
523 {
524 factory.Set("Reverse", BooleanValue(!calRev));
525 BenchSuite(factory, pop, total, runs, eventStream, !calRev).Log();
526 }
527 }
528 if (schedHeap)
529 {
530 factory.SetTypeId("ns3::HeapScheduler");
531 BenchSuite(factory, pop, total, runs, eventStream, calRev).Log();
532 }
533 if (schedList)
534 {
535 factory.SetTypeId("ns3::ListScheduler");
536 auto listTotal = total;
537 if (allSched)
538 {
539 LOG("Running List scheduler with 1/10 total events");
540 listTotal /= 10;
541 }
542 BenchSuite(factory, pop, listTotal, runs, eventStream, calRev).Log();
543 }
544 if (schedMap)
545 {
546 factory.SetTypeId("ns3::MapScheduler");
547 BenchSuite(factory, pop, total, runs, eventStream, calRev).Log();
548 }
549 if (schedPQ)
550 {
551 factory.SetTypeId("ns3::PriorityQueueScheduler");
552 BenchSuite(factory, pop, total, runs, eventStream, calRev).Log();
553 }
554
555 return 0;
556}
Ptr< RandomVariableStream > GetRandomStream(std::string filename)
Create a RandomVariableStream to generate next event delays.
bool g_debug
Flag to write debugging output.
#define LOGME(x)
Log with program name prefix.
int g_fwidth
Output field width for numeric data.
std::string g_me
Name of this program.
#define ACCUMULATE(phase, field)
#define LOG(x)
Log to std::cout.
#define DEB(x)
Log debugging output.
Benchmark instance which can do a single run.
void SetRandomStream(Ptr< RandomVariableStream > stream)
Set the event delay interval random stream.
uint64_t m_population
Event population size.
void SetTotal(const uint64_t total)
Set the total number of events to execute.
Result Run()
Run the benchmark as configured.
Ptr< RandomVariableStream > m_rand
Stream for event delays.
uint64_t m_count
Count of events executed so far.
Bench(const uint64_t population, const uint64_t total)
Constructor.
void SetPopulation(const uint64_t population)
Set the number of events to populate the scheduler with.
uint64_t m_total
Total number of events to execute.
void Cb()
Event function.
Benchmark which performs an ensemble of runs.
void Log() const
Write the results to LOG()
BenchSuite(ObjectFactory &factory, uint64_t pop, uint64_t total, uint64_t runs, Ptr< RandomVariableStream > eventStream, bool calRev)
Perform the runs for a single scheduler type.
void Header() const
Print the table header.
std::vector< Result > m_results
Store for the run results.
std::string m_scheduler
Descriptive string for the scheduler.
AttributeValue implementation for Boolean.
Definition: boolean.h:37
Parse command-line arguments.
Definition: command-line.h:232
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition: double.h:42
Instantiate subclasses of ns3::Object.
TypeId GetTypeId() const
Get the TypeId which will be created by this ObjectFactory.
virtual double GetValue()=0
Get the next random value as a double drawn from the distribution.
Measure elapsed wall clock time in milliseconds.
int64_t End()
Stop measuring the time since Start() was called.
void Start()
Start a measure.
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
std::string GetName() const
Get the name.
Definition: type-id.cc:995
Time Now()
create an ns3::Time instance which contains the current simulation time.
Definition: simulator.cc:296
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1374
Every class exported by the ns3 library is enclosed in the ns3 namespace.
value
Definition: second.py:41
cmd
Definition: second.py:33
ns3::StringValue attribute value declarations.
uint64_t events
Number of events executed.
uint64_t pop
Event population.
double init
Time (s) for initialization.
double simu
Time (s) for simulation.
Statistics from a single phase, init or run.
double rate
Phase event rate (events/s).
double period
Phase period (s/event).
double time
Phase run time time (s).
Results from initialization and execution of a single run.
PhaseResult init
Initialization phase results.
static Result Bench(Bench::Result r)
Construct from the individual run result.
void Log(T label) const
Log this result.
PhaseResult run
Run (simulation) phase results.