def _send_messages(self, size, vertex, region, sequence_no): """ Send a set of messages """ # Get the sent messages for the vertex if vertex not in self._sent_messages: self._sent_messages[vertex] = BuffersSentDeque(region) sent_messages = self._sent_messages[vertex] # If the sequence number is outside the window, return no messages if not sent_messages.update_last_received_sequence_number(sequence_no): return list() # Remote the existing packets from the size available bytes_to_go = size for message in sent_messages.messages: if isinstance(message.eieio_data_message, EIEIODataMessage): bytes_to_go -= message.eieio_data_message.size else: bytes_to_go -= (message.eieio_data_message .get_min_packet_length()) # Add messages up to the limits while (vertex.is_next_timestamp(region) and not sent_messages.is_full and bytes_to_go > 0): space_available = min( bytes_to_go, constants.UDP_MESSAGE_MAX_SIZE - HostSendSequencedData.get_min_packet_length()) # logger.debug( # "Bytes to go {}, space available {}".format( # bytes_to_go, space_available)) next_message = self._create_message_to_send( space_available, vertex, region) if next_message is None: break sent_messages.add_message_to_send(next_message) bytes_to_go -= next_message.size # logger.debug("Adding additional buffer of {} bytes".format( # next_message.size)) # If the vertex is empty, send the stop messages if there is space if (not sent_messages.is_full and not vertex.is_next_timestamp(region) and bytes_to_go >= EventStopRequest.get_min_packet_length()): sent_messages.send_stop_message() # If there are no more messages, turn off requests for more messages if not vertex.is_next_timestamp(region) and sent_messages.is_empty(): # logger.debug("Sending stop") self._send_request(vertex, StopRequests()) # Send the messages for message in sent_messages.messages: # logger.debug("Sending message with sequence {}".format( # message.sequence_no)) self._send_request(vertex, message)
def _send_messages(self, size, vertex, region, sequence_no): """ Send a set of messages """ # Get the sent messages for the vertex if vertex not in self._sent_messages: self._sent_messages[vertex] = BuffersSentDeque(region) sent_messages = self._sent_messages[vertex] # If the sequence number is outside the window, return no messages if not sent_messages.update_last_received_sequence_number(sequence_no): return list() # Remote the existing packets from the size available bytes_to_go = size for message in sent_messages.messages: if isinstance(message.eieio_data_message, EIEIODataMessage): bytes_to_go -= message.eieio_data_message.size else: bytes_to_go -= ( message.eieio_data_message.get_min_packet_length()) # Add messages up to the limits while (vertex.is_next_timestamp(region) and not sent_messages.is_full and bytes_to_go > 0): space_available = min( bytes_to_go, constants.UDP_MESSAGE_MAX_SIZE - HostSendSequencedData.get_min_packet_length()) # logger.debug( # "Bytes to go {}, space available {}".format( # bytes_to_go, space_available)) next_message = self._create_message_to_send( space_available, vertex, region) if next_message is None: break sent_messages.add_message_to_send(next_message) bytes_to_go -= next_message.size # logger.debug("Adding additional buffer of {} bytes".format( # next_message.size)) # If the vertex is empty, send the stop messages if there is space if (not sent_messages.is_full and not vertex.is_next_timestamp(region) and bytes_to_go >= EventStopRequest.get_min_packet_length()): sent_messages.send_stop_message() # If there are no more messages, turn off requests for more messages if not vertex.is_next_timestamp(region) and sent_messages.is_empty(): # logger.debug("Sending stop") self._send_request(vertex, StopRequests()) # Send the messages for message in sent_messages.messages: # logger.debug("Sending message with sequence {}".format( # message.sequence_no)) self._send_request(vertex, message)
def read_eieio_command_message(data, offset): command_header = EIEIOCommandHeader.from_bytestring(data, offset) command_number = command_header.command if command_number == constants.EIEIO_COMMAND_IDS.DATABASE_CONFIRMATION.value: return DatabaseConfirmation.from_bytestring(command_header, data, offset + 2) # Fill in buffer area with padding elif command_number == constants.EIEIO_COMMAND_IDS.EVENT_PADDING.value: return PaddingRequest() # End of all buffers, stop execution elif command_number == constants.EIEIO_COMMAND_IDS.EVENT_STOP.value: return EventStopRequest() # Stop complaining that there is sdram free space for buffers elif command_number == constants.EIEIO_COMMAND_IDS.STOP_SENDING_REQUESTS.value: return StopRequests() # Start complaining that there is sdram free space for buffers elif command_number == constants.EIEIO_COMMAND_IDS.START_SENDING_REQUESTS.value: return StartRequests() # Spinnaker requesting new buffers for spike source population elif command_number == constants.EIEIO_COMMAND_IDS.SPINNAKER_REQUEST_BUFFERS.value: return SpinnakerRequestBuffers.from_bytestring(command_header, data, offset + 2) # Buffers being sent from host to SpiNNaker elif command_number == constants.EIEIO_COMMAND_IDS.HOST_SEND_SEQUENCED_DATA.value: return HostSendSequencedData.from_bytestring(command_header, data, offset + 2) # Buffers available to be read from a buffered out vertex elif command_number == constants.EIEIO_COMMAND_IDS.SPINNAKER_REQUEST_READ_DATA.value: return SpinnakerRequestReadData.from_bytestring(command_header, data, offset + 2) # Host confirming data being read form SpiNNaker memory elif command_number == constants.EIEIO_COMMAND_IDS.HOST_DATA_READ.value: return HostDataRead.from_bytestring(command_header, data, offset + 2) return EIEIOCommandMessage(command_header, data, offset + 2)
def add_message_to_send(self, message): """ Add a message to send. The message is converted to a sequenced\ message. :param message: The message to be added :type message:\ :py:class:`spinnman.messages.eieio.abstract_messages.AbstractEIEIOMessage` """ # If full, raise an exception if self.is_full: raise exceptions.SpinnFrontEndException("The buffer is full") # Create a sequenced message and update the sequence number self._sequence_lock.acquire() sequenced_message = HostSendSequencedData(self._region, self._sequence_number, message) self._sequence_number = ((self._sequence_number + 1) % _N_SEQUENCES) self._sequence_lock.release() # Add the sequenced message to the buffers self._buffers_sent.append(sequenced_message)
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 """ _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 = (constants.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 self.clear() @property def buffer_size(self): """ property method for getting the max size of this buffer :return: """ 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 :return: """ 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: """ return self._max_size_of_buffer def _calculate_sizes(self): """ Deduce how big the buffer and the region needs to be :return: """ 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 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)) 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 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 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 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 def rewind(self): """ Rewind the buffer to initial position. """ self._current_timestamp_pos = 0 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
def read_eieio_command_message(data, offset): """ Reads the content of an EIEIO command message and returns an object\ identifying the command which was contained in the packet, including\ any parameter, if required by the command :param data: data received from the network :type data: bytestring :param offset: offset at which the parsing operation should start :type offset: int :return: an object which inherits from EIEIOCommandMessage which contains\ parsed data received from the network :rtype: \ :py:class:`spinnman.messages.eieio.command_messages.eieio_command_message.EIEIOCommandMessage` """ command_header = EIEIOCommandHeader.from_bytestring(data, offset) command_number = command_header.command if (command_number == constants.EIEIO_COMMAND_IDS.DATABASE_CONFIRMATION.value): return DatabaseConfirmation.from_bytestring(command_header, data, offset + 2) # Fill in buffer area with padding elif (command_number == constants.EIEIO_COMMAND_IDS.EVENT_PADDING.value): return PaddingRequest() # End of all buffers, stop execution elif (command_number == constants.EIEIO_COMMAND_IDS.EVENT_STOP.value): return EventStopRequest() # Stop complaining that there is sdram free space for buffers elif (command_number == constants.EIEIO_COMMAND_IDS.STOP_SENDING_REQUESTS.value): return StopRequests() # Start complaining that there is sdram free space for buffers elif (command_number == constants.EIEIO_COMMAND_IDS.START_SENDING_REQUESTS.value): return StartRequests() # Spinnaker requesting new buffers for spike source population elif (command_number == constants.EIEIO_COMMAND_IDS.SPINNAKER_REQUEST_BUFFERS.value): return SpinnakerRequestBuffers.from_bytestring(command_header, data, offset + 2) # Buffers being sent from host to SpiNNaker elif (command_number == constants.EIEIO_COMMAND_IDS.HOST_SEND_SEQUENCED_DATA.value): return HostSendSequencedData.from_bytestring(command_header, data, offset + 2) # Buffers available to be read from a buffered out vertex elif (command_number == constants.EIEIO_COMMAND_IDS.SPINNAKER_REQUEST_READ_DATA.value): return SpinnakerRequestReadData.from_bytestring( command_header, data, offset + 2) # Host confirming data being read form SpiNNaker memory elif (command_number == constants.EIEIO_COMMAND_IDS.HOST_DATA_READ.value): return HostDataRead.from_bytestring(command_header, data, offset + 2) return EIEIOCommandMessage(command_header, data, offset + 2)
# The minimum size of any message - this is the headers plus one entry _MIN_MESSAGE_SIZE = (EIEIO32BitTimedPayloadPrefixDataMessage .get_min_packet_length()) # The size of the header of a message _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 # The number of keys allowed (different from the actual number as there is an # additional header) _N_KEYS_PER_MESSAGE = (constants.UDP_MESSAGE_MAX_SIZE - (HostSendSequencedData.get_min_packet_length() + _HEADER_SIZE) / _N_BYTES_PER_KEY) class BufferManager(object): """ Manager of send buffers """ def __init__(self, placements, routing_info, tags, transceiver): """ :param placements: The placements of the vertices :type placements:\ :py:class:`pacman.model.placements.placements.Placements` :param routing_infos: The routing keys of the vertices :type routing_infos:\