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/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 
85 const int NS_COLOR = 1;
86 const int NOT_NS_COLOR = NS_COLOR + 1;
95 void
96 ReportRank (int color, MPI_Comm splitComm)
97 {
98  int otherId=0;
99  int otherSize=1;
100 
101  MPI_Comm_rank (splitComm, &otherId);
102  MPI_Comm_size (splitComm, &otherSize);
103 
104  if (color == NS_COLOR)
105  {
106  RANK0COUT ( "ns-3 rank: ");
107  }
108  else
109  {
110  RANK0COUT ( "Other rank: ");
111  }
112 
113  RANK0COUTAPPEND ( "in MPI_COMM_WORLD: " << SinkTracer::GetWorldRank () << ":" << SinkTracer::GetWorldSize ()
114  << ", in splitComm: " << otherId << ":" << otherSize
115  << std::endl);
116 
117 } // ReportRank()
118 
119 int
120 main (int argc, char *argv[])
121 {
122  bool nix = true;
123  bool nullmsg = false;
124  bool tracing = false;
125  bool init = false;
126  bool verbose = false;
127  bool testing = false;
128 
129  // Parse command line
130  CommandLine cmd(__FILE__);
131  cmd.AddValue ("nix", "Enable the use of nix-vector or global routing", nix);
132  cmd.AddValue ("nullmsg", "Enable the use of null-message synchronization (instead of granted time window)", nullmsg);
133  cmd.AddValue ("tracing", "Enable pcap tracing", tracing);
134  cmd.AddValue ("init", "ns-3 should initialize MPI by calling MPI_Init", init);
135  cmd.AddValue ("verbose", "verbose output", verbose);
136  cmd.AddValue ("test", "Enable regression test output", testing);
137  cmd.Parse (argc, argv);
138 
139  // Defer reporting the configuration until we know the communicator
140 
141  // Distributed simulation setup; by default use granted time window algorithm.
142  if(nullmsg)
143  {
144  GlobalValue::Bind ("SimulatorImplementationType",
145  StringValue ("ns3::NullMessageSimulatorImpl"));
146  }
147  else
148  {
149  GlobalValue::Bind ("SimulatorImplementationType",
150  StringValue ("ns3::DistributedSimulatorImpl"));
151  }
152 
153  // MPI_Init
154 
155  if (init)
156  {
157  // Initialize MPI directly
158  MPI_Init(&argc, &argv);
159  }
160  else
161  {
162  // Let ns-3 call MPI_Init and MPI_Finalize
163  MpiInterface::Enable (&argc, &argv);
164  }
165 
166  SinkTracer::Init ();
167 
168  auto worldSize = SinkTracer::GetWorldSize ();
169  auto worldRank = SinkTracer::GetWorldRank ();
170 
171  if ( (!init) && (worldSize != 2))
172  {
173  RANK0COUT ("This simulation requires exactly 2 logical processors if --init is not set." << std::endl);
174  return 1;
175  }
176 
177  if (worldSize < 2)
178  {
179  RANK0COUT ("This simulation requires 2 or more logical processors." << std::endl);
180  return 1;
181  }
182 
183  // Set up the MPI communicator for ns-3
184  // Condition ns-3 Communicator
185  // a. worldSize = 2 copy of MPI_COMM_WORLD
186  // b. worldSize > 2 communicator of ranks 1-2
187 
188  // Flag to record that we created a communicator so we can free it at the end.
189  bool freeComm = false;
190  // The new communicator, if we create one
191  MPI_Comm splitComm = MPI_COMM_WORLD;
192  // The list of ranks assigned to ns-3
193  std::string ns3Ranks;
194  // Tag for whether this rank should go into a new communicator
195  int color = MPI_UNDEFINED;
196 
197 
198  if (worldSize == 2)
199  {
200  std::stringstream ss;
201  color = NS_COLOR;
202  ss << "MPI_COMM_WORLD (" << worldSize << " ranks)";
203  ns3Ranks = ss.str ();
204  splitComm = MPI_COMM_WORLD;
205  freeComm = false;
206  }
207  else
208  {
209  // worldSize > 2 communicator of ranks 1-2
210 
211  // Put ranks 1-2 in the new communicator
212  if (worldRank == 1 || worldRank == 2)
213  {
214  color = NS_COLOR;
215  }
216  else
217  {
218  color = NOT_NS_COLOR;
219  }
220  std::stringstream ss;
221  ss << "Split [1-2] (out of " << worldSize << " ranks) from MPI_COMM_WORLD";
222  ns3Ranks = ss.str ();
223 
224  // Now create the new communicator
225  MPI_Comm_split (MPI_COMM_WORLD, color, worldRank, &splitComm);
226  freeComm = true;
227  }
228 
229 
230  if(init)
231  {
232  MpiInterface::Enable (splitComm);
233  }
234 
235  // Report the configuration from rank 0 only
236  RANK0COUT (cmd.GetName () << "\n");
237  RANK0COUT ("\n" );
238  RANK0COUT ("Configuration:\n" );
239  RANK0COUT ("Routing: " << (nix ? "nix-vector" : "global") << "\n");
240  RANK0COUT ("Synchronization: " << (nullmsg ? "null-message" : "granted time window (YAWNS)") << "\n");
241  RANK0COUT ("MPI_Init called: " << (init ? "explicitly by this program" : "implicitly by ns3::MpiInterface::Enable()") << "\n" );
242  RANK0COUT ("ns-3 Communicator: " << ns3Ranks << "\n");
243  RANK0COUT ("PCAP tracing: " << (tracing ? "" : "not") << " enabled\n");
244  RANK0COUT ("\n");
245  RANK0COUT ("Rank assignments:" << std::endl);
246 
247  if (worldRank == 0)
248  {
249  ReportRank (color, splitComm);
250  }
251 
252  if(verbose)
253  {
254  // Circulate a token to have each rank report in turn
255  int token;
256 
257  if (worldRank == 0)
258  {
259  token = 1;
260  }
261  else
262  {
263  MPI_Recv (&token, 1, MPI_INT, worldRank - 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
264  ReportRank (color, splitComm);
265  }
266 
267  MPI_Send (&token, 1, MPI_INT, (worldRank + 1) % worldSize, 0, MPI_COMM_WORLD);
268 
269  if (worldRank == 0)
270  {
271  MPI_Recv (&token, 1, MPI_INT, worldSize - 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
272  }
273  } // circulate token to report rank
274 
275  RANK0COUT (std::endl);
276 
277  if (color != NS_COLOR)
278  {
279  // Do other work outside the ns-3 communicator
280 
281  // In real use of a separate communicator from ns-3
282  // the other tasks would be running another simulator
283  // or other desired work here..
284 
285  // Our work is done, just wait for everyone else to finish.
286 
288 
289  if(init)
290  {
291  MPI_Finalize ();
292  }
293 
294  return 0;
295  }
296 
297  // The code below here is essentially the same as simple-distributed.cc
298  // --------------------------------------------------------------------
299 
300  // We use a trace instead of relying on NS_LOG
301 
302  if (verbose)
303  {
304  LogComponentEnable ("PacketSink", LOG_LEVEL_INFO);
305  }
306 
307  uint32_t systemId = MpiInterface::GetSystemId ();
308  uint32_t systemCount = MpiInterface::GetSize ();
309 
310  // Check for valid distributed parameters.
311  // Both this script and simple-distributed.cc will work
312  // with arbitrary numbers of ranks, as long as there are at least 2.
313  if (systemCount < 2)
314  {
315  RANK0COUT ("This simulation requires at least 2 logical processors." << std::endl);
316  return 1;
317  }
318 
319  // Some default values
320  Config::SetDefault ("ns3::OnOffApplication::PacketSize", UintegerValue (512));
321  Config::SetDefault ("ns3::OnOffApplication::DataRate", StringValue ("1Mbps"));
322  Config::SetDefault ("ns3::OnOffApplication::MaxBytes", UintegerValue (512));
323 
324  // Create leaf nodes on left with system id 0
325  NodeContainer leftLeafNodes;
326  leftLeafNodes.Create (4, 0);
327 
328  // Create router nodes. Left router
329  // with system id 0, right router with
330  // system id 1
331  NodeContainer routerNodes;
332  Ptr<Node> routerNode1 = CreateObject<Node> (0);
333  Ptr<Node> routerNode2 = CreateObject<Node> (1);
334  routerNodes.Add (routerNode1);
335  routerNodes.Add (routerNode2);
336 
337  // Create leaf nodes on left with system id 1
338  NodeContainer rightLeafNodes;
339  rightLeafNodes.Create (4, 1);
340 
341  PointToPointHelper routerLink;
342  routerLink.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
343  routerLink.SetChannelAttribute ("Delay", StringValue ("5ms"));
344 
345  PointToPointHelper leafLink;
346  leafLink.SetDeviceAttribute ("DataRate", StringValue ("1Mbps"));
347  leafLink.SetChannelAttribute ("Delay", StringValue ("2ms"));
348 
349  // Add link connecting routers
350  NetDeviceContainer routerDevices;
351  routerDevices = routerLink.Install (routerNodes);
352 
353  // Add links for left side leaf nodes to left router
354  NetDeviceContainer leftRouterDevices;
355  NetDeviceContainer leftLeafDevices;
356  for (uint32_t i = 0; i < 4; ++i)
357  {
358  NetDeviceContainer temp = leafLink.Install (leftLeafNodes.Get (i), routerNodes.Get (0));
359  leftLeafDevices.Add (temp.Get (0));
360  leftRouterDevices.Add (temp.Get (1));
361  }
362 
363  // Add links for right side leaf nodes to right router
364  NetDeviceContainer rightRouterDevices;
365  NetDeviceContainer rightLeafDevices;
366  for (uint32_t i = 0; i < 4; ++i)
367  {
368  NetDeviceContainer temp = leafLink.Install (rightLeafNodes.Get (i), routerNodes.Get (1));
369  rightLeafDevices.Add (temp.Get (0));
370  rightRouterDevices.Add (temp.Get (1));
371  }
372 
374  Ipv4NixVectorHelper nixRouting;
375  Ipv4StaticRoutingHelper staticRouting;
376 
378  list.Add (staticRouting, 0);
379  list.Add (nixRouting, 10);
380 
381  if (nix)
382  {
383  stack.SetRoutingHelper (list); // has effect on the next Install ()
384  }
385 
386  stack.InstallAll ();
387 
388  Ipv4InterfaceContainer routerInterfaces;
389  Ipv4InterfaceContainer leftLeafInterfaces;
390  Ipv4InterfaceContainer leftRouterInterfaces;
391  Ipv4InterfaceContainer rightLeafInterfaces;
392  Ipv4InterfaceContainer rightRouterInterfaces;
393 
394  Ipv4AddressHelper leftAddress;
395  leftAddress.SetBase ("10.1.1.0", "255.255.255.0");
396 
397  Ipv4AddressHelper routerAddress;
398  routerAddress.SetBase ("10.2.1.0", "255.255.255.0");
399 
400  Ipv4AddressHelper rightAddress;
401  rightAddress.SetBase ("10.3.1.0", "255.255.255.0");
402 
403  // Router-to-Router interfaces
404  routerInterfaces = routerAddress.Assign (routerDevices);
405 
406  // Left interfaces
407  for (uint32_t i = 0; i < 4; ++i)
408  {
409  NetDeviceContainer ndc;
410  ndc.Add (leftLeafDevices.Get (i));
411  ndc.Add (leftRouterDevices.Get (i));
412  Ipv4InterfaceContainer ifc = leftAddress.Assign (ndc);
413  leftLeafInterfaces.Add (ifc.Get (0));
414  leftRouterInterfaces.Add (ifc.Get (1));
415  leftAddress.NewNetwork ();
416  }
417 
418  // Right interfaces
419  for (uint32_t i = 0; i < 4; ++i)
420  {
421  NetDeviceContainer ndc;
422  ndc.Add (rightLeafDevices.Get (i));
423  ndc.Add (rightRouterDevices.Get (i));
424  Ipv4InterfaceContainer ifc = rightAddress.Assign (ndc);
425  rightLeafInterfaces.Add (ifc.Get (0));
426  rightRouterInterfaces.Add (ifc.Get (1));
427  rightAddress.NewNetwork ();
428  }
429 
430  if (!nix)
431  {
433  }
434 
435  if (tracing == true)
436  {
437  if (systemId == 0)
438  {
439  routerLink.EnablePcap("router-left", routerDevices, true);
440  leafLink.EnablePcap("leaf-left", leftLeafDevices, true);
441  }
442 
443  if (systemId == 1)
444  {
445  routerLink.EnablePcap("router-right", routerDevices, true);
446  leafLink.EnablePcap("leaf-right", rightLeafDevices, true);
447  }
448  }
449 
450  // Create a packet sink on the right leafs to receive packets from left leafs
451  uint16_t port = 50000;
452  if (systemId == 1)
453  {
454  Address sinkLocalAddress (InetSocketAddress (Ipv4Address::GetAny (), port));
455  PacketSinkHelper sinkHelper ("ns3::UdpSocketFactory", sinkLocalAddress);
456  ApplicationContainer sinkApp;
457  for (uint32_t i = 0; i < 4; ++i)
458  {
459  auto apps = sinkHelper.Install (rightLeafNodes.Get (i));
460  auto sink = DynamicCast<PacketSink> (apps.Get (0));
461  NS_ASSERT_MSG (sink, "Couldn't get PacketSink application.");
462  if (testing)
463  {
465  }
466  sinkApp.Add (apps);
467  }
468  sinkApp.Start (Seconds (1.0));
469  sinkApp.Stop (Seconds (5));
470  }
471 
472  // Create the OnOff applications to send
473  if (systemId == 0)
474  {
475  OnOffHelper clientHelper ("ns3::UdpSocketFactory", Address ());
476  clientHelper.SetAttribute
477  ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1]"));
478  clientHelper.SetAttribute
479  ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0]"));
480 
482  for (uint32_t i = 0; i < 4; ++i)
483  {
484  AddressValue remoteAddress
485  (InetSocketAddress (rightLeafInterfaces.GetAddress (i), port));
486  clientHelper.SetAttribute ("Remote", remoteAddress);
487  clientApps.Add (clientHelper.Install (leftLeafNodes.Get (i)));
488  }
489  clientApps.Start (Seconds (1.0));
490  clientApps.Stop (Seconds (5));
491  }
492 
493  RANK0COUT (std::endl);
494 
495  Simulator::Stop (Seconds (5));
496  Simulator::Run ();
498 
499  // --------------------------------------------------------------------
500  // Conditional cleanup based on whether we built a communicator
501  // and called MPI_Init
502 
503  if (freeComm)
504  {
505  MPI_Comm_free (&splitComm);
506  }
507 
508  if (testing)
509  {
510  SinkTracer::Verify (4);
511  }
512 
513  // Clean up the ns-3 MPI execution environment
514  // This will call MPI_Finalize if MpiInterface::Initialize was called
516 
517  if (init)
518  {
519  // We called MPI_Init, so we have to call MPI_Finalize
520  MPI_Finalize ();
521  }
522 
523  return 0;
524 }
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:229
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 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.
Helper class that adds Nix-vector routing to nodes.
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:364
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 Init.
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
#define RANK0COUT(x)
Write to std::cout only from rank 0.
#define RANK0COUTAPPEND(x)
Append to std::cout only from rank 0.
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1244
Common methods for MPI examples.
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:1648
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
Tag for whether this rank should go into a new communicator ns-3 ranks will have color == 1.
const int NOT_NS_COLOR
Tag for whether this rank should go into a new communicator ns-3 ranks will have color == 1.
bool tracing
Flag to enable/disable generation of tracing files.
Definition: wifi-bianchi.cc:88
Ptr< PacketSink > sink
Definition: wifi-tcp.cc:56