-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #23 from SpiNNakerManchester/chip_id_allocation
added a chip id allocator and tied it into all the examples and tool …
- Loading branch information
Showing
9 changed files
with
379 additions
and
157 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
""" | ||
AbstractVirtualVertex | ||
""" | ||
|
||
# pacman imports | ||
from pacman.model.partitionable_graph.abstract_partitionable_vertex import \ | ||
AbstractPartitionableVertex | ||
from pacman.model.constraints.placer_constraints\ | ||
.placer_chip_and_core_constraint import PlacerChipAndCoreConstraint | ||
|
||
# general imports | ||
from abc import ABCMeta | ||
from six import add_metaclass | ||
from abc import abstractmethod | ||
|
||
|
||
@add_metaclass(ABCMeta) | ||
class AbstractVirtualVertex(AbstractPartitionableVertex): | ||
""" A class that allows models to define that they are virtual | ||
""" | ||
|
||
def __init__(self, n_atoms, spinnaker_link_id, label, max_atoms_per_core): | ||
|
||
AbstractPartitionableVertex.__init__(self, n_atoms, label, | ||
max_atoms_per_core) | ||
# set up virtual data structures | ||
self._virtual_chip_x = None | ||
self._virtual_chip_y = None | ||
self._spinnaker_link_id = spinnaker_link_id | ||
|
||
placement_constaint = \ | ||
PlacerChipAndCoreConstraint(self._virtual_chip_x, | ||
self._virtual_chip_y) | ||
self.add_constraint(placement_constaint) | ||
|
||
@property | ||
def virtual_chip_x(self): | ||
return self._virtual_chip_x | ||
|
||
@property | ||
def virtual_chip_y(self): | ||
return self._virtual_chip_y | ||
|
||
@virtual_chip_x.setter | ||
def virtual_chip_x(self, new_value): | ||
self._virtual_chip_x = new_value | ||
|
||
@virtual_chip_y.setter | ||
def virtual_chip_y(self, new_value): | ||
self._virtual_chip_y = new_value | ||
|
||
@property | ||
def get_spinnaker_link_id(self): | ||
""" | ||
proeprty for returning the spinnaker link being used | ||
:return: | ||
""" | ||
return self._spinnaker_link_id | ||
|
||
@abstractmethod | ||
def is_virtual_vertex(self): | ||
""" helper method for is instance | ||
:return: | ||
""" | ||
|
||
# overlaoded method from partitionable vertex | ||
def get_cpu_usage_for_atoms(self, vertex_slice, graph): | ||
return 0 | ||
|
||
# overlaoded method from partitionable vertex | ||
def get_dtcm_usage_for_atoms(self, vertex_slice, graph): | ||
return 0 | ||
|
||
# overlaoded method from partitionable vertex | ||
def get_sdram_usage_for_atoms(self, vertex_slice, graph): | ||
return 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
143 changes: 143 additions & 0 deletions
143
pacman/operations/abstract_algorithms/abstract_element_allocator_algorithm.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
""" | ||
AbstractElementAllocatorAlgorithm | ||
""" | ||
|
||
# pacman imports | ||
from pacman.model.resources.element_free_space import ElementFreeSpace | ||
from pacman import exceptions | ||
|
||
# general imports | ||
from abc import ABCMeta | ||
from six import add_metaclass | ||
import math | ||
|
||
|
||
@add_metaclass(ABCMeta) | ||
class AbstractElementAllocatorAlgorithm(object): | ||
""" | ||
abstract element allocator algorithm which allocates elements from | ||
a base place for a number of elements | ||
""" | ||
|
||
def __init__(self, size_begin, size_end): | ||
self._free_space_tracker = list() | ||
self._free_space_tracker.append(ElementFreeSpace(size_begin, size_end)) | ||
|
||
def _allocate_elements(self, base_element_id, n_elements): | ||
""" Handle the allocating of space for a given set of elements | ||
:param base_element_id: the first element id to allocate | ||
:param n_elements: the number of elements to allocate | ||
:raises: PacmanRouteInfoAllocationException when the element id | ||
cannot be assigned with the given number of elements | ||
""" | ||
|
||
index = self._find_slot(base_element_id) | ||
if index is None: | ||
raise exceptions.PacmanElementAllocationException( | ||
"Space for {} elements starting at {} has already " | ||
"been allocated".format(n_elements, base_element_id)) | ||
|
||
# base element should be >= slot element at this point | ||
self._do_allocation(index, base_element_id, n_elements) | ||
|
||
def _find_slot(self, base_element_id, lo=0): | ||
""" Find the free slot with the closest | ||
base element id <= base element using a binary | ||
search | ||
""" | ||
hi = len(self._free_space_tracker) - 1 | ||
while lo < hi: | ||
mid = int(math.ceil(float(lo + hi) / 2.0)) | ||
free_space_slot = self._free_space_tracker[mid] | ||
|
||
if free_space_slot.start_address > base_element_id: | ||
hi = mid - 1 | ||
else: | ||
lo = mid | ||
|
||
# If we have gone off the end of the array, we haven't found a slot | ||
if (lo >= len(self._free_space_tracker) or hi < 0 or | ||
self._free_space_tracker[lo].start_address > base_element_id): | ||
return None | ||
return lo | ||
|
||
def _do_allocation(self, index, base_element_id, n_elements): | ||
""" Allocate a given base element id and number of elements into the | ||
space at the given slot | ||
:param index: The index of the free space slot to check | ||
:param base_element_id: The element id to start with - must be | ||
inside the slot | ||
:param n_elements: The number of elements to be allocated -\ | ||
should be power of 2 | ||
""" | ||
|
||
free_space_slot = self._free_space_tracker[index] | ||
if free_space_slot.start_address > base_element_id: | ||
raise exceptions.PacmanElementAllocationException( | ||
"Trying to allocate a element in the wrong slot!") | ||
|
||
# Check if there is enough space to allocate | ||
space = self._check_allocation(index, base_element_id, n_elements) | ||
if space is None: | ||
raise exceptions.PacmanElementAllocationException( | ||
"Not enough space to allocate {} elements starting at {}" | ||
.format(n_elements, hex(base_element_id))) | ||
|
||
if (free_space_slot.start_address == base_element_id and | ||
free_space_slot.size == n_elements): | ||
|
||
# If the slot exactly matches the space, remove it | ||
del self._free_space_tracker[index] | ||
|
||
elif free_space_slot.start_address == base_element_id: | ||
|
||
# If the slot starts with the element id, reduce the size | ||
self._free_space_tracker[index] = ElementFreeSpace( | ||
free_space_slot.start_address + n_elements, | ||
free_space_slot.size - n_elements) | ||
|
||
elif space == n_elements: | ||
|
||
# If the space at the end exactly matches the spot, reduce the size | ||
self._free_space_tracker[index] = ElementFreeSpace( | ||
free_space_slot.start_address, | ||
free_space_slot.size - n_elements) | ||
|
||
else: | ||
|
||
# Otherwise, the allocation lies in the middle of the region: | ||
# First, reduce the size of the space before the allocation | ||
self._free_space_tracker[index] = ElementFreeSpace( | ||
free_space_slot.start_address, | ||
base_element_id - free_space_slot.start_address) | ||
|
||
# Then add a new space after the allocation | ||
self._free_space_tracker.insert(index + 1, ElementFreeSpace( | ||
base_element_id + n_elements, | ||
free_space_slot.start_address + free_space_slot.size - | ||
(base_element_id + n_elements))) | ||
|
||
def _check_allocation(self, index, base_element_id, n_elements): | ||
""" Check if there is enough space for a given set of element ids | ||
starting at a base element id inside a given slot | ||
:param index: The index of the free space slot to check | ||
:param base_element_id: The element id to start with - | ||
must be inside the slot | ||
:param n_elements: The number of elements to be allocated -\ | ||
should be power of 2 | ||
""" | ||
free_space_slot = self._free_space_tracker[index] | ||
space = (free_space_slot.size - | ||
(base_element_id - free_space_slot.start_address)) | ||
|
||
if free_space_slot.start_address > base_element_id: | ||
raise exceptions.PacmanElementAllocationException( | ||
"Trying to allocate a element id in the wrong slot!") | ||
|
||
# Check if there is enough space for the elements | ||
if space < n_elements: | ||
return None | ||
return space |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from pacman.operations.chip_id_allocator_algorithms.\ | ||
malloc_based_chip_id_allocator import MallocBasedChipIdAllocator |
65 changes: 65 additions & 0 deletions
65
pacman/operations/chip_id_allocator_algorithms/malloc_based_chip_id_allocator.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
""" | ||
MallocBasedChipIdAllocator | ||
""" | ||
|
||
# pacman imports | ||
from pacman.model.abstract_classes.abstract_virtual_vertex import \ | ||
AbstractVirtualVertex | ||
from pacman.operations.abstract_algorithms.\ | ||
abstract_element_allocator_algorithm import \ | ||
AbstractElementAllocatorAlgorithm | ||
from pacman.utilities.progress_bar import ProgressBar | ||
|
||
# general imports | ||
import logging | ||
import math | ||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class MallocBasedChipIdAllocator(AbstractElementAllocatorAlgorithm): | ||
""" A Chip id Allocation Allocator algorithm that keeps track of | ||
chip ids and attempts to allocate them as requested | ||
""" | ||
|
||
def __init__(self): | ||
AbstractElementAllocatorAlgorithm.__init__(self, 0, math.pow(2, 32)) | ||
|
||
def allocate_chip_ids(self, partitionable_graph, machine): | ||
""" | ||
:param partitionable_graph: | ||
:param machine: | ||
:return: | ||
""" | ||
# Go through the groups and allocate keys | ||
progress_bar = ProgressBar( | ||
(len(partitionable_graph.vertices) + len(list(machine.chips))), | ||
"on allocating the chip id's for the machine's real and virtual " | ||
"chips") | ||
|
||
# allocate standard ids for real chips | ||
for chip in machine.chips: | ||
expected_chip_id = (chip.x << 8) + chip.y | ||
self._allocate_elements(expected_chip_id, 1) | ||
progress_bar.update() | ||
|
||
# allocate ids for virtual chips | ||
for vertex in partitionable_graph.vertices: | ||
if isinstance(vertex, AbstractVirtualVertex): | ||
chip_id_x, chip_id_y = self._allocate_id() | ||
vertex.virtual_chip_x = chip_id_x | ||
vertex.virtual_chip_y = chip_id_y | ||
progress_bar.update() | ||
progress_bar.end() | ||
|
||
def _allocate_id(self): | ||
""" | ||
allocate a chip id from the free space | ||
:return: | ||
""" | ||
# can always asssume theres at least one element in the free space, | ||
# otherwise it will have already been deleted already. | ||
free_space_chunk = self._free_space_tracker[0] | ||
chip_id = free_space_chunk.start_address | ||
self._allocate_elements(chip_id, 1) | ||
return (chip_id >> 8), (chip_id & 0xFFFF) |
Oops, something went wrong.