Example #1
0
    def generate_estimations(self, action_counts_path, ERT_path, output_path,
                             precision):
        print('\n=========================================')
        print('Generating energy estimation')
        print('=========================================')
        # load and parse access counts
        self.energy_reference_table = load(open(ERT_path), accelergy_loader)
        self.construct_action_counts(
            load(open(action_counts_path), accelergy_loader))
        INFO('design under evaluation:', self.design_name)
        INFO('processing action counts file:',
             os.path.abspath(action_counts_path))

        self.decimal_points = precision

        for name, action_count_list in self.action_counts.items():
            INFO('processing for component:', name)
            # the components in the list are stored differently in ERT
            processed_name = self.process_name(name)
            component_ERT = self.energy_reference_table[processed_name]
            # generate the total energy for the component
            component_energy = self.process_component_action_counts(
                action_count_list, component_ERT)

            self.energy_estimation_table[name] = round(component_energy,
                                                       self.decimal_points)

        # save results
        file_path = output_path + '/estimation.yaml'
        write_yaml_file(file_path, self.energy_estimation_table)
        print(
            '---> Finished: energy calculation finished, estimation saved to:\n',
            os.path.abspath(output_path))
Example #2
0
    def generate_ERTs(self, design_path, output_path, precision,
                      flatten_arch_flag):
        """
        main function to start the energy reference generator
        parses the input files
        produces the energy reference tables for components
        """
        print('\n=========================================')
        print('Generating energy reference tables')
        print('=========================================')

        # Interpret inputs
        self.design_path = design_path
        self.output_path = output_path
        self.decimal_place = precision

        # Load accelergy config and design specification
        self.config = config_file_checker()
        self.design = load(open(self.design_path), accelergy_loader)
        INFO('design loaded:', design_path)
        self.construct_compound_class_description(
            self.design['compound_components'])

        # Load the primitive classes library
        self.construct_primitive_class_description()

        # Parse the architecture description and save the parsed version if flag high
        self.construct_save_architecture_description(
            self.design['architecture'])
        if flatten_arch_flag:
            self.generate_easy_to_read_flattened_architecture()
            arch_file_path = self.output_path + '/' + 'flattened_architecture.yaml'
            flattened_architecture_yaml = {
                'flattened_architecture': {
                    'version': self.arch_version,
                    'components': self.easy_to_read_flattened_architecture
                }
            }
            write_yaml_file(arch_file_path, flattened_architecture_yaml)
            INFO('Architecture flattened ... saved to ', arch_file_path)

        # Instantiate the estimation plug-ins as intances of the corresponding plug-in classes
        self.instantiate_estimator_plug_ins()

        # Generate Energy Reference Tables for the components
        self.generate_ERTs_for_architecture()

        # Save the ERTs to ERT.yaml in the output directory
        ERT_file_path = self.output_path + '/' + 'ERT.yaml'
        ERT_dict = {
            'ERT': {
                'version': self.arch_version,
                'tables': self.energy_reference_table
            }
        }
        write_yaml_file(ERT_file_path, ERT_dict)
        print('---> Finished: ERT generation finished, ERT saved to:\n',
              os.path.abspath(output_path))
Example #3
0
    def instantiate_estimator_plug_ins(self):
        """
        instantiate a list of estimator plug-in objects for later queries
        estimator plug-in paths are specified in config file

        """
        for estimator_dir in self.config['estimator_plug_ins']:
            for root, directories, file_names in os.walk(estimator_dir):
                for file_name in file_names:
                    if '.estimator.yaml' in file_name:
                        INFO('estimator plug-in identified by: ',
                             root + os.sep + file_name)
                        estimator_spec = load(open(root + os.sep + file_name),
                                              accelergy_loader)
                        # validate the spec syntax
                        syntax_validators.validate_estimator_API(
                            estimator_spec)
                        for key, val in estimator_spec.items():
                            if not key == 'version':
                                estimator_info = val
                        module_name = estimator_info['module']
                        class_name = estimator_info['class']
                        file_path = root + '/' + module_name + '.py'
                        estimator_module = SourceFileLoader(
                            class_name, file_path).load_module()

                        if 'parameters' not in estimator_info:
                            estimator_obj = getattr(estimator_module,
                                                    class_name)()
                        else:
                            estimator_obj = getattr(
                                estimator_module,
                                class_name)(estimator_info['parameters'])

                        self.estimator_plug_ins.append(estimator_obj)
Example #4
0
 def eval_primitive_action_energy(self, estimator_plug_in_interface):
     """
     :param estimator_plug_in_interface: dictionary that adheres to
            Accelergy-external estimator interface format
     :return energy estimation of the action
     """
     best_accuracy = 0
     best_estimator = None
     for estimator in self.estimator_plug_ins:
         accuracy = estimator.primitive_action_supported(
             estimator_plug_in_interface)
         ASSERT_MSG(
             type(accuracy) is int or type(accuracy) is float,
             'Wrong plug-in accuracy: %s ...  Returned accuracy must be integers or floats'
             % (estimator))
         if accuracy > best_accuracy:
             best_accuracy = accuracy
             best_estimator = estimator
     if best_estimator is None:
         ERROR_CLEAN_EXIT('cannot find estimator plug-in:',
                          estimator_plug_in_interface,
                          'Available plug-ins:', self.estimator_plug_ins)
     energy = round(
         best_estimator.estimate_energy(estimator_plug_in_interface),
         self.decimal_place)
     if self.verbose:
         INFO('Received energy estimation for primitive class:\n',
              estimator_plug_in_interface, '\n estimated by:',
              best_estimator, ' ---> estimated energy:', str(energy), 'pJ')
     return energy
