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
76using namespace ns3;
77
78NS_LOG_COMPONENT_DEFINE ("SimpleDistributedMpiComm");
79
85const int NS_COLOR = 1;
86const int NOT_NS_COLOR = NS_COLOR + 1;
95void
96ReportRank (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
119int
120main (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
287 MpiInterface::Disable ();
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 {
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 {
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 {
432 Ipv4GlobalRoutingHelper::PopulateRoutingTables ();
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 {
464 sink->TraceConnectWithoutContext ("RxWithAddresses", MakeCallback(&SinkTracer::SinkTrace));
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 ();
497 Simulator::Destroy ();
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
515 MpiInterface::Disable ();
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.
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
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...
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.
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)
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
uint32_t GetSize(Ptr< const Packet > packet, const WifiMacHeader *hdr, bool isAmpdu)
Return the total size of the packet after WifiMacHeader and FCS trailer have been added.
Definition: wifi-utils.cc:129
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