greedyAllocator.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. # Copyright 2014 Modelling, Simulation and Design Lab (MSDL) at
  2. # McGill University and the University of Antwerp (http://msdl.cs.mcgill.ca/)
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from collections import defaultdict
  16. class GreedyAllocator(object):
  17. """
  18. Allocate all models in a greedy manner: make the most heavy link local and extend from there on until an average load is reached.
  19. """
  20. def allocate(self, models, edges, nr_nodes, total_activities):
  21. """
  22. Calculate allocations for the nodes, using the information provided.
  23. :param models: the models to allocte
  24. :param edges: the edges between the models
  25. :param nr_nodes: the number of nodes to allocate over. Simply an upper bound!
  26. :param total_activities: activity tracking information from each model
  27. :returns: allocation that was found
  28. """
  29. # Run over all edges to create the nodes and link in their edges
  30. nodes = {}
  31. remaining_edges = set()
  32. to_alloc = set()
  33. for source in edges:
  34. for destination in edges[source]:
  35. # A connection from 'source' to 'destination'
  36. edge = edges[source][destination]
  37. nodes.setdefault(source, []).append((edge, destination))
  38. nodes.setdefault(destination, []).append((edge, source))
  39. remaining_edges.add((edge, source, destination))
  40. to_alloc.add(destination)
  41. to_alloc.add(source)
  42. # OK, nodes are constructed
  43. # Allocate 1 node too much for spilling
  44. nr_nodes += 1
  45. # Find average activity (our target)
  46. avg_activity = sum([total_activities[i] for i in total_activities]) / nr_nodes
  47. # Get the strongest edge
  48. alloc_node = 0
  49. node_load = []
  50. allocation = {}
  51. allocation_rev = defaultdict(set)
  52. while alloc_node < (nr_nodes - 1):
  53. while remaining_edges:
  54. max_edge = max(remaining_edges)
  55. remaining_edges.remove(max_edge)
  56. edge_weight, source, destination = max_edge
  57. if source in to_alloc and destination in to_alloc:
  58. break
  59. else:
  60. break
  61. activity_source = total_activities[source.model_id]
  62. activity_destination = total_activities[destination.model_id]
  63. node_load.append(activity_source + activity_destination)
  64. allocation[source.model_id] = alloc_node
  65. allocation[destination.model_id] = alloc_node
  66. allocation_rev[alloc_node].add(source)
  67. allocation_rev[alloc_node].add(destination)
  68. to_alloc.remove(source)
  69. to_alloc.remove(destination)
  70. while node_load[alloc_node] < average_activity:
  71. edge_search = []
  72. for edge in remaining_edges:
  73. if ((edge[1] in allocation_rev[alloc_node] and
  74. edge[2] in to_alloc) or
  75. (edge[2] in allocation_rev[alloc_node] and
  76. edge[1] in to_alloc)):
  77. edge_search.append(edge)
  78. if not edge_search:
  79. break
  80. # Allocate some more nodes
  81. max_edge = max(edge_search)
  82. remaining_edges.remove(max_edge)
  83. edge_weight, source, destination = max_edge
  84. # Ok, this is an unbound connection, so add it
  85. if source in to_alloc:
  86. to_alloc.remove(source)
  87. allocation[source.model_id] = alloc_node
  88. allocation_rev[alloc_node].add(source.model_id)
  89. node_load[alloc_node] += total_activities[source.model_id]
  90. if destination in to_alloc:
  91. to_alloc.remove(destination)
  92. allocation[destination.model_id] = alloc_node
  93. allocation_rev[alloc_node].add(destination.model_id)
  94. node_load[alloc_node] += total_activities[destination.model_id]
  95. alloc_node += 1
  96. # All unassigned nodes are for the spill node
  97. # Undo our spilling node
  98. while to_alloc:
  99. changes = False
  100. n = list(to_alloc)
  101. for model in n:
  102. options = set()
  103. for oport in model.OPorts:
  104. for oline, _ in oport.routing_outline:
  105. location = oline.host_DEVS.location
  106. if oline.host_DEVS.location is not None:
  107. options.add((node_load[location], location))
  108. for iport in model.IPorts:
  109. for iline in oport.routing_inline:
  110. location = iline.host_DEVS.location
  111. if iline.host_DEVS.location is not None:
  112. options.add((node_load[location], location))
  113. if not options:
  114. continue
  115. # Get the best option
  116. _, loc = min(options)
  117. node_load[loc] += total_activities[model.model_id]
  118. allocation[model.model_id] = loc
  119. allocation_rev[loc].add(model.model_id)
  120. to_alloc.remove(model)
  121. if not changes:
  122. # An iteration without changes, means that we loop forever
  123. for m in to_alloc:
  124. # Force an allocation to 0
  125. allocation[m.model_id] = 0
  126. # allocation_rev doesn't need to be updated
  127. break
  128. return allocation
  129. def getTerminationTime(self):
  130. """
  131. Returns the time it takes for the allocator to make an 'educated guess' of the advised allocation.
  132. This time will not be used exactly, but as soon as the GVT passes over it. While this is not exactly
  133. necessary, it avoids the overhead of putting such a test in frequently used code.
  134. :returns: float -- the time at which to perform the allocations (and save them)
  135. """
  136. return 10.0