Example #5
0
 def construct_primitive_class_description(self):
     """
     construct a dictionary for primitive classes Accelergy
     primitive class file paths are specified in config file
     """
     # load in the stored primitive classes
     primitive_class_paths = self.config['primitive_components']
     for pc_path in primitive_class_paths:
         print(pc_path)
         # primitive component library file is directly specified
         if '.yaml' in pc_path:
             self.expand_primitive_component_lib_info(pc_path)
         else:
             # primitive component dir is specified, need recursive search
             for root, directories, file_names in os.walk(pc_path):
                 for file_name in file_names:
                     if '.lib.yaml' in file_name:
                         pc_path = root + os.sep + file_name
                         primitive_component_list = load(
                             open(pc_path), accelergy_loader)
                         syntax_validators.validate_primitive_classes(
                             primitive_component_list)
                         for idx in range(
                                 len(primitive_component_list['classes'])):
                             pc_name = primitive_component_list['classes'][
                                 idx]['name']
                             if pc_name in self.primitive_class_description:
                                 WARN(pc_name, 'redefined in', pc_path)
                             self.primitive_class_description[
                                 pc_name] = primitive_component_list[
                                     'classes'][idx]
                         INFO('primitive component file parsed: ', pc_path)
Example #6
0
 def process_name(self, name):
     """
     list component: reformat the component names to their format stored in ERTs
     standalone component: no modifications
     """
     if '[' not in name and ']' not in name:
         ASSERT_MSG(name in self.energy_reference_table,
                    "Cannot find %s's ERT" % name)
         return name
     else:
         new_name = ''
         hierarchical_name_list = name.split('.')
         for name_segment in hierarchical_name_list:
             base_name_segment = EnergyCalculator.belongs_to_list(
                 name_segment)
             new_name += base_name_segment + '.'
         new_name = new_name[:-1]
         if self.flattened_list is not None:
             ASSERT_MSG(
                 new_name in self.flattened_list,
                 "According to the flattened architecture, %s is not a legal name "
                 % name)
             saved_entry = self.flattened_list[new_name]
             for idx in range(len(saved_entry['format'])):
                 range_location = saved_entry['format'][idx]
                 range_min = int(saved_entry['range'][idx][0])
                 range_max = int(saved_entry['range'][idx][1])
                 curr_idx = int(hierarchical_name_list[range_location][hierarchical_name_list[range_location].find('[')+1:\
                                                        hierarchical_name_list[range_location].find(']')])
                 ASSERT_MSG(
                     range_max >= curr_idx >= range_min,
                     'Invalid list component name %s, index out of bound' %
                     name)
         INFO('list component detected:', name, 'projected into', new_name)
         return new_name
def v02_validate_top_level_architecture_description(architecture_description):
    INFO("architecture description file version: 0.2, checking syntax...")
    if 'subtree' not in architecture_description:
        ERROR_CLEAN_EXIT(
            'v0.2 error: architecture description...',
            'Architecture tree nodes should be the value of top level key "subtree", '
            '"subtree" not found at top-level')
    # top-level subtree should only have one node
    ASSERT_MSG((
        isinstance(architecture_description['subtree'], list)
        and len(architecture_description['subtree']) == 1
    ), 'v0.2 error: top-level subtree must be a single element list that contains the design name'
               )
    if 'local' in architecture_description:
        ERROR_CLEAN_EXIT(
            'v0.2 error: architecture description...',
            'The first level list of your architecture description should only have a subtree, '
            'which is your design\'s root node')

    # design name syntax check
    raw_architecture_description = architecture_description['subtree'][0]
    if 'name' not in raw_architecture_description:
        ERROR_CLEAN_EXIT(
            "v0.2 error: architecture description...\n"
            "please specify the design name as top-level key-value pair =>  name: <design_name>"
        )
    design_name = raw_architecture_description['name']
    if 'local' in raw_architecture_description:
        ASSERT_MSG(
            isinstance(raw_architecture_description['local'], list),
            "v0.2 error: %s.local has to be a list of components" %
            design_name)
    if 'subtree' in raw_architecture_description:
        ASSERT_MSG(isinstance(raw_architecture_description['subtree'], list),
                   "v0.2 error: %s.subtree has to be a list" % design_name)
