Skip to content

Commit

Permalink
Merge pull request #23 from SpiNNakerManchester/chip_id_allocation
Browse files Browse the repository at this point in the history
added a chip id allocator and tied it into all the examples and tool …
  • Loading branch information
rowleya committed Aug 15, 2015
2 parents 7ac99d0 + 7a69f20 commit 2c54e98
Show file tree
Hide file tree
Showing 9 changed files with 379 additions and 157 deletions.
13 changes: 13 additions & 0 deletions pacman/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,19 @@ def __init__(self, problem):
PacmanException.__init__(self, problem)


class PacmanElementAllocationException(PacmanException):
""" An exception that indicates that something went wrong with element\
allocation
"""

def __init__(self, problem):
"""
:param problem: The problem with the allocation
:type problem: str
"""
PacmanException.__init__(self, problem)


class PacmanRoutingException(PacmanException):
""" An exception that indicates that something went wrong with routing
"""
Expand Down
77 changes: 77 additions & 0 deletions pacman/model/abstract_classes/abstract_virtual_vertex.py
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@


class FreeSpace(object):
class ElementFreeSpace(object):

def __init__(self, start_address, size):
self._start_address = start_address
Expand All @@ -15,8 +15,8 @@ def size(self):
return self._size

def __repr__(self):
return "FreeSpace:start={}:size={}".format(self._start_address,
self._size)
return "ElementFreeSpace:start={}:size={}".format(
hex(self._start_address), self._size)

def __str__(self):
return self.__repr__()
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
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from abc import ABCMeta
from abc import abstractmethod
from collections import OrderedDict
from six import add_metaclass

"""
AbstractRoutingInfoAllocatorAlgorithm
"""

# pacman imports
from pacman.operations.abstract_algorithms.\
abstract_element_allocator_algorithm import \
AbstractElementAllocatorAlgorithm
from pacman.utilities import utility_calls

from pacman.exceptions import PacmanValueError

from pacman.model.constraints.key_allocator_constraints\
.key_allocator_contiguous_range_constraint \
import KeyAllocatorContiguousRangeContraint
Expand All @@ -21,14 +22,22 @@
import KeyAllocatorSameKeysConstraint
from pacman.utilities.ordered_set import OrderedSet

# general imports
from abc import ABCMeta
from abc import abstractmethod
from collections import OrderedDict
from six import add_metaclass
import math


@add_metaclass(ABCMeta)
class AbstractRoutingInfoAllocatorAlgorithm(object):
class AbstractRoutingInfoAllocatorAlgorithm(AbstractElementAllocatorAlgorithm):
""" An abstract algorithm that can produce routing keys and masks for\
subedges in a partitioned_graph
"""

def __init__(self):
AbstractElementAllocatorAlgorithm.__init__(self, 0, math.pow(2, 32))
self._supported_constraints = list()

@staticmethod
Expand Down
2 changes: 2 additions & 0 deletions pacman/operations/chip_id_allocator_algorithms/__init__.py
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
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)

0 comments on commit 2c54e98

Please sign in to comment.