Source code for pacman.operations.router_compressors.basic_route_merger
# Copyright (c) 2017-2019 The University of Manchester
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from spinn_utilities.progress_bar import ProgressBar
from spinn_machine import MulticastRoutingEntry
from pacman.model.routing_tables import (
MulticastRoutingTable, MulticastRoutingTables)
from pacman.exceptions import PacmanRoutingException
_32_BITS = 0xFFFFFFFF
_UPPER_16_BITS = 0xFFFF << 16 # upper 16 bits of 32
_LOWER_16_BITS = 0xFFFF
[docs]class BasicRouteMerger(object):
""" Merges routing tables entries via different masks and an\
exploration process
"""
__slots__ = []
def __call__(self, router_tables):
tables = MulticastRoutingTables()
previous_masks = dict()
progress = ProgressBar(
len(router_tables.routing_tables) * 2,
"Compressing Routing Tables")
# Create all masks without holes
allowed_masks = [_32_BITS - ((2 ** i) - 1) for i in range(33)]
# Check that none of the masks have "holes" e.g. 0xFFFF0FFF has a hole
for router_table in router_tables.routing_tables:
for entry in router_table.multicast_routing_entries:
if entry.mask not in allowed_masks:
raise PacmanRoutingException(
"Only masks without holes are allowed in tables for"
" BasicRouteMerger (disallowed mask={})".format(
hex(entry.mask)))
for router_table in progress.over(router_tables.routing_tables):
new_table = self._merge_routes(router_table, previous_masks)
tables.add_routing_table(new_table)
n_entries = len([
entry for entry in new_table.multicast_routing_entries
if not entry.defaultable])
# print("Reduced from {} to {}".format(
# len(router_table.multicast_routing_entries), n_entries))
if n_entries > 1023:
raise PacmanRoutingException(
"Cannot make table small enough: {} entries".format(
n_entries))
return tables
def _get_merge_masks(self, mask, previous_masks):
if mask in previous_masks:
return previous_masks[mask]
last_one = 33 - bin(mask).rfind('1')
n_bits = 16 - last_one
merge_masks = sorted(
[_LOWER_16_BITS - ((2 ** n) - 1) for n in range(n_bits - 1, 17)],
key=lambda x: bin(x).count("1"))
# print(hex(mask), [hex(m) for m in merge_masks])
previous_masks[mask] = merge_masks
return merge_masks
def _merge_routes(self, router_table, previous_masks):
merged_routes = MulticastRoutingTable(router_table.x, router_table.y)
keys_merged = set()
entries = router_table.multicast_routing_entries
for router_entry in entries:
if router_entry.routing_entry_key in keys_merged:
continue
mask = router_entry.mask
if mask & _UPPER_16_BITS == _UPPER_16_BITS:
for extra_bits in self._get_merge_masks(mask, previous_masks):
new_mask = _UPPER_16_BITS | extra_bits
new_key = router_entry.routing_entry_key & new_mask
new_n_keys = ~new_mask & _32_BITS
# Get candidates for this particular possible merge
potential_merges = self._mergeable_entries(
router_entry, entries, new_key, new_mask,
new_key + new_n_keys, keys_merged)
# Only do a merge if there's real merging to do
if len(potential_merges) > 1:
merged_routes.add_multicast_routing_entry(
MulticastRoutingEntry(
new_key, new_mask,
router_entry.processor_ids,
router_entry.link_ids, defaultable=False))
keys_merged.update([
route.routing_entry_key
for route in potential_merges])
break
else:
# print("Was not able to merge", hex(key))
merged_routes.add_multicast_routing_entry(router_entry)
keys_merged.add(router_entry.routing_entry_key)
else:
merged_routes.add_multicast_routing_entry(router_entry)
keys_merged.add(router_entry.routing_entry_key)
return merged_routes
def _mergeable_entries(
self, entry, entries, new_key, new_mask, new_last_key, merged):
# Check that all the cores on this chip have the same route as this is
# the only way we can merge here
potential_merges = set()
for entry_2 in entries:
key = entry_2.routing_entry_key
n_keys = ~entry_2.mask & _32_BITS
last_key = key + n_keys
masked_key = entry_2.routing_entry_key & new_mask
overlap = (min(new_last_key, last_key) - max(new_key, key)) > 0
in_range = new_key <= key and new_last_key >= last_key
if (new_key == masked_key and (
not in_range
or entry_2.routing_entry_key in merged
or entry.processor_ids != entry_2.processor_ids
or entry.link_ids != entry_2.link_ids)):
# Mismatched routes; cannot merge
return []
elif new_key == masked_key:
# This one is mergeable
potential_merges.add(entry_2)
elif overlap:
# Overlapping routes; cannot merge
return []
return potential_merges