Example #8
0
 def generate_ERTs_for_architecture(self):
     """
     For each component in the architecture, generate an ERT if needed
     generated ERTs are saved to self.energy_reference_table
     """
     for component_name, component_info in self.architecture_description.items(
     ):
         component_info['name'] = component_name
         ERT_check_result = self.ERT_existed(component_name)
         if not ERT_check_result[0]:
             INFO(component_info['name'], ' ---> Generating new ERT')
             is_primitive_class = True if self.is_primitive_class(
                 component_info['class']) else False
             ERT = self.generate_component_ert(component_info,
                                               is_primitive_class)
             self.energy_reference_table[ERT_check_result[1]] = ERT
             INFO(component_info['name'], ' ---> New ERT generated')
Example #9
0
def config_file_v02():
    possible_config_dirs = [
        '.' + os.sep,
        os.path.expanduser('~') + '/.config/accelergy/'
    ]
    config_file_name = 'accelergy_config.yaml'
    for possible_dir in possible_config_dirs:
        if os.path.exists(possible_dir + config_file_name):
            original_config_file_path = possible_dir + config_file_name
            original_content = load(open(original_config_file_path),
                                    accelergy_loader)
            INFO('config file located:', original_config_file_path)
            print('config file content: \n', original_content)
            if 'version' not in original_content:
                ERROR_CLEAN_EXIT(
                    'config file has no version number, cannot proceed')
            file_version = original_content['version']
            if file_version == 0.1 or file_version == 1.0:  # probably exist 1.0 in the very initial version
                ERROR_CLEAN_EXIT(
                    'config file version outdated. Latest version is 0.2.'
                    '\nPlease delete the original file, and run accelergy to create a new default config file.'
                    '\nPlease ADD YOUR USER_DEFINED file paths BACK to the updated config file at '
                    '~/.config/accelergy/accelergy_config.yaml')
            else:
                return original_content
    else:
        create_folder(possible_config_dirs[1])
        config_file_path = possible_config_dirs[1] + config_file_name
        curr_file_path = os.path.abspath(__file__)
        accelergy_share_folder_path = os.path.abspath(
            curr_file_path + '../../../../../../share/accelergy/')
        default_estimator_path = os.path.abspath(accelergy_share_folder_path +
                                                 '/estimation_plug_ins/')
        default_pc_lib_path = os.path.abspath(accelergy_share_folder_path +
                                              '/primitive_component_libs/')
        config_file_content = {
            'version': 0.2,
            'estimator_plug_ins': [default_estimator_path],
            'primitive_components': [default_pc_lib_path]
        }
        INFO('Accelergy creating default config at:',
             possible_config_dirs[1] + config_file_name, 'with:\n',
             config_file_content)
        write_yaml_file(config_file_path, config_file_content)
        return config_file_content
Example #10
0
 def process_name(self, name):
     """
     list component: reformat the component names to their format stored in ERTs
     standalone component: no modifications
     """
     if '[' not in name and ']' not in name:
         return name
     else:
         new_name = ''
         hierarchical_name_list = name.split('.')
         for name_segment in hierarchical_name_list:
             base_name_segment = EnergyCalculator.belongs_to_list(
                 name_segment)
             new_name += base_name_segment + '.'
         new_name = new_name[:-1]
         INFO('list component detected:', name, 'projected into', new_name)
         return new_name
Example #11
0
    def generate_estimations(self, araw_action_counts, raw_ERT, output_path,
                             precision, raw_flattened_arch):
        print('\n=========================================')
        print('Generating energy estimation')
        print('=========================================')
        # load and parse access counts
        self.extract_ERT(raw_ERT['ERT'])
        self.construct_action_counts(araw_action_counts)
        self.decimal_points = precision
        if raw_flattened_arch is None:
            WARN(
                'flattened architecture is not given or --enable_flattened_arch not set high, will not perform legal component name check'
            )
        else:
            self.flattened_list = {}
            self.load_flattened_arch(
                raw_flattened_arch['flattened_architecture']['components'])

        for name, action_count_list in self.action_counts.items():
            INFO('processing for component:', name)
            # the components in the list are stored differently in ERT
            processed_name = self.process_name(name)
            component_ERT = self.energy_reference_table[processed_name]
            # generate the total energy for the component
            component_energy = self.process_component_action_counts(
                action_count_list, component_ERT)
            self.energy_estimation_table[name] = round(component_energy,
                                                       self.decimal_points)

        # save results
        file_path = output_path + '/estimation.yaml'
        write_yaml_file(
            file_path, {
                'energy_estimation': {
                    'components': self.energy_estimation_table,
                    'version': self.action_counts_version
                }
            })
        print(
            '---> Finished: energy calculation finished, estimation saved to:\n',
            os.path.abspath(output_path))
Example #12
0
def v01_validate_top_level_architecture_description(
        architecture_description_list):
    INFO("architecture description file version: 0.1")
    if 'nodes' not in architecture_description_list:
        ERROR_CLEAN_EXIT('v0.1 error: architecture description...',
                         '"nodes" not found at top-level')
    if not len(architecture_description_list['nodes']) == 1:
        ERROR_CLEAN_EXIT(
            'v0.1 error: architecture description...',
            'The first level list of your architecture description should only have one node, '
            'which is your design\'s root node')
    raw_architecture_description = architecture_description_list['nodes'][0]
    if 'nodes' not in raw_architecture_description:
        ERROR_CLEAN_EXIT('v0.1 error: architecture description...',
                         'second-level design tree must contain nodes')
    # design name syntax check
    if 'name' not in raw_architecture_description:
        ERROR_CLEAN_EXIT(
            "v0.1 error: architecture description..."
            "please specify the design name as top-level key-value pair =>  name: <design_name>"
        )
