Source code for spinn_front_end_common.utilities.report_functions.bit_field_compressor_report

# Copyright (c) 2019 The University of Manchester
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
import os
import sys
from collections import defaultdict
from spinn_utilities.log import FormatAdapter
from spinn_front_end_common.abstract_models import AbstractHasAssociatedBinary
from spinn_front_end_common.data import FecDataView
from spinn_front_end_common.interface.provenance import (
    ProvenanceReader, ProvenanceWriter)
from .bit_field_summary import BitFieldSummary
from spinn_front_end_common.utilities.utility_objs import ExecutableType

logger = FormatAdapter(logging.getLogger(__name__))
_FILE_NAME = "bit_field_compressed_summary.rpt"
# provenance data item names

MERGED_NAME = "bit_fields_merged"
NOT_APPLICABLE = "N/A"


def generate_provenance_item(x, y, bit_fields_merged):
    """
    Generates a provenance item in the format BitFieldCompressorReport expects.

    :param x:
    :param y:
    :param bit_fields_merged:
    """
    with ProvenanceWriter() as db:
        db.insert_router(x, y, MERGED_NAME, bit_fields_merged)


[docs]def bitfield_compressor_report(): """ Generates a report that shows the impact of the compression of bitfields into the routing table. :return: a summary, or `None` if the report file can't be written :rtype: BitFieldSummary """ file_name = os.path.join(FecDataView.get_run_dir_path(), _FILE_NAME) try: with open(file_name, "w", encoding="utf-8") as f: _write_report(f) except IOError: logger.exception("Generate_placement_reports: Can't open file" " {} for writing.", _FILE_NAME)
def _merged_component(to_merge_per_chip, writer): """ Report how many bitfields were merged into the router. :param dict([int, int], int: to_merge_per_chip: number of bitfields that could be merged per chip :param ~io.FileIO writer: file writer. :return: tuple containing 4 elements. 1. min_bit_fields merged in a chip, 2. the max bit_fields merged in a chip, 3. the total bit_fields_merged into all the routers. 4. average number of bit-fields merged on the routers. :rtype: tuple(int or str, int or str, int or str, float or str) """ top_bit_field = 0 min_bit_field = sys.maxsize total_bit_fields_merged = 0 average_per_chip_merged = 0 n_chips = 0 to_merge_chips = set(to_merge_per_chip.keys()) found = False with ProvenanceReader() as db: for (x, y, merged) in db.get_router_by_chip( MERGED_NAME): if (x, y) not in to_merge_per_chip: continue to_merge = to_merge_per_chip[x, y] to_merge_chips.discard((x, y)) found = True writer.write( f"Chip {x}:{y} has {merged} bitfields out of {to_merge} " f"merged into it. Which is {merged / to_merge:.2%}\n") total_bit_fields_merged += int(merged) if merged > top_bit_field: top_bit_field = merged if merged < min_bit_field: min_bit_field = merged average_per_chip_merged += merged n_chips += 1 if found: average_per_chip_merged = ( float(average_per_chip_merged) / float(n_chips)) else: min_bit_field = NOT_APPLICABLE top_bit_field = NOT_APPLICABLE total_bit_fields_merged = NOT_APPLICABLE average_per_chip_merged = NOT_APPLICABLE if len(to_merge_chips) > 0: writer.write( f"The Chips {to_merge_chips} had bitfields.\n" "But no record was found of any attempt to merge them.\n") return (min_bit_field, top_bit_field, total_bit_fields_merged, average_per_chip_merged) def _compute_to_merge_per_chip(): """ :rtype: tuple(int, int, int, float or int) """ total_to_merge = 0 to_merge_per_chip = defaultdict(int) for partition in FecDataView.iterate_partitions(): for edge in partition.edges: splitter = edge.post_vertex.splitter for vertex in splitter.get_source_specific_in_coming_vertices( partition.pre_vertex, partition.identifier): if not isinstance(vertex, AbstractHasAssociatedBinary): continue if vertex.get_binary_start_type() == ExecutableType.SYSTEM: continue place = FecDataView.get_placement_of_vertex(vertex) to_merge_per_chip[place.chip] += 1 total_to_merge += 1 return total_to_merge, to_merge_per_chip def _before_merge_component(total_to_merge, to_merge_per_chip): """ :rtype: tuple(int, int, int, float or int) """ max_bit_fields_on_chip = 0 min_bit_fields_on_chip = sys.maxsize for bitfield_count in to_merge_per_chip.values(): if bitfield_count > max_bit_fields_on_chip: max_bit_fields_on_chip = bitfield_count if bitfield_count < min_bit_fields_on_chip: min_bit_fields_on_chip = bitfield_count if len(to_merge_per_chip) == 0: average = 0 else: average = float(total_to_merge) / float(len(to_merge_per_chip)) return max_bit_fields_on_chip, min_bit_fields_on_chip, average def _write_report(writer): """ Writes the report. :param ~io.FileIO writer: the file writer :return: a summary :rtype: BitFieldSummary """ total_to_merge, to_merge_per_chip = _compute_to_merge_per_chip() (max_to_merge_per_chip, low_to_merge_per_chip, average_per_chip_to_merge) = _before_merge_component( total_to_merge, to_merge_per_chip) (min_bit_field, top_bit_field, total_bit_fields_merged, average_per_chip_merged) = _merged_component(to_merge_per_chip, writer) writer.write( f"\n\nBefore merge there where {total_to_merge} bitfields on " f"{len(to_merge_per_chip)} Chips ranging from {max_to_merge_per_chip} " f"to {low_to_merge_per_chip} bitfields per chip with an average " f"of {average_per_chip_to_merge}") writer.write( f"\nSuccessfully merged {total_bit_fields_merged} bitfields ranging " f"from {top_bit_field} to {min_bit_field} bitfields per chip with an " f"average of {average_per_chip_merged}") if total_to_merge: if total_bit_fields_merged == NOT_APPLICABLE: writer.write(f"\nNone of the {total_to_merge} bitfields merged") else: writer.write("\nIn total {:.2%} of the bitfields merged".format( total_bit_fields_merged / total_to_merge)) return BitFieldSummary( lowest_per_chip=min_bit_field, max_per_chip=top_bit_field, total_merged=total_bit_fields_merged, total_to_merge=total_to_merge, max_to_merge_per_chip=max_to_merge_per_chip, low_to_merge_per_chip=low_to_merge_per_chip, average_per_chip_merged=average_per_chip_merged, average_per_chip_to_merge=average_per_chip_to_merge)