A Discrete-Event Network Simulator
API
simple-distributed-mpi-comm.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright 2018. Lawrence Livermore National Security, LLC.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * Author: Steven Smith <smith84@llnl.gov>
19  */
20 
58 #include "mpi-test-fixtures.h"
59 
60 #include "ns3/core-module.h"
61 #include "ns3/network-module.h"
62 #include "ns3/mpi-interface.h"
63 #include "ns3/ipv4-global-routing-helper.h"
64 #include "ns3/ipv4-static-routing-helper.h"
65 #include "ns3/ipv4-list-routing-helper.h"
66 #include "ns3/point-to-point-helper.h"
67 #include "ns3/internet-stack-helper.h"
68 #include "ns3/ipv4-nix-vector-helper.h"
69 #include "ns3/ipv4-address-helper.h"
70 #include "ns3/on-off-helper.h"
71 #include "ns3/packet-sink.h"
72 #include "ns3/packet-sink-helper.h"
73 
74 #include "mpi.h"
75 
76 using namespace ns3;
77 
78 NS_LOG_COMPONENT_DEFINE ("SimpleDistributedMpiComm");
79 
80 // Tag for whether this rank should go into a new communicator
81 // ns-3 ranks will have color == 1.
82 const int NS_COLOR = 1;
83 const int NOT_NS_COLOR = NS_COLOR + 1;
84 
91 void
92 ReportRank (int color, MPI_Comm splitComm)
93 {
94  int otherId=0;
95  int otherSize=1;
96 
97  MPI_Comm_rank (splitComm, &otherId);
98  MPI_Comm_size (splitComm, &otherSize);
99 
100  if (color == NS_COLOR)
101  {
102  RANK0COUT ( "ns-3 rank: ");
103  }
104  else
105  {
106  RANK0COUT ( "Other rank: ");
107  }
108 
109  RANK0COUTAPPEND ( "in MPI_COMM_WORLD: " << SinkTracer::GetWorldRank () << ":" << SinkTracer::GetWorldSize ()
110  << ", in splitComm: " << otherId << ":" << otherSize
111  << std::endl);
112 
113 } // ReportRank()
114 
115 int
116 main (int argc, char *argv[])
117 {
118  bool nix = true;
119  bool nullmsg = false;
120  bool tracing = false;
121  bool init = false;
122  bool verbose = false;
123  bool testing = false;
124 
125  // Parse command line
127  cmd.AddValue ("nix", "Enable the use of nix-vector or global routing", nix);
128  cmd.AddValue ("nullmsg", "Enable the use of null-message synchronization (instead of granted time window)", nullmsg);
129  cmd.AddValue ("tracing", "Enable pcap tracing", tracing);
130  cmd.AddValue ("init", "ns-3 should initialize MPI by calling MPI_Init", init);
131  cmd.AddValue ("verbose", "verbose output", verbose);
132  cmd.AddValue ("test", "Enable regression test output", testing);
133  cmd.Parse (argc, argv);
134 
135  // Defer reporting the configuration until we know the communicator
136 
137  // Distributed simulation setup; by default use granted time window algorithm.
138  if(nullmsg)
139  {
140  GlobalValue::Bind ("SimulatorImplementationType",
141  StringValue ("ns3::NullMessageSimulatorImpl"));
142  }
143  else
144  {
145  GlobalValue::Bind ("SimulatorImplementationType",
146  StringValue ("ns3::DistributedSimulatorImpl"));
147  }
148 
149  // MPI_Init
150 
151  if (init)
152  {
153  // Initialize MPI directly
154  MPI_Init(&argc, &argv);
155  }
156  else
157  {
158  // Let ns-3 call MPI_Init and MPI_Finalize
159  MpiInterface::Enable (&argc, &argv);
160  }
161 
162  SinkTracer::Init ();
163 
164  auto worldSize = SinkTracer::GetWorldSize ();
165  auto worldRank = SinkTracer::GetWorldRank ();
166 
167  if ( (!init) && (worldSize != 2))
168  {
169  RANK0COUT ("This simulation requires exactly 2 logical processors if --init is not set." << std::endl);
170  return 1;
171  }
172 
173  if (worldSize < 2)
174  {
175  RANK0COUT ("This simulation requires 2 or more logical processors." << std::endl);
176  return 1;
177  }
178 
179  // Set up the MPI communicator for ns-3
180  // Condition ns-3 Communicator
181  // a. worldSize = 2 copy of MPI_COMM_WORLD
182  // b. worldSize > 2 communicator of ranks 1-2
183 
184  // Flag to record that we created a communicator so we can free it at the end.
185  bool freeComm = false;
186  // The new communicator, if we create one
187  MPI_Comm splitComm = MPI_COMM_WORLD;
188  // The list of ranks assigned to ns-3
189  std::string ns3Ranks;
190  // Tag for whether this rank should go into a new communicator
191  int color = MPI_UNDEFINED;
192 
193 
194  if (worldSize == 2)
195  {
196  std::stringstream ss;
197  color = NS_COLOR;
198  ss << "MPI_COMM_WORLD (" << worldSize << " ranks)";
199  ns3Ranks = ss.str ();
200  splitComm = MPI_COMM_WORLD;
201  freeComm = false;
202  }
203  else
204  {
205  // worldSize > 2 communicator of ranks 1-2
206 
207  // Put ranks 1-2 in the new communicator
208  if (worldRank == 1 || worldRank == 2)
209  {
210  color = NS_COLOR;
211  }
212  else
213  {
214  color = NOT_NS_COLOR;
215  }
216  std::stringstream ss;
217  ss << "Split [1-2] (out of " << worldSize << " ranks) from MPI_COMM_WORLD";
218  ns3Ranks = ss.str ();
219 
220  // Now create the new communicator
221  MPI_Comm_split (MPI_COMM_WORLD, color, worldRank, &splitComm);
222  freeComm = true;
223  }
224 
225 
226  if(init)
227  {
228  MpiInterface::Enable (splitComm);
229  }
230 
231  // Report the configuration from rank 0 only
232  RANK0COUT (cmd.GetName () << "\n");
233  RANK0COUT ("\n" );
234  RANK0COUT ("Configuration:\n" );
235  RANK0COUT ("Routing: " << (nix ? "nix-vector" : "global") << "\n");
236  RANK0COUT ("Synchronization: " << (nullmsg ? "null-message" : "granted time window (YAWNS)") << "\n");
237  RANK0COUT ("MPI_Init called: " << (init ? "explicitly by this program" : "implicitly by ns3::MpiInterface::Enable()") << "\n" );
238  RANK0COUT ("ns-3 Communicator: " << ns3Ranks << "\n");
239  RANK0COUT ("PCAP tracing: " << (tracing ? "" : "not") << " enabled\n");
240  RANK0COUT ("\n");
241  RANK0COUT ("Rank assignments:" << std::endl);
242 
243  if (worldRank == 0)
244  {
245  ReportRank (color, splitComm);
246  }
247 
248  if(verbose)
249  {
250  // Circulate a token to have each rank report in turn
251  int token;
252 
253  if (worldRank == 0)
254  {
255  token = 1;
256  }
257  else
258  {
259  MPI_Recv (&token, 1, MPI_INT, worldRank - 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
260  ReportRank (color, splitComm);
261  }
262 
263  MPI_Send (&token, 1, MPI_INT, (worldRank + 1) % worldSize, 0, MPI_COMM_WORLD);
264 
265  if (worldRank == 0)
266  {
267  MPI_Recv (&token, 1, MPI_INT, worldSize - 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
268  }
269  } // circulate token to report rank
270 
271  RANK0COUT (std::endl);
272 
273  if (color != NS_COLOR)
274  {
275  // Do other work outside the ns-3 communicator
276 
277  // In real use of a separate communicator from ns-3
278  // the other tasks would be running another simulator
279  // or other desired work here..
280 
281  // Our work is done, just wait for everyone else to finish.
282 
284 
285  if(init)
286  {
287  MPI_Finalize ();
288  }
289 
290  return 0;
291  }
292 
293  // The code below here is essentially the same as simple-distributed.cc
294  // --------------------------------------------------------------------
295 
296  // We use a trace instead of relying on NS_LOG
297 
298  if (verbose)
299  {
300  LogComponentEnable ("PacketSink", LOG_LEVEL_INFO);
301  }
302 
303  uint32_t systemId = MpiInterface::GetSystemId ();
304  uint32_t systemCount = MpiInterface::GetSize ();
305 
306  // Check for valid distributed parameters.
307  // Both this script and simple-distributed.cc will work
308  // with arbitrary numbers of ranks, as long as there are at least 2.
309  if (systemCount < 2)
310  {
311  RANK0COUT ("This simulation requires at least 2 logical processors." << std::endl);
312  return 1;
313  }
314 
315  // Some default values
316  Config::SetDefault ("ns3::OnOffApplication::PacketSize", UintegerValue (512));
317  Config::SetDefault ("ns3::OnOffApplication::DataRate", StringValue ("1Mbps"));
318  Config::SetDefault ("ns3::OnOffApplication::MaxBytes", UintegerValue (512));
319 
320  // Create leaf nodes on left with system id 0
321  NodeContainer leftLeafNodes;
322  leftLeafNodes.Create (4, 0);
323 
324  // Create router nodes. Left router
325  // with system id 0, right router with
326  // system id 1
327  NodeContainer routerNodes;
328  Ptr<Node> routerNode1 = CreateObject<Node> (0);
329  Ptr<Node> routerNode2 = CreateObject<Node> (1);
330  routerNodes.Add (routerNode1);
331  routerNodes.Add (routerNode2);
332 
333  // Create leaf nodes on left with system id 1
334  NodeContainer rightLeafNodes;
335  rightLeafNodes.Create (4, 1);
336 
337  PointToPointHelper routerLink;
338  routerLink.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
339  routerLink.SetChannelAttribute ("Delay", StringValue ("5ms"));
340 
341  PointToPointHelper leafLink;
342  leafLink.SetDeviceAttribute ("DataRate", StringValue ("1Mbps"));
343  leafLink.SetChannelAttribute ("Delay", StringValue ("2ms"));
344 
345  // Add link connecting routers
346  NetDeviceContainer routerDevices;
347  routerDevices = routerLink.Install (routerNodes);
348 
349  // Add links for left side leaf nodes to left router
350  NetDeviceContainer leftRouterDevices;
351  NetDeviceContainer leftLeafDevices;
352  for (uint32_t i = 0; i < 4; ++i)
353  {
354  NetDeviceContainer temp = leafLink.Install (leftLeafNodes.Get (i), routerNodes.Get (0));
355  leftLeafDevices.Add (temp.Get (0));
356  leftRouterDevices.Add (temp.Get (1));
357  }
358 
359  // Add links for right side leaf nodes to right router
360  NetDeviceContainer rightRouterDevices;
361  NetDeviceContainer rightLeafDevices;
362  for (uint32_t i = 0; i < 4; ++i)
363  {
364  NetDeviceContainer temp = leafLink.Install (rightLeafNodes.Get (i), routerNodes.Get (1));
365  rightLeafDevices.Add (temp.Get (0));
366  rightRouterDevices.Add (temp.Get (1));
367  }
368 
370  Ipv4NixVectorHelper nixRouting;
371  Ipv4StaticRoutingHelper staticRouting;
372 
374  list.Add (staticRouting, 0);
375  list.Add (nixRouting, 10);
376 
377  if (nix)
378  {
379  stack.SetRoutingHelper (list); // has effect on the next Install ()
380  }
381 
382  stack.InstallAll ();
383 
384  Ipv4InterfaceContainer routerInterfaces;
385  Ipv4InterfaceContainer leftLeafInterfaces;
386  Ipv4InterfaceContainer leftRouterInterfaces;
387  Ipv4InterfaceContainer rightLeafInterfaces;
388  Ipv4InterfaceContainer rightRouterInterfaces;
389 
390  Ipv4AddressHelper leftAddress;
391  leftAddress.SetBase ("10.1.1.0", "255.255.255.0");
392 
393  Ipv4AddressHelper routerAddress;
394  routerAddress.SetBase ("10.2.1.0", "255.255.255.0");
395 
396  Ipv4AddressHelper rightAddress;
397  rightAddress.SetBase ("10.3.1.0", "255.255.255.0");
398 
399  // Router-to-Router interfaces
400  routerInterfaces = routerAddress.Assign (routerDevices);
401 
402  // Left interfaces
403  for (uint32_t i = 0; i < 4; ++i)
404  {
405  NetDeviceContainer ndc;
406  ndc.Add (leftLeafDevices.Get (i));
407  ndc.Add (leftRouterDevices.Get (i));
408  Ipv4InterfaceContainer ifc = leftAddress.Assign (ndc);
409  leftLeafInterfaces.Add (ifc.Get (0));
410  leftRouterInterfaces.Add (ifc.Get (1));
411  leftAddress.NewNetwork ();
412  }
413 
414  // Right interfaces
415  for (uint32_t i = 0; i < 4; ++i)
416  {
417  NetDeviceContainer ndc;
418  ndc.Add (rightLeafDevices.Get (i));
419  ndc.Add (rightRouterDevices.Get (i));
420  Ipv4InterfaceContainer ifc = rightAddress.Assign (ndc);
421  rightLeafInterfaces.Add (ifc.Get (0));
422  rightRouterInterfaces.Add (ifc.Get (1));
423  rightAddress.NewNetwork ();
424  }
425 
426  if (!nix)
427  {
429  }
430 
431  if (tracing == true)
432  {
433  if (systemId == 0)
434  {
435  routerLink.EnablePcap("router-left", routerDevices, true);
436  leafLink.EnablePcap("leaf-left", leftLeafDevices, true);
437  }
438 
439  if (systemId == 1)
440  {
441  routerLink.EnablePcap("router-right", routerDevices, true);
442  leafLink.EnablePcap("leaf-right", rightLeafDevices, true);
443  }
444  }
445 
446  // Create a packet sink on the right leafs to receive packets from left leafs
447  uint16_t port = 50000;
448  if (systemId == 1)
449  {
450  Address sinkLocalAddress (InetSocketAddress (Ipv4Address::GetAny (), port));
451  PacketSinkHelper sinkHelper ("ns3::UdpSocketFactory", sinkLocalAddress);
452  ApplicationContainer sinkApp;
453  for (uint32_t i = 0; i < 4; ++i)
454  {
455  auto apps = sinkHelper.Install (rightLeafNodes.Get (i));
456  auto sink = DynamicCast<PacketSink> (apps.Get (0));
457  NS_ASSERT_MSG (sink, "Couldn't get PacketSink application.");
458  if (testing)
459  {
461  }
462  sinkApp.Add (apps);
463  }
464  sinkApp.Start (Seconds (1.0));
465  sinkApp.Stop (Seconds (5));
466  }
467 
468  // Create the OnOff applications to send
469  if (systemId == 0)
470  {
471  OnOffHelper clientHelper ("ns3::UdpSocketFactory", Address ());
472  clientHelper.SetAttribute
473  ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1]"));
474  clientHelper.SetAttribute
475  ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0]"));
476 
478  for (uint32_t i = 0; i < 4; ++i)
479  {
480  AddressValue remoteAddress
481  (InetSocketAddress (rightLeafInterfaces.GetAddress (i), port));
482  clientHelper.SetAttribute ("Remote", remoteAddress);
483  clientApps.Add (clientHelper.Install (leftLeafNodes.Get (i)));
484  }
485  clientApps.Start (Seconds (1.0));
486  clientApps.Stop (Seconds (5));
487  }
488 
489  RANK0COUT (std::endl);
490 
491  Simulator::Stop (Seconds (5));
492  Simulator::Run ();
494 
495  // --------------------------------------------------------------------
496  // Conditional cleanup based on whether we built a communicator
497  // and called MPI_Init
498 
499  if (freeComm)
500  {
501  MPI_Comm_free (&splitComm);
502  }
503 
504  if (testing)
505  {
506  SinkTracer::Verify (4);
507  }
508 
509  // Clean up the ns-3 MPI execution environment
510  // This will call MPI_Finalize if MpiInterface::Initialize was called
512 
513  if (init)
514  {
515  // We called MPI_Init, so we have to call MPI_Finalize
516  MPI_Finalize ();
517  }
518 
519  return 0;
520 }
a polymophic address class
Definition: address.h:91
AttributeValue implementation for Address.
Definition: address.h:278
holds a vector of ns3::Application pointers.
void Start(Time start)
Arrange for all of the Applications in this container to Start() at the Time given as a parameter.
void Add(ApplicationContainer other)
Append the contents of another ApplicationContainer to the end of this container.
void Stop(Time stop)
Arrange for all of the Applications in this container to Stop() at the Time given as a parameter.
Parse command-line arguments.
Definition: command-line.h:228
static void Bind(std::string name, const AttributeValue &value)
Iterate over the set of GlobalValues until a matching name is found and then set its value with Globa...
an Inet address class
aggregate IP/TCP/UDP functionality to existing Nodes.
A helper class to make life easier while doing simple IPv4 address assignment in scripts.
Ipv4Address NewNetwork(void)
Increment the network number and reset the IP address counter to the base value provided in the SetBa...
void SetBase(Ipv4Address network, Ipv4Mask mask, Ipv4Address base="0.0.0.1")
Set the base network number, network mask and base address.
Ipv4InterfaceContainer Assign(const NetDeviceContainer &c)
Assign IP addresses to the net devices specified in the container based on the current network prefix...
static Ipv4Address GetAny(void)
static void PopulateRoutingTables(void)
Build a routing database and initialize the routing tables of the nodes in the simulation.
holds a vector of std::pair of Ptr<Ipv4> and interface index.
std::pair< Ptr< Ipv4 >, uint32_t > Get(uint32_t i) const
Get the std::pair of an Ptr<Ipv4> and interface stored at the location specified by the index.
void Add(const Ipv4InterfaceContainer &other)
Concatenate the entries in the other container with ours.
Ipv4Address GetAddress(uint32_t i, uint32_t j=0) const
Helper class that adds ns3::Ipv4ListRouting objects.
Helper class that adds Nix-vector routing to nodes.
Helper class that adds ns3::Ipv4StaticRouting objects.
static uint32_t GetSystemId()
Get the id number of this rank.
static uint32_t GetSize()
Get the number of ranks used by ns-3.
static void Disable()
Clean up the ns-3 parallel communications interface.
static void Enable(int *pargc, char ***pargv)
Setup the parallel communication interface.
holds a vector of ns3::NetDevice pointers
void Add(NetDeviceContainer other)
Append the contents of another NetDeviceContainer to the end of this container.
Ptr< NetDevice > Get(uint32_t i) const
Get the Ptr<NetDevice> stored in this container at a given index.
keep track of a set of node pointers.
void Create(uint32_t n)
Create n nodes and append pointers to them to the end of this NodeContainer.
void Add(NodeContainer other)
Append the contents of another NodeContainer to the end of this container.
Ptr< Node > Get(uint32_t i) const
Get the Ptr<Node> stored in this container at a given index.
bool TraceConnectWithoutContext(std::string name, const CallbackBase &cb)
Connect a TraceSource to a Callback without a context.
Definition: object-base.cc:293
A helper to make it easier to instantiate an ns3::OnOffApplication on a set of nodes.
Definition: on-off-helper.h:43
A helper to make it easier to instantiate an ns3::PacketSinkApplication on a set of nodes.
void EnablePcap(std::string prefix, Ptr< NetDevice > nd, bool promiscuous=false, bool explicitFilename=false)
Enable pcap output the indicated net device.
Build a set of PointToPointNetDevice objects.
void SetDeviceAttribute(std::string name, const AttributeValue &value)
Set an attribute value to be propagated to each NetDevice created by the helper.
void SetChannelAttribute(std::string name, const AttributeValue &value)
Set an attribute value to be propagated to each Channel created by the helper.
NetDeviceContainer Install(NodeContainer c)
static void Stop(void)
Tell the Simulator the calling event should be the last one executed.
Definition: simulator.cc:180
static void Destroy(void)
Execute the events scheduled with ScheduleDestroy().
Definition: simulator.cc:136
static void Run(void)
Run the simulation.
Definition: simulator.cc:172
static void SinkTrace(const ns3::Ptr< const ns3::Packet > packet, const ns3::Address &srcAddress, const ns3::Address &destAddress)
PacketSink receive trace callback.
static int GetWorldRank(void)
Get the MPI rank in the world communicator.
static void Init(void)
PacketSink receive trace callback.
static void Verify(unsigned long expectedCount)
Verify the sink trace count observed matches the expected count.
static int GetWorldSize(void)
Get the MPI size of the world communicator.
Hold variables of type string.
Definition: string.h:41
Hold an unsigned integer type.
Definition: uinteger.h:44
uint16_t port
Definition: dsdv-manet.cc:45
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:88
void SetDefault(std::string name, const AttributeValue &value)
Definition: config.cc:849
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1289
Common methods for MPI examples.
#define RANK0COUT(x)
Write to std::cout only from rank 0.
#define RANK0COUTAPPEND(x)
clientApps
Definition: first.py:61
stack
Definition: first.py:41
Every class exported by the ns3 library is enclosed in the ns3 namespace.
@ LOG_LEVEL_INFO
LOG_INFO and above.
Definition: log.h:107
void LogComponentEnable(char const *name, enum LogLevel level)
Enable the logging output associated with that log component.
Definition: log.cc:361
Callback< R, Ts... > MakeCallback(R(T::*memPtr)(Ts...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition: callback.h:1642
cmd
Definition: second.py:35
#define list
bool verbose
void ReportRank(int color, MPI_Comm splitComm)
Report my rank, in both MPI_COMM_WORLD and the split communicator.
const int NS_COLOR
const int NOT_NS_COLOR
bool tracing
Flag to enable/disable generation of tracing files.
Definition: wifi-bianchi.cc:85
Ptr< PacketSink > sink
Definition: wifi-tcp.cc:56