Example #13
0
    def generate_easy_to_read_flattened_architecture_ERT_summary(self):
        easy_to_read_flattened_architecture = {}
        list_names = {}
        ERT_summary = {}
        for component_name, component_info in self.architecture_description.items(
        ):

            if '[' not in component_name or ']' not in component_name:
                easy_to_read_flattened_architecture[component_name] = deepcopy(
                    component_info)
                if self.show_ERT_summary:
                    ERT_summary[component_name] = self.generate_ERT_summary(
                        component_name)
            else:
                name_base = EnergyReferenceTableGenerator.remove_brackets(
                    component_name)
                idx_list = []
                for match in re.finditer(r'\[\w+\]', component_name):
                    idx_list.append(
                        int(component_name[match.start() + 1:match.end() - 1]))
                if name_base not in list_names:
                    list_names[name_base] = {}
                    list_names[name_base]['format'] = []
                    parts = component_name.split('.')
                    for part_idx in range(len(parts)):
                        if '[' and ']' in parts[part_idx]:
                            list_names[name_base]['format'].append(part_idx)
                    list_names[name_base]['idx_list'] = idx_list
                else:
                    i = 0
                    for existing_idx in list_names[name_base]['idx_list']:
                        if idx_list[i] > existing_idx:
                            list_names[name_base]['idx_list'][i] = idx_list[i]
                        i += 1

        for name_base, list_info in list_names.items():
            ranged_name_list = name_base.split('.')
            max_name_list = name_base.split('.')
            for idx in range(len(list_info['format'])):
                range_location = list_info['format'][idx]
                range_max = list_info['idx_list'][idx]
                ranged_name_list[range_location] += '[0..' + str(
                    range_max) + ']'
                max_name_list[range_location] += '[' + str(range_max) + ']'
            sep = '.'
            ranged_name_str = sep.join(ranged_name_list)
            max_name_str = sep.join(max_name_list)
            easy_to_read_flattened_architecture[
                ranged_name_str] = self.architecture_description[max_name_str]
            if self.show_ERT_summary:

                ERT_summary[ranged_name_str] = self.generate_ERT_summary(
                    name_base)

        if self.show_ERT_summary:
            ERT_summary_file = {
                'ERT_summary': {
                    'version': self.compound_class_version,
                    'ERT summaries': ERT_summary
                }
            }
            write_yaml_file(self.output_path + '/ERT_summary.yaml',
                            ERT_summary_file)
            INFO('ERT summary saved to ',
                 self.output_path + '/ERT_summary.yaml')
        if self.flattened_arch:
            arch_file_path = self.output_path + '/' + 'flattened_architecture.yaml'
            flattened_architecture_yaml = {
                'flattened_architecture': {
                    'version': self.arch_version,
                    'components': easy_to_read_flattened_architecture
                }
            }
            write_yaml_file(arch_file_path, flattened_architecture_yaml)
            INFO('Architecture flattened ... saved to ', arch_file_path)
