#!/usr/bin/env python # Copyright 2022 the V8 project authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can # be found in the LICENSE file. """ This script combines the branch hints for profile-guided optimization produced by get_hints.py. The hints can simply be concatenated in priority order instead of using this script if the earliest seen hint is to be used. Usage: combine_hints.py combine-option N output_file hints_file_1 weight_1 hints_file_2 weight_2 ... where weights_n is the integer weight applied to the hints in hints_file_n and combine-option N is one of the below: diff N: Only use the hint when the weighted sum of the hints in one direction is equal to or greater than the weighted sum of hints in the opposite direction by at least N. agreed N: Only use the hint if every file containing this branch agrees and the weighted sum of these hints is at least N. Using diff num_input_files and using a weight of 1 for every hints_file will give the strict intersection of all files. """ import argparse import sys PARSER = argparse.ArgumentParser( description="A script that combines the hints produced by get_hints.py", epilog="Example:\n\tcombine_hints.py combine-option N output_file hints_file_1 2 hints_file_2 1 ...\"" ) PARSER.add_argument( 'combine_option', choices=['diff', 'agreed'], help="The combine option dictates how the hints will be combined, diff \ only uses the hint if the positive/negative hints outweigh the \ negative/positive hints by N, while agreed only uses the hint if \ the weighted sum of hints in one direction matches or exceeds N and \ no conflicting hints are found.") PARSER.add_argument( 'weight_threshold', type=int, help="The threshold value which the hint's weight must match or exceed \ to be used.") PARSER.add_argument( 'output_file', help="The file which the hints and builtin hashes are written to") PARSER.add_argument( 'hint_files_and_weights', nargs=argparse.REMAINDER, help="The hint files produced by get_hints.py along with their weights") ARGS = vars(PARSER.parse_args()) BRANCH_HINT_MARKER = "block_hint" BUILTIN_HASH_MARKER = "builtin_hash" must_agree = ARGS['combine_option'] == "agreed" weight_threshold = max(1, ARGS['weight_threshold']) hint_args = ARGS['hint_files_and_weights'] hint_files_and_weights = zip(hint_args[0::2], hint_args[1::2]) def add_branch_hints(hint_file, weight, branch_hints, builtin_hashes): try: with open(hint_file, "r") as f: for line in f.readlines(): fields = line.split(',') if fields[0] == BRANCH_HINT_MARKER: builtin_name = fields[1] true_block_id = int(fields[2]) false_block_id = int(fields[3]) key = (builtin_name, true_block_id, false_block_id) delta = weight if (int(fields[4]) > 0) else -weight if key not in branch_hints: if must_agree: # The boolean value records whether or not any conflicts have been # found for this branch. initial_hint = (False, 0) else: initial_hint = 0 branch_hints[key] = initial_hint if must_agree: (has_conflicts, count) = branch_hints[key] if not has_conflicts: if abs(delta) + abs(count) == abs(delta + count): branch_hints[key] = (False, count + delta) else: branch_hints[key] = (True, 0) else: branch_hints[key] += delta elif fields[0] == BUILTIN_HASH_MARKER: builtin_name = fields[1] builtin_hash = int(fields[2]) if builtin_name in builtin_hashes: if builtin_hashes[builtin_name] != builtin_hash: print("Builtin hashes {} and {} for {} do not match.".format( builtin_hashes[builtin_name], builtin_hash, builtin_name)) sys.exit(1) else: builtin_hashes[builtin_name] = builtin_hash except IOError as e: print("Cannot read from {}. {}.".format(hint_file, e.strerror)) sys.exit(1) def write_hints_to_output(output_file, branch_hints, builtin_hashes): try: with open(output_file, "w") as f: for key in branch_hints: if must_agree: (has_conflicts, count) = branch_hints[key] if has_conflicts: count = 0 else: count = branch_hints[key] if abs(count) >= abs(weight_threshold): hint = 1 if count > 0 else 0 f.write("{},{},{},{},{}\n".format(BRANCH_HINT_MARKER, key[0], key[1], key[2], hint)) for builtin_name in builtin_hashes: f.write("{},{},{}\n".format(BUILTIN_HASH_MARKER, builtin_name, builtin_hashes[builtin_name])) except IOError as e: print("Cannot write to {}. {}.".format(output_file, e.strerror)) sys.exit(1) branch_hints = {} builtin_hashes = {} for (hint_file, weight) in hint_files_and_weights: add_branch_hints(hint_file, int(weight), branch_hints, builtin_hashes) write_hints_to_output(ARGS['output_file'], branch_hints, builtin_hashes)