Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Deprecate/remove Comm::wait_any and Comm::wait_any_for
[simgrid.git] / examples / python / network-nonlinear / network-nonlinear.py
1 # Copyright (c) 2006-2023. The SimGrid Team. All rights reserved.
2 #
3 # This program is free software; you can redistribute it and/or modify it
4 # under the terms of the license (GNU LGPL) which comes with this package.
5
6 """
7 This example shows how to simulate a non-linear resource sharing for network links.
8 """
9
10 import functools
11 import sys
12 from simgrid import Actor, ActivitySet, Engine, Comm, Mailbox, NetZone, Link, LinkInRoute, this_actor
13
14 class Sender:
15     """
16     Send a series of messages to mailbox "receiver"
17     """
18     def __init__(self, msg_count: int, msg_size=int(1e6)):
19         self.msg_count = msg_count
20         self.msg_size = msg_size
21
22     # Actors that are created as object will execute their __call__ method.
23     # So, the following constitutes the main function of the Sender actor.
24     def __call__(self):
25         pending_comms = ActivitySet()
26         mbox = Mailbox.by_name("receiver")
27
28         for i in range(self.msg_count):
29             msg = "Message " + str(i)
30             size = self.msg_size * (i + 1)
31             this_actor.info("Send '%s' to '%s, msg size: %d'" % (msg, mbox.name, size))
32             comm = mbox.put_async(msg, size)
33             pending_comms.push(comm)
34
35         this_actor.info("Done dispatching all messages")
36
37         # Now that all message exchanges were initiated, wait for their completion in one single call
38         pending_comms.wait_all()
39
40         this_actor.info("Goodbye now!")
41
42 class Receiver:
43     """
44     Receiver actor: wait for N messages on the mailbox "receiver"
45     """
46
47     def __init__(self, msg_count=10):
48         self.msg_count = msg_count
49
50     def __call__(self):
51         mbox = Mailbox.by_name("receiver")
52
53         pending_comms = ActivitySet()
54
55         this_actor.info("Wait for %d messages asynchronously" % self.msg_count)
56         for _ in range(self.msg_count):
57             comm = mbox.get_async()
58             pending_comms.push(comm)
59
60         while not pending_comms.empty():
61             comm = pending_comms.wait_any()
62             this_actor.info("I got '%s'." % comm.get_payload())
63
64 ####################################################################################################
65 def link_nonlinear(link: Link, capacity: float, n: int) -> float:
66     """
67     Non-linear resource sharing for links
68
69     Note that the callback is called twice in this example:
70     1) link UP: with the number of active flows (from 9 to 1)
71     2) link DOWN: with 0 active flows. A crosstraffic communication is happing
72     in the down link, but it's not considered as an active flow.
73     """
74     # emulates a degradation in link according to the number of flows
75     # you probably want something more complex than that and based on real
76     # experiments
77     capacity = min(capacity, capacity * (1.0 - (n - 1) / 10.0))
78     this_actor.info("Link %s, %d active communications, new capacity %f" % (link.name, n, capacity))
79     return capacity
80
81 def load_platform():
82     """
83     Create a simple 2-hosts platform
84      ________                 __________
85     | Sender |===============| Receiver |
86     |________|    Link1      |__________|
87
88     """
89     zone = NetZone.create_full_zone("Zone1")
90     sender = zone.create_host("sender", 1).seal()
91     receiver = zone.create_host("receiver", 1).seal()
92
93     link = zone.create_split_duplex_link("link1", 1e6)
94     # setting same callbacks (could be different) for link UP/DOWN in split-duplex link
95     link.link_up.set_sharing_policy(Link.SharingPolicy.NONLINEAR,
96                                     functools.partial(link_nonlinear, link.link_up))
97     link.link_down.set_sharing_policy(Link.SharingPolicy.NONLINEAR,
98                                       functools.partial(link_nonlinear, link.link_down))
99     link.set_latency(10e-6).seal()
100
101     # create routes between nodes
102     zone.add_route(sender, receiver, [link])
103     zone.seal()
104
105     # create actors Sender/Receiver
106     Actor.create("receiver", receiver, Receiver(9))
107     Actor.create("sender", sender, Sender(9))
108
109 ###################################################################################################
110
111 if __name__ == '__main__':
112     e = Engine(sys.argv)
113
114     # create platform
115     load_platform()
116
117     # runs the simulation
118     e.run()
119
120     # explicitly deleting Engine object to avoid segfault during cleanup phase.
121     # During Engine destruction, the cleanup of std::function linked to link_non_linear callback is called.
122     # If we let the cleanup by itself, it fails trying on its destruction because the python main program
123     # has already freed its variables
124     del e