Example #14
0
def v02_compound_component_constructor(self, compound_component_info):
    """
    given the physical information of a compound component, the compound component object is constructed according
    to its compound classes, all compound properties are fully resolved and subcomponent definitions are included
    1. compound attributes are all assigned with values
    2. compound action argument ranges are all assigned values/ compound action arguments are static values
    3. subcomponent attributes values are assigned, as they only depend on compound attributes
        - subcomponent attributes can be:
            1. numbers
            2. string bindings to compound component attributes
            3. arithmetic operations that contain string bindings and numbers
    3. subcomponent definition of the compound actions are included

    :param compound_component_info: physical specification of a compound component
    :return: fully defined compound component
    """
    compound_component_name = compound_component_info['name']
    compound_class_info = self.compound_class_description[
        compound_component_info['class']]
    compound_component_definition = deepcopy(
        compound_class_info)  # a copy of default class definition
    compound_component_definition['class'] = compound_component_info['class']
    compound_component_definition['attributes'] = deepcopy(
        compound_component_info['attributes'])

    # fully defined attribute values
    compound_attributes = compound_component_definition['attributes']

    # process subcomponent name format
    #     if subcomponent is a list, expand the list of subcomponents (list tail index can be arithmetic operantions)
    #     else keep the subcomponent name

    subcomponents = deepcopy(compound_component_definition['subcomponents'])
    # check if any sub-compound-component attribute is not specified in the top-level, apply defualt value specified
    # in the class definition

    list_of_new_components = []
    list_of_to_remove_components = []

    for subcomponent_idx in range(len(subcomponents)):
        subcomponent = subcomponents[subcomponent_idx]
        list_length, subcomponent_name_base = v02_is_component_list(
            subcomponent['name'], compound_attributes)
        if subcomponent_name_base is not None:
            list_of_to_remove_components.append(subcomponent)
            # INFO('list component name: ', subcomponent['name'], 'detected in compound class: ', compound_component_info['class'])
            for i in range(list_length):
                new_component = deepcopy(subcomponent)
                new_component['name'] = subcomponent_name_base + '[' + str(
                    i) + ']'
                list_of_new_components.append(new_component)
    for comp in list_of_to_remove_components:
        subcomponents.remove(comp)
    for comp in list_of_new_components:
        subcomponents.append(comp)

    # process the subcomponent attribute values
    # subcomponent attributes can be:
    #     1. numbers
    #     2. string bindings to compound component attributes
    #     3. arithmetic operations that contain string bindings and numbers
    compound_component_definition['subcomponents'] = {}
    for subcomponent in subcomponents:
        subcomponent_name = subcomponent['name']
        sub_component_attributes = subcomponent['attributes']
        for sub_attr_name, sub_attr_val in sub_component_attributes.items():
            if type(sub_attr_val) is str:
                # subcomponent attributes can be computed in terms of upper-level compound attributes
                op_type, op1, op2 = parse_expression_for_arithmetic(
                    sub_attr_val, compound_attributes)
                if op_type is not None:
                    sub_component_attributes[
                        sub_attr_name] = process_arithmetic(op1, op2, op_type)
                    # INFO(compound_component_name, 'sub-attribute', sub_attr_name, 'processed as arithmetic operation')
                else:
                    try:
                        sub_component_attributes[
                            sub_attr_name] = compound_attributes[sub_attr_val]
                        # INFO(compound_component_name, 'sub-attribute', sub_attr_name,'processed as binding')
                    except KeyError:
                        ERROR_CLEAN_EXIT(
                            'cannot find bindings from upper-level attribute names',
                            '{', sub_attr_name, ':', sub_attr_val, '}')
        # process default sub-component-component attribute values that are not specified in the top-level
        # default values can be :
        #   (1) numerical values
        #   (2) arithmetic operations of other sub-compound-component attribute values
        sub_class = subcomponent['class']
        if sub_class in self.compound_class_description:
            sub_class_description = deepcopy(
                self.compound_class_description[sub_class])
            for attr_name, default_attr_val in sub_class_description[
                    'attributes'].items():
                if attr_name not in subcomponent['attributes']:
                    if type(default_attr_val) is str:
                        op_type, op1, op2 = parse_expression_for_arithmetic(
                            default_attr_val, subcomponent['attributes'])
                        if op_type is not None:
                            default_attr_val = process_arithmetic(
                                op1, op2, op_type)
                            # INFO(compound_component_name, 'sub-attribute', sub_attr_name, 'processed as arithmetic operation')
                        else:
                            try:
                                default_attr_val = subcomponent['attributes'][
                                    default_attr_val]
                                # INFO(compound_component_name, 'sub-attribute', sub_attr_name,'processed as binding')
                            except KeyError:
                                WARN(
                                    'did not find bindings of the specified default attribute value for class: ',
                                    sub_class, '---> {', attr_name, ':',
                                    default_attr_val, '}, '
                                    '   Keep the original specifications')
                    subcomponent['attributes'][attr_name] = default_attr_val
        compound_component_definition['subcomponents'][
            subcomponent_name] = subcomponent
        # check if the subcomponent name is a list
        # list_length, name_base = v02_is_component_list(subcomponent_name, compound_attributes)
        # if subcomponent_name == 'Y_memory_controller[0..total_PEs-1]':
        #     print('----------------------', list_length, name_base)
        # if name_base is None:
        #     compound_component_definition['subcomponents'][subcomponent_name] = subcomponent
        # else:
        #     for item_idx in range(list_length):
        #         new_subcomponent_name = name_base + '[' + str(item_idx) + ']'
        #         compound_component_definition['subcomponents'][new_subcomponent_name] = deepcopy(subcomponent)

    # top-level compound component will not have 'actions' specified in the component info
    #      because accelergy needs to generate the energy values for all possible actions (and arguments)
    # the actions in the component class description is therefore processed
    #     action arguments can be:
    #             1. numbers
    #             2. string bindings to compound attributes (its own attributes)
    #     action arguments cannot be arithmetic operations
    if 'actions' not in compound_component_info:
        # if there is no actions specified in the compound component info
        compound_actions = compound_component_definition['actions']
        for c_action in compound_actions:
            c_action_name = c_action['name']
            if 'arguments' in c_action:
                c_action_args = c_action['arguments']
                for c_action_arg_name, c_action_arg_range in c_action_args.items(
                ):
                    c_action_args[c_action_arg_name], detect_arg_range_binding = \
                        self.map_arg_range_bounds(c_action_arg_range, compound_attributes)
                    if detect_arg_range_binding:
                        INFO(compound_component_name, 'action:', c_action_name,
                             'arg:', c_action_arg_name,
                             'range interpreted as:',
                             c_action_args[c_action_arg_name])
    # low-level compound components will have 'actions' assigned, since top-level action will be interpreted as
    # one or more defined low-level compound action
    #     no change should be added as the action arguments should be defined already, so the required action list
    #     from component info is copied, with the definition of the action retrieved from class description
    else:
        compound_component_definition['actions'] = deepcopy(
            compound_component_info['actions'])
        for action in compound_component_definition['actions']:
            action_name = action['name']
            for class_action_def in compound_class_info['actions']:
                if class_action_def['name'] == action_name:
                    action['subcomponents'] = deepcopy(
                        class_action_def['subcomponents'])
    return compound_component_definition
