A Discrete-Event Network Simulator
API
distributed-simulator-impl.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License version 2 as
5  * published by the Free Software Foundation;
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15  *
16  * Author: George Riley <riley@ece.gatech.edu>
17  *
18  */
19 
22 #include "mpi-interface.h"
23 
24 #include "ns3/simulator.h"
25 #include "ns3/scheduler.h"
26 #include "ns3/event-impl.h"
27 #include "ns3/channel.h"
28 #include "ns3/node-container.h"
29 #include "ns3/ptr.h"
30 #include "ns3/pointer.h"
31 #include "ns3/assert.h"
32 #include "ns3/log.h"
33 
34 #include <mpi.h>
35 #include <cmath>
36 
37 namespace ns3 {
38 
39 NS_LOG_COMPONENT_DEFINE ("DistributedSimulatorImpl");
40 
41 NS_OBJECT_ENSURE_REGISTERED (DistributedSimulatorImpl);
42 
44 {
45 }
46 
47 Time
49 {
50  return m_smallestTime;
51 }
52 
53 uint32_t
55 {
56  return m_txCount;
57 }
58 
59 uint32_t
61 {
62  return m_rxCount;
63 }
64 uint32_t
66 {
67  return m_myId;
68 }
69 
70 bool
72 {
73  return m_isFinished;
74 }
75 
77 
78 TypeId
80 {
81  static TypeId tid = TypeId ("ns3::DistributedSimulatorImpl")
83  .SetGroupName ("Mpi")
84  .AddConstructor<DistributedSimulatorImpl> ()
85  ;
86  return tid;
87 }
88 
90 {
91  NS_LOG_FUNCTION (this);
92 
95 
96  // Allocate the LBTS message buffer
98  m_grantedTime = Seconds (0);
99 
100  m_stop = false;
101  m_globalFinished = false;
102  // uids are allocated from 4.
103  // uid 0 is "invalid" events
104  // uid 1 is "now" events
105  // uid 2 is "destroy" events
106  m_uid = 4;
107  // before ::Run is entered, the m_currentUid will be zero
108  m_currentUid = 0;
109  m_currentTs = 0;
112  m_eventCount = 0;
113  m_events = 0;
114 }
115 
117 {
118  NS_LOG_FUNCTION (this);
119 }
120 
121 void
123 {
124  NS_LOG_FUNCTION (this);
125 
126  while (!m_events->IsEmpty ())
127  {
128  Scheduler::Event next = m_events->RemoveNext ();
129  next.impl->Unref ();
130  }
131  m_events = 0;
132  delete [] m_pLBTS;
134 }
135 
136 void
138 {
139  NS_LOG_FUNCTION (this);
140 
141  while (!m_destroyEvents.empty ())
142  {
143  Ptr<EventImpl> ev = m_destroyEvents.front ().PeekEventImpl ();
144  m_destroyEvents.pop_front ();
145  NS_LOG_LOGIC ("handle destroy " << ev);
146  if (!ev->IsCancelled ())
147  {
148  ev->Invoke ();
149  }
150  }
151 
153 }
154 
155 
156 void
158 {
159  NS_LOG_FUNCTION (this);
160 
161  if (MpiInterface::GetSize () <= 1)
162  {
163  m_lookAhead = Seconds (0);
164  }
165  else
166  {
167  if (m_lookAhead == Seconds (-1))
168  {
170  }
171  // else it was already set by SetLookAhead
172 
174  for (NodeContainer::Iterator iter = c.Begin (); iter != c.End (); ++iter)
175  {
176  if ((*iter)->GetSystemId () != MpiInterface::GetSystemId ())
177  {
178  continue;
179  }
180 
181  for (uint32_t i = 0; i < (*iter)->GetNDevices (); ++i)
182  {
183  Ptr<NetDevice> localNetDevice = (*iter)->GetDevice (i);
184  // only works for p2p links currently
185  if (!localNetDevice->IsPointToPoint ())
186  {
187  continue;
188  }
189  Ptr<Channel> channel = localNetDevice->GetChannel ();
190  if (channel == 0)
191  {
192  continue;
193  }
194 
195  // grab the adjacent node
196  Ptr<Node> remoteNode;
197  if (channel->GetDevice (0) == localNetDevice)
198  {
199  remoteNode = (channel->GetDevice (1))->GetNode ();
200  }
201  else
202  {
203  remoteNode = (channel->GetDevice (0))->GetNode ();
204  }
205 
206  // if it's not remote, don't consider it
207  if (remoteNode->GetSystemId () == MpiInterface::GetSystemId ())
208  {
209  continue;
210  }
211 
212  // compare delay on the channel with current value of
213  // m_lookAhead. if delay on channel is smaller, make
214  // it the new lookAhead.
215  TimeValue delay;
216  channel->GetAttribute ("Delay", delay);
217 
218  if (delay.Get () < m_lookAhead)
219  {
220  m_lookAhead = delay.Get ();
221  }
222  }
223  }
224  }
225 
226  // m_lookAhead is now set
228 
229  /*
230  * Compute the maximum inter-task latency and use that value
231  * for tasks with no inter-task links.
232  *
233  * Special processing for edge cases. For tasks that have no
234  * nodes need to determine a reasonable lookAhead value. Infinity
235  * would work correctly but introduces a performance issue; tasks
236  * with an infinite lookAhead would execute all their events
237  * before doing an AllGather resulting in very bad load balance
238  * during the first time window. Since all tasks participate in
239  * the AllGather it is desirable to have all the tasks advance in
240  * simulation time at a similar rate assuming roughly equal events
241  * per unit of simulation time in order to equalize the amount of
242  * work per time window.
243  */
244  long sendbuf;
245  long recvbuf;
246 
247  /* Tasks with no inter-task links do not contribute to max */
249  {
250  sendbuf = 0;
251  }
252  else
253  {
254  sendbuf = m_lookAhead.GetInteger ();
255  }
256 
257  MPI_Allreduce (&sendbuf, &recvbuf, 1, MPI_LONG, MPI_MAX, MPI_COMM_WORLD);
258 
259  /* For nodes that did not compute a lookahead use max from ranks
260  * that did compute a value. An edge case occurs if all nodes have
261  * no inter-task links (max will be 0 in this case). Use infinity so all tasks
262  * will proceed without synchronization until a single AllGather
263  * occurs when all tasks have finished.
264  */
265  if (m_lookAhead == GetMaximumSimulationTime () && recvbuf != 0)
266  {
267  m_lookAhead = Time (recvbuf);
269  }
270 }
271 
272 void
274 {
275  if (lookAhead > Time (0))
276  {
277  NS_LOG_FUNCTION (this << lookAhead);
278  m_lookAhead = lookAhead;
279  }
280  else
281  {
282  NS_LOG_WARN ("attempted to set look ahead negative: " << lookAhead);
283  }
284 }
285 
286 void
288 {
289  NS_LOG_FUNCTION (this << schedulerFactory);
290 
291  Ptr<Scheduler> scheduler = schedulerFactory.Create<Scheduler> ();
292 
293  if (m_events != 0)
294  {
295  while (!m_events->IsEmpty ())
296  {
297  Scheduler::Event next = m_events->RemoveNext ();
298  scheduler->Insert (next);
299  }
300  }
301  m_events = scheduler;
302 }
303 
304 void
306 {
307  NS_LOG_FUNCTION (this);
308 
309  Scheduler::Event next = m_events->RemoveNext ();
310 
311  NS_ASSERT (next.key.m_ts >= m_currentTs);
313  m_eventCount++;
314 
315  NS_LOG_LOGIC ("handle " << next.key.m_ts);
316  m_currentTs = next.key.m_ts;
318  m_currentUid = next.key.m_uid;
319  next.impl->Invoke ();
320  next.impl->Unref ();
321 }
322 
323 bool
325 {
326  return m_globalFinished;
327 }
328 
329 bool
331 {
332  return m_events->IsEmpty () || m_stop;
333 }
334 
335 uint64_t
337 {
338  // If local MPI task is has no more events or stop was called
339  // next event time is infinity.
340  if (IsLocalFinished ())
341  {
343  }
344  else
345  {
346  Scheduler::Event ev = m_events->PeekNext ();
347  return ev.key.m_ts;
348  }
349 }
350 
351 Time
353 {
354  return TimeStep (NextTs ());
355 }
356 
357 void
359 {
360  NS_LOG_FUNCTION (this);
361 
363  m_stop = false;
364  m_globalFinished = false;
365  while (!m_globalFinished)
366  {
367  Time nextTime = Next ();
368 
369  // If local event is beyond grantedTime then need to synchronize
370  // with other tasks to determine new time window. If local task
371  // is finished then continue to participate in allgather
372  // synchronizations with other tasks until all tasks have
373  // completed.
374  if (nextTime > m_grantedTime || IsLocalFinished () )
375  {
376  // Can't process next event, calculate a new LBTS
377  // First receive any pending messages
379  // reset next time
380  nextTime = Next ();
381  // And check for send completes
383  // Finally calculate the lbts
385  m_myId, IsLocalFinished (), nextTime);
386  m_pLBTS[m_myId] = lMsg;
387  MPI_Allgather (&lMsg, sizeof (LbtsMessage), MPI_BYTE, m_pLBTS,
388  sizeof (LbtsMessage), MPI_BYTE, MPI_COMM_WORLD);
389  Time smallestTime = m_pLBTS[0].GetSmallestTime ();
390  // The totRx and totTx counts insure there are no transient
391  // messages; If totRx != totTx, there are transients,
392  // so we don't update the granted time.
393  uint32_t totRx = m_pLBTS[0].GetRxCount ();
394  uint32_t totTx = m_pLBTS[0].GetTxCount ();
396 
397  for (uint32_t i = 1; i < m_systemCount; ++i)
398  {
399  if (m_pLBTS[i].GetSmallestTime () < smallestTime)
400  {
401  smallestTime = m_pLBTS[i].GetSmallestTime ();
402  }
403  totRx += m_pLBTS[i].GetRxCount ();
404  totTx += m_pLBTS[i].GetTxCount ();
406  }
407  if (totRx == totTx)
408  {
409  // If lookahead is infinite then granted time should be as well.
410  // Covers the edge case if all the tasks have no inter tasks
411  // links, prevents overflow of granted time.
413  {
415  }
416  else
417  {
418  // Overflow is possible here if near end of representable time.
419  m_grantedTime = smallestTime + m_lookAhead;
420  }
421  }
422  }
423 
424  // Execute next event if it is within the current time window.
425  // Local task may be completed.
426  if ( (nextTime <= m_grantedTime) && (!IsLocalFinished ()) )
427  { // Safe to process
428  ProcessOneEvent ();
429  }
430  }
431 
432  // If the simulator stopped naturally by lack of events, make a
433  // consistency test to check that we didn't lose any events along the way.
434  NS_ASSERT (!m_events->IsEmpty () || m_unscheduledEvents == 0);
435 }
436 
438 {
439  return m_myId;
440 }
441 
442 void
444 {
445  NS_LOG_FUNCTION (this);
446 
447  m_stop = true;
448 }
449 
450 void
452 {
453  NS_LOG_FUNCTION (this << delay.GetTimeStep ());
454 
456 }
457 
458 //
459 // Schedule an event for a _relative_ time in the future.
460 //
461 EventId
463 {
464  NS_LOG_FUNCTION (this << delay.GetTimeStep () << event);
465 
466  Time tAbsolute = delay + TimeStep (m_currentTs);
467 
468  NS_ASSERT (tAbsolute.IsPositive ());
469  NS_ASSERT (tAbsolute >= TimeStep (m_currentTs));
470  Scheduler::Event ev;
471  ev.impl = event;
472  ev.key.m_ts = static_cast<uint64_t> (tAbsolute.GetTimeStep ());
473  ev.key.m_context = GetContext ();
474  ev.key.m_uid = m_uid;
475  m_uid++;
477  m_events->Insert (ev);
478  return EventId (event, ev.key.m_ts, ev.key.m_context, ev.key.m_uid);
479 }
480 
481 void
482 DistributedSimulatorImpl::ScheduleWithContext (uint32_t context, Time const &delay, EventImpl *event)
483 {
484  NS_LOG_FUNCTION (this << context << delay.GetTimeStep () << m_currentTs << event);
485 
486  Scheduler::Event ev;
487  ev.impl = event;
488  ev.key.m_ts = m_currentTs + delay.GetTimeStep ();
489  ev.key.m_context = context;
490  ev.key.m_uid = m_uid;
491  m_uid++;
493  m_events->Insert (ev);
494 }
495 
496 EventId
498 {
499  NS_LOG_FUNCTION (this << event);
500 
501  Scheduler::Event ev;
502  ev.impl = event;
503  ev.key.m_ts = m_currentTs;
504  ev.key.m_context = GetContext ();
505  ev.key.m_uid = m_uid;
506  m_uid++;
508  m_events->Insert (ev);
509  return EventId (event, ev.key.m_ts, ev.key.m_context, ev.key.m_uid);
510 }
511 
512 EventId
514 {
515  NS_LOG_FUNCTION (this << event);
516 
517  EventId id (Ptr<EventImpl> (event, false), m_currentTs, 0xffffffff, 2);
518  m_destroyEvents.push_back (id);
519  m_uid++;
520  return id;
521 }
522 
523 Time
525 {
526  return TimeStep (m_currentTs);
527 }
528 
529 Time
531 {
532  if (IsExpired (id))
533  {
534  return TimeStep (0);
535  }
536  else
537  {
538  return TimeStep (id.GetTs () - m_currentTs);
539  }
540 }
541 
542 void
544 {
545  if (id.GetUid () == 2)
546  {
547  // destroy events.
548  for (DestroyEvents::iterator i = m_destroyEvents.begin (); i != m_destroyEvents.end (); i++)
549  {
550  if (*i == id)
551  {
552  m_destroyEvents.erase (i);
553  break;
554  }
555  }
556  return;
557  }
558  if (IsExpired (id))
559  {
560  return;
561  }
562  Scheduler::Event event;
563  event.impl = id.PeekEventImpl ();
564  event.key.m_ts = id.GetTs ();
565  event.key.m_context = id.GetContext ();
566  event.key.m_uid = id.GetUid ();
567  m_events->Remove (event);
568  event.impl->Cancel ();
569  // whenever we remove an event from the event list, we have to unref it.
570  event.impl->Unref ();
571 
573 }
574 
575 void
577 {
578  if (!IsExpired (id))
579  {
580  id.PeekEventImpl ()->Cancel ();
581  }
582 }
583 
584 bool
586 {
587  if (id.GetUid () == 2)
588  {
589  if (id.PeekEventImpl () == 0
590  || id.PeekEventImpl ()->IsCancelled ())
591  {
592  return true;
593  }
594  // destroy events.
595  for (DestroyEvents::const_iterator i = m_destroyEvents.begin (); i != m_destroyEvents.end (); i++)
596  {
597  if (*i == id)
598  {
599  return false;
600  }
601  }
602  return true;
603  }
604  if (id.PeekEventImpl () == 0
605  || id.GetTs () < m_currentTs
606  || (id.GetTs () == m_currentTs
607  && id.GetUid () <= m_currentUid)
608  || id.PeekEventImpl ()->IsCancelled ())
609  {
610  return true;
611  }
612  else
613  {
614  return false;
615  }
616 }
617 
618 Time
620 {
623  return TimeStep (0x7fffffffffffffffLL);
624 }
625 
626 uint32_t
628 {
629  return m_currentContext;
630 }
631 
632 uint64_t
634 {
635  return m_eventCount;
636 }
637 
638 } // namespace ns3
static EventId Schedule(Time const &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition: simulator.h:557
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:103
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:73
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by "...
virtual void SetScheduler(ObjectFactory schedulerFactory)
Set the Scheduler to be used to manage the event list.
virtual uint32_t GetContext(void) const
Get the current simulation context.
void Unref(void) const
Decrement the reference count.
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:45
int64_t GetInteger(void) const
Get the raw time value, in the current resolution unit.
Definition: nstime.h:423
std::vector< Ptr< Node > >::const_iterator Iterator
Node container iterator.
uint64_t m_ts
Event time stamp.
Definition: scheduler.h:170
virtual EventId Schedule(Time const &delay, EventImpl *event)
Schedule a future event execution (in the same context).
EventImpl * impl
Pointer to the event implementation.
Definition: scheduler.h:183
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file...
Definition: assert.h:67
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
virtual EventId ScheduleDestroy(EventImpl *event)
Schedule an event to run at the end of the simulation, after the Stop() time or condition has been re...
void(* Time)(Time oldValue, Time newValue)
TracedValue callback signature for Time.
Definition: nstime.h:802
virtual void DoDispose(void)
Destructor implementation.
Definition: object.cc:346
virtual void SetMaximumLookAhead(const Time lookAhead)
Iterator End(void) const
Get an iterator which indicates past-the-last Node in the container.
uint64_t m_eventCount
The event count.
virtual Time GetDelayLeft(const EventId &id) const
Get the remaining time until this event will execute.
virtual void DoDispose(void)
Destructor implementation.
channel
Definition: third.py:92
void Invoke(void)
Called by the simulation engine to notify the event that it is time to execute.
Definition: event-impl.cc:46
virtual Time GetMaximumSimulationTime(void) const
Get the maximum representable simulation time.
static void TestSendComplete()
Check for completed sends.
EventKey key
Key for sorting and ordering Events.
Definition: scheduler.h:184
virtual bool IsFinished(void) const
Check if the simulation should finish.
static void Destroy()
Deletes storage used by the parallel environment.
AttributeValue implementation for Time.
Definition: nstime.h:1342
Ptr< Object > Create(void) const
Create an Object instance of the configured TypeId.
uint32_t m_uid
Event unique id.
Definition: scheduler.h:171
virtual void Cancel(const EventId &id)
Set the cancel bit on this event: the event&#39;s associated function will not be invoked when it expires...
virtual EventId ScheduleNow(EventImpl *event)
Schedule an event to run at the current virtual time.
virtual uint64_t GetEventCount(void) const
Get the number of events executed.
Maintain the event list.
Definition: scheduler.h:155
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:289
virtual bool IsExpired(const EventId &id) const
Check if an event has already run or been cancelled.
Scheduler event.
Definition: scheduler.h:181
Distributed simulator implementation using lookahead.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
keep track of a set of node pointers.
virtual uint32_t GetSystemId(void) const
Get the system id of this simulator.
virtual void Destroy()
Execute the events scheduled with ScheduleDestroy().
uint32_t GetSystemId(void) const
Definition: node.cc:123
Structure used for all-reduce LBTS computation.
static NodeContainer GetGlobal(void)
Create a NodeContainer that contains a list of all nodes created through NodeContainer::Create() and ...
virtual void Remove(const EventId &id)
Remove an event from the event list.
Instantiate subclasses of ns3::Object.
A simulation event.
Definition: event-impl.h:44
static void ReceiveMessages()
Check for received messages complete.
An identifier for simulation events.
Definition: event-id.h:53
static uint32_t GetSystemId()
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:265
static void Stop(void)
Tell the Simulator the calling event should be the last one executed.
Definition: simulator.cc:180
Time Get(void) const
Definition: time.cc:469
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1278
Flag for events not associated with any particular context.
Definition: simulator.h:199
virtual void ScheduleWithContext(uint32_t context, Time const &delay, EventImpl *event)
Schedule a future event execution (in a different context).
virtual Time Now(void) const
Return the current simulation virtual time.
virtual void Run(void)
Run the simulation.
virtual void Stop(void)
Tell the Simulator the calling event should be the last one executed.
a unique identifier for an interface.
Definition: type-id.h:58
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:923
static uint32_t GetSize()
uint32_t m_context
Event context.
Definition: scheduler.h:172
Iterator Begin(void) const
Get an iterator which refers to the first Node in the container.
The SimulatorImpl base class.
int64_t GetTimeStep(void) const
Get the raw time value, in the current resolution unit.
Definition: nstime.h:415