Source code for spinn_front_end_common.interface.buffer_management.storage_objects.buffered_sending_region
# The size of the header of a message
from spinnman.messages.eieio.command_messages \
import EventStopRequest, HostSendSequencedData
from spinnman.messages.eieio.data_messages import EIEIODataHeader
from spinnman.messages.eieio import EIEIOType
from spinnman.constants import UDP_MESSAGE_MAX_SIZE
import bisect
import math
[docs]class BufferedSendingRegion(object):
""" A set of keys to be sent at given timestamps for a given region of\
data. Note that keys must be added in timestamp order or else an\
exception will be raised
"""
__slots__ = [
# The maximum size of any buffer
"_max_size_of_buffer",
# A dictionary of timestamp -> list of keys
"_buffer",
# A list of timestamps
"_timestamps",
# The current position in the list of timestamps
"_current_timestamp_pos",
# int stating the size of the buffer
"_buffer_size",
# int stating the total size of the buffered region
"_total_region_size",
# The maximum number of packets in any timestamp
"_max_packets_in_timestamp"
]
_HEADER_SIZE = EIEIODataHeader.get_header_size(
EIEIOType.KEY_32_BIT, is_payload_base=True)
# The number of bytes in each key to be sent
_N_BYTES_PER_KEY = EIEIOType.KEY_32_BIT.key_bytes # @UndefinedVariable
# The number of keys allowed (different from the actual number as there is
# an additional header)
_N_KEYS_PER_MESSAGE = (UDP_MESSAGE_MAX_SIZE -
(HostSendSequencedData.get_min_packet_length() +
_HEADER_SIZE)) / _N_BYTES_PER_KEY
def __init__(self, max_buffer_size):
self._max_size_of_buffer = max_buffer_size
# A dictionary of timestamp -> list of keys
self._buffer = dict()
# A list of timestamps
self._timestamps = list()
# The current position in the list of timestamps
self._current_timestamp_pos = 0
self._buffer_size = None
self._total_region_size = None
self._max_packets_in_timestamp = 0
@property
def buffer_size(self):
"""
property method for getting the max size of this buffer
"""
if self._buffer_size is None:
self._calculate_sizes()
return self._buffer_size
@property
def total_region_size(self):
""" Get the max size of this region
"""
if self._total_region_size is None:
self._calculate_sizes()
return self._total_region_size
@property
def max_buffer_size_possible(self):
""" Get the max possible size of a buffer from this region
"""
return self._max_size_of_buffer
def _calculate_sizes(self):
""" Deduce how big the buffer and the region needs to be
"""
size = 0
for timestamp in self._timestamps:
n_keys = self.get_n_keys(timestamp)
size += self.get_n_bytes(n_keys)
size += EventStopRequest.get_min_packet_length()
if size > self._max_size_of_buffer:
self._buffer_size = self._max_size_of_buffer
else:
self._buffer_size = size
self._total_region_size = size
[docs] def get_n_bytes(self, n_keys):
""" Get the number of bytes used by a given number of keys
:param n_keys: The number of keys
:type n_keys: int
"""
# Get the total number of messages
n_messages = int(math.ceil(float(n_keys) / self._N_KEYS_PER_MESSAGE))
# Add up the bytes
return ((self._HEADER_SIZE * n_messages) +
(n_keys * self._N_BYTES_PER_KEY))
[docs] def add_key(self, timestamp, key):
""" Add a key to be sent at a given time
:param timestamp: The time at which the key is to be sent
:type timestamp: int
:param key: The key to send
:type key: int
"""
if timestamp not in self._buffer:
bisect.insort(self._timestamps, timestamp)
self._buffer[timestamp] = list()
self._buffer[timestamp].append(key)
self._total_region_size = None
self._buffer_size = None
if len(self._buffer[timestamp]) > self._max_packets_in_timestamp:
self._max_packets_in_timestamp = len(self._buffer[timestamp])
[docs] def add_keys(self, timestamp, keys):
""" Add a set of keys to be sent at the given time
:param timestamp: The time at which the keys are to be sent
:type timestamp: int
:param keys: The keys to send
:type keys: iterable of int
"""
for key in keys:
self.add_key(timestamp, key)
@property
def n_timestamps(self):
""" The number of timestamps available
:rtype: int
"""
return len(self._timestamps)
@property
def timestamps(self):
""" The timestamps for which there are keys
:rtype: iterable of int
"""
return self._timestamps
[docs] def get_n_keys(self, timestamp):
""" Get the number of keys for a given timestamp
:param timestamp: the time stamp to check if there's still keys to\
transmit
"""
if timestamp in self._buffer:
return len(self._buffer[timestamp])
return 0
@property
def is_next_timestamp(self):
""" Determines if the region is empty
:return: True if the region is empty, false otherwise
:rtype: bool
"""
return self._current_timestamp_pos < len(self._timestamps)
@property
def next_timestamp(self):
""" The next timestamp of the data to be sent, or None if no more data
:rtype: int or None
"""
if self.is_next_timestamp:
return self._timestamps[self._current_timestamp_pos]
return None
[docs] def is_next_key(self, timestamp):
""" Determine if there is another key for the given timestamp
:param timestamp: the time stamp to check if there's still keys to\
transmit
:rtype: bool
"""
if timestamp in self._buffer:
return len(self._buffer[timestamp]) > 0
return False
@property
def next_key(self):
""" The next key to be sent
:rtype: int
"""
next_timestamp = self.next_timestamp
keys = self._buffer[next_timestamp]
key = keys.pop()
if len(keys) == 0:
del self._buffer[next_timestamp]
self._current_timestamp_pos += 1
return key
@property
def current_timestamp(self):
""" Get the current timestamp in the iterator
"""
return self._current_timestamp_pos
[docs] def rewind(self):
""" Rewind the buffer to initial position.
"""
self._current_timestamp_pos = 0
[docs] def clear(self):
""" Clears the buffer
"""
# A dictionary of timestamp -> list of keys
self._buffer = dict()
# A list of timestamps
self._timestamps = list()
# The current position in the list of timestamps
self._current_timestamp_pos = 0
self._buffer_size = None
self._total_region_size = None
@property
def max_packets_in_timestamp(self):
""" The maximum number of packets in any time stamp
"""
return self._max_packets_in_timestamp