Example #15
0
def main():

    parser = argparse.ArgumentParser(
        description=
        'Accelergy is an architecture-level energy estimator for accelerator designs. Accelergy allows '
        ' users to describe the architecture of a design with user-defined compound components and generates energy '
        'estimations according to the workload-generated action counts.')
    parser.add_argument(
        '-o',
        '--outdir',
        type=str,
        default='./',
        help='Path to output directory that stores '
        'the ERT and/or flattened_architecture and/or energy estimation. '
        'Default is current directory.')
    parser.add_argument(
        '-p',
        '--precision',
        type=int,
        default='3',
        help='Number of decimal points for generated energy values. '
        'Default is 3.')
    parser.add_argument(
        '-v',
        '--verbose',
        type=int,
        default=0,
        help=
        'If set to 1, Accelergy outputs the interactions between the estimation plug-ins. '
        'Default is 0')
    parser.add_argument(
        '-s',
        '--ERT_summary',
        type=int,
        default=1,
        help='If set to 1, Accelergy outputs an easy-to-read '
        'ERT summary that contains the average, min and max energy/action'
        'for all the actions of all the components. '
        'Default is 1')
    parser.add_argument(
        '--enable_flattened_arch',
        type=int,
        default='0',
        help=
        'If set to 1, Accelergy outputs an architecture summary in the output directory and checks'
        ' the validity of component names in the action counts file. '
        'The flattened architecture includes all the interpreted attribute values and classes '
        'for all the components in the design. '
        'Default is 0.')

    parser.add_argument(
        'files',
        nargs='*',
        help='list of input files in arbitrary order.'
        'Accelergy parses the top keys of the files to decide the type of input the file describes, '
        'e.g., architecture description, '
        'compound component class descriptions, etc. ')

    args = parser.parse_args()
    path_arglist = args.files
    output_path = args.outdir
    precision = args.precision
    verbose = args.verbose
    flatten_arch_flag = args.enable_flattened_arch
    ERT_summary = args.ERT_summary

    print(
        '\n#===================================================================================#'
    )
    print(
        '#=========================== Running Accelergy =====================================#'
    )
    print(
        '#===================================================================================#\n'
    )

    raw_architecture_description, \
    raw_compound_class_description,\
    raw_action_counts,\
    raw_ERT,\
    raw_flattened_arch = interpret_input_path(path_arglist)
    INFO('Summary of collected inputs:'
         '\n   Architecture description found: %s '
         '\n   Compound component description found: %s '
         '\n   Action counts found: %s'
         '\n   ERT found: %s '
         '\n   Flattened architecture found: %s' %
         (raw_architecture_description is not None,
          raw_compound_class_description is not None, raw_action_counts
          is not None, raw_ERT is not None, raw_flattened_arch is not None))

    if raw_ERT is not None and raw_action_counts is not None:
        INFO('Accelergy found ERT and ACTION COUNTS '
             '\n----------> DIRECTLY PERFORM ENERGY ESTIMATION')
        if flatten_arch_flag == 0:
            raw_flattened_arch = None
        else:
            if raw_flattened_arch is None:
                ERROR_CLEAN_EXIT(
                    'enable_flattened_arch flag is set high, '
                    'but no flattened architecture yaml file provided')
        estimator = EnergyCalculator()
        estimator.generate_estimations(raw_action_counts, raw_ERT, output_path,
                                       precision, raw_flattened_arch)

    elif raw_compound_class_description is not None and raw_architecture_description is not None:
        if raw_action_counts is None:
            INFO(
                'Accelergy found ARCHITECTURE and COMPOUND COMPONENT but NO ERT or ACTION COUNTS  '
                '\n---------->  PERFORM ERT GENERATION')
        else:
            INFO(
                'Accelergy found ARCHITECTURE, COMPOUND COMPONENT, and ACTION COUNTS but NO ERT  '
                '\n----------> PERFORM ERT GENERATION AND ENERGY ESTIMATION')

        generator = EnergyReferenceTableGenerator()
        generator.generate_ERTs(raw_architecture_description,
                                raw_compound_class_description, output_path,
                                precision, flatten_arch_flag, verbose,
                                ERT_summary)

        if raw_action_counts is not None:
            ert_path = output_path + '/' + 'ERT.yaml'
            raw_ERT = load(open(ert_path), accelergy_loader)
            if flatten_arch_flag == 0:
                raw_flattened_arch = None
            else:
                arch_path = output_path + '/' + 'flattened_architecture.yaml'
                raw_flattened_arch = load(open(arch_path), accelergy_loader)
            estimator = EnergyCalculator()
            estimator.generate_estimations(raw_action_counts, raw_ERT,
                                           output_path, precision,
                                           raw_flattened_arch)
    else:
        INFO('Not enough inputs to start computations. ')
Example #16
0
def interpret_input_path(path_arglist):
    raw_architecture_description = None
    raw_compound_class_description = None
    raw_action_counts = None
    raw_ERT = None
    raw_flattened_arch = None

    available_keys = [
        'architecture', 'compound_components', 'action_counts', 'ERT',
        'flattened_architecture'
    ]

    for file_path in path_arglist:

        file = load(open(file_path), accelergy_loader)
        for key in file:

            if key == 'architecture':
                content = file[key]
                #-------------------------------------------------------------------------
                # check syntax of input file
                #-------------------------------------------------------------------------
                ASSERT_MSG(
                    'version' in content,
                    'File content not legal: %s, %s must contain '
                    '"version" key' % (file_path, key))
                tree_root_name = None
                if content['version'] == 0.1:
                    tree_root_name = 'nodes'
                if content['version'] == 0.2:
                    tree_root_name = 'subtree'
                ASSERT_MSG(
                    tree_root_name is not None, 'File content not legal: %s, '
                    'version %s not supported' %
                    (file_path, content['version']))
                ASSERT_MSG(
                    tree_root_name in content,
                    'File content not legal: %s, architecture description must contain %s key'
                    % (file_path, tree_root_name))
                ASSERT_MSG(
                    type(content[tree_root_name]) is list,
                    'File content not legal: %s, %s key must have value of type list'
                    % (file_path, tree_root_name))
                #-------------------------------------------------------------------------
                # merge input file
                #-------------------------------------------------------------------------
                if raw_architecture_description is None:
                    raw_architecture_description = {'architecture': file[key]}
                else:
                    ERROR_CLEAN_EXIT(
                        'File content not legal: %s. '
                        '\n Second architecture description detected... '
                        'Only one architecture is allowed...' % (file_path))
                    # ASSERT_MSG(raw_architecture_description['architecture']['version'] == content['version'],
                    #            'File content not legal: %s, versions of two %s'
                    #            'related file do not match'%(file_path, key))
                    # raw_architecture_description[key].update(file[key])
            if key == 'compound_components':
                content = file[key]
                file_reload = load(open(file_path), accelergy_loader_ordered)
                #-------------------------------------------------------------------------
                # check syntax of input file
                #-------------------------------------------------------------------------
                ASSERT_MSG(
                    'version' in content and 'classes' in content,
                    'File content not legal: %s, %s must contain '
                    '"version" and "classes" keys' % (file_path, key))
                ASSERT_MSG(
                    type(content['classes']) is list,
                    'File content not legal: %s, "classes" key must have value of type list'
                    % file_path)
                #-------------------------------------------------------------------------
                # merge input file
                #-------------------------------------------------------------------------
                if raw_compound_class_description is None:
                    raw_compound_class_description = {
                        'compound_components': file_reload[key]
                    }
                else:
                    ASSERT_MSG(
                        raw_compound_class_description['compound_components']
                        ['version'] == content['version'],
                        'File content not legal: %s, versions of two %s '
                        'related file do not match' % (file_path, key))

                    raw_compound_class_description[key]['classes'].extend(
                        file_reload[key]['classes'])

            if key == 'action_counts':
                content = file[key]
                #-------------------------------------------------------------------------
                # check syntax of input file
                #-------------------------------------------------------------------------
                ASSERT_MSG(
                    'version' in content,
                    'File content not legal: %s, %s must contain '
                    '"version" key ' % (file_path, key))
                if content['version'] == 0.1:
                    ASSERT_MSG(
                        'nodes' in content,
                        'v0.1 error... File content not legal: %s, %s must contain '
                        '"nodes" key ' % (file_path, key))
                    if raw_action_counts is None:
                        raw_action_counts = {key: content}
                    else:
                        ASSERT_MSG(
                            raw_action_counts[key]['version'] ==
                            content['version'],
                            'File content not legal: %s, versions of two %s'
                            'related files do not match' % (file_path, key))
                        if "nodes" in content:
                            if "nodes" in raw_action_counts[key]:
                                raw_action_counts[key]["nodes"].extend(
                                    content["nodes"])
                            else:
                                raw_action_counts[key]["nodes"] = content[
                                    "nodes"]

                if content['version'] == 0.2:

                    ASSERT_MSG(
                        'local' in content or 'subtree' in content,
                        'File content not legal: %s, %s must contain '
                        '"local"/"subtree" key ' % (file_path, key))

                    if "local" in content:
                        ASSERT_MSG(
                            type(content["local"]) is list,
                            'File content not legal: %s, "local" key must have value of type list'
                            % (file_path))

                    if "subtree" in content:
                        ASSERT_MSG(
                            type(content["subtree"]) is list,
                            'File content not legal: %s, "subtree" key must have value of type list'
                            % (file_path))

                    if raw_action_counts is None:
                        raw_action_counts = {key: content}
                    else:
                        ASSERT_MSG(
                            raw_action_counts[key]['version'] ==
                            content['version'],
                            'File content not legal: %s, versions of two %s'
                            'related files do not match' % (file_path, key))
                        if "local" in content:
                            if "local" in raw_action_counts[key]:
                                raw_action_counts[key]["local"].extend(
                                    content["local"])
                            else:
                                raw_action_counts[key]["local"] = content[
                                    "local"]
                        if "subtree" in content:
                            if "subtree" in raw_action_counts[key]:
                                raw_action_counts[key]["subtree"].extend(
                                    content["subtree"])
                            else:
                                raw_action_counts[key]["subtree"] = content[
                                    "subtree"]

            if key == 'ERT':
                content = file[key]
                ASSERT_MSG(
                    'version' in content and 'tables' in content,
                    'File content not legal: %s, ERT must contain '
                    '"version" and "tables" keys' % file_path)
                ASSERT_MSG(
                    type(content['tables'] is dict),
                    'File content not legal: %s, "tables" key must have value of type dict'
                    % file_path)
                if raw_ERT is None:
                    raw_ERT = {key: file[key]}
                else:
                    ASSERT_MSG(
                        raw_ERT[key]['version'] == content['version'],
                        'File content not legal: %s, versions of two %s '
                        'related files do not match' % (file_path, key))
                    raw_ERT[key]['tables'].update(file[key]['tables'])

            if key == 'flattened_architecture':
                content = file[key]
                ASSERT_MSG(
                    'version' in content and 'components' in content,
                    'File content not legal: %s, flattened_architecture description must contain '
                    '"version" and "tables" keys' % file_path)
                ASSERT_MSG(
                    type(content['components'] is dict),
                    'File content not legal: %s, "components" key must have value of type dict'
                    % file_path)
                if raw_flattened_arch is None:
                    raw_flattened_arch = {key: file[key]}
                else:
                    ASSERT_MSG(
                        raw_ERT['version'] == content['version'],
                        'File content not legal: %s, versions of two %s '
                        'related files do not match' % (file_path, key))
                    raw_flattened_arch[key]['components'].update(
                        file[key]['components'])
            if key not in available_keys:
                WARN('File contains unrecognized information:', file_path, key,
                     '\n Accelergy only recognizes the following keys',
                     available_keys)
            else:
                INFO('%s related info found in %s' % (key, file_path))

    return raw_architecture_description, raw_compound_class_description, raw_action_counts, raw_ERT, raw_flattened_arch
Example #17
0
    def construct_new_leaf_node_description(self, full_name, leaf_description,
                                            shared_attr_dict):
        """
        Interprets the architecture component
           1. apply the default values specified in the compound and primitive classes
           2. apply shared attributes projected from upper hierarchy
           3. check if there is any arithmetic expressions in the attribute
                    bindings have to be amongst the attribute names
           4. updates the architecture description data structure
        """

        # ERROR: duplicate names in architecture
        if full_name in self.architecture_description:
            ERROR_CLEAN_EXIT(
                'flattened name: ', full_name,
                ' is already in architecture, check duplicated names')

        class_name = leaf_description['class']
        new_component_description = {'class': class_name}

        # apply the default values specified in the compound and primitive classes
        if not self.is_primitive_class(
                class_name
        ) and not class_name in self.compound_class_description:
            ERROR_CLEAN_EXIT('cannot find the class specified:', class_name)
        if not self.is_primitive_class(class_name):
            new_component_description['attributes'] = deepcopy(
                self.compound_class_description[class_name]['attributes'])
        else:
            new_component_description['attributes'] = deepcopy(
                self.primitive_class_description[class_name]['attributes'])

        if 'attributes' in leaf_description:
            new_component_description['attributes'].update(
                leaf_description['attributes'])

        # apply shared attributes projected from upper hierarchy
        # if redefined at the node, the attribute value will not be projected
        if shared_attr_dict is not None:
            for name, value in shared_attr_dict.items():
                if 'attributes' not in leaf_description or \
                    name not in leaf_description['attributes'] :
                    new_component_description['attributes'].update(
                        {name: value})

                # Info: projected shared attributes overridden
                if 'attributes'  in leaf_description and \
                    name in leaf_description['attributes'] :
                    INFO(
                        'Ignored shared attribute value projection: attribute ',
                        name, ' redefined at component ', full_name,
                        ' => ignore projected value from upper hierarchy')

        # check if there is any binding or arithmetic expressions in the attribute dictionary
        for attr_name, attr_val in new_component_description[
                'attributes'].items():
            if type(attr_val) is str:
                op_type, op1, op2 = parse_expression_for_arithmetic\
                                                (attr_val, new_component_description['attributes'])
                if not op_type is None:
                    result = process_arithmetic(op1, op2, op_type)
                    new_component_description['attributes'][attr_name] = result
                else:
                    if attr_val in new_component_description['attributes']:
                        new_component_description['attributes'][
                            attr_name] = new_component_description[
                                'attributes'][attr_val]

        # save to data structure
        self.architecture_description[full_name] = deepcopy(
            new_component_description)