1ea280170Shappy-lximport os 2ea280170Shappy-lximport random 3ea280170Shappy-lxfrom subprocess import Popen, PIPE 4ea280170Shappy-lximport psutil 5ea280170Shappy-lximport json 6ea280170Shappy-lximport sys 7ea280170Shappy-lximport math 8*f2421014SYanqin Liimport time 9*f2421014SYanqin Lifrom datetime import datetime 10ea280170Shappy-lx 11*f2421014SYanqin Li# usage: python3 constantHelper.py JSON_FILE_PATH [BUILD_PATH] 12ea280170Shappy-lx# 13ea280170Shappy-lx# an example json config file is as follow: 14ea280170Shappy-lx# visit https://bosc.yuque.com/yny0gi/gr7hyo/oy3dagqi9v97p696 for detail 15ea280170Shappy-lx# { 16ea280170Shappy-lx# "constants": [ 17ea280170Shappy-lx# { 18ea280170Shappy-lx# "name": "block_cycles_cache_0", 19ea280170Shappy-lx# "width": 7, 20ea280170Shappy-lx# "guide": 20, 21ea280170Shappy-lx# "init": 11 22ea280170Shappy-lx# }, 23ea280170Shappy-lx# { 24ea280170Shappy-lx# "name": "block_cycles_cache_1", 25ea280170Shappy-lx# "width": 7, 26ea280170Shappy-lx# "init": 18 27ea280170Shappy-lx# }, 28ea280170Shappy-lx# { 29ea280170Shappy-lx# "name": "block_cycles_cache_2", 30ea280170Shappy-lx# "width": 7, 31ea280170Shappy-lx# "init": 127 32ea280170Shappy-lx# }, 33ea280170Shappy-lx# { 34ea280170Shappy-lx# "name": "block_cycles_cache_3", 35ea280170Shappy-lx# "width": 7, 36ea280170Shappy-lx# "init": 17 37ea280170Shappy-lx# } 38ea280170Shappy-lx# ], 39ea280170Shappy-lx# "opt_target": [ 40ea280170Shappy-lx# {"successfully_forward_channel_D": {"policy" :"max", "baseline" :0} }, 41ea280170Shappy-lx# {"successfully_forward_mshr": {"policy" :"max", "baseline" :0} }, 42ea280170Shappy-lx# {"dcache.missQueue.entries_0: load_miss_penalty_to_use,": {"policy" :"min", "baseline" :250396} }, 43ea280170Shappy-lx# {"dcache.missQueue.entries_1: load_miss_penalty_to_use,": {"policy" :"min", "baseline" :5634} }, 44ea280170Shappy-lx# {"dcache.missQueue.entries_2: load_miss_penalty_to_use,": {"policy" :"min", "baseline" :4599} }, 45ea280170Shappy-lx# {"dcache.missQueue.entries_3: load_miss_penalty_to_use,": {"policy" :"min", "baseline" :4146} } 46ea280170Shappy-lx# ], 47ea280170Shappy-lx 48ea280170Shappy-lx# "population_num": 50, 49ea280170Shappy-lx# "iteration_num": 50, 50ea280170Shappy-lx# "crossover_rate": 50, 51ea280170Shappy-lx# "mutation_rate": 50, 52ea280170Shappy-lx 53ea280170Shappy-lx# "emu_threads": 16, 54ea280170Shappy-lx# "concurrent_emu": 4, 55ea280170Shappy-lx# "max_instr": 1000000, 56ea280170Shappy-lx# "seed": 3888, 57ea280170Shappy-lx# "work_load": "~/nexus-am/apps/maprobe/build/maprobe-riscv64-xs.bin" 58ea280170Shappy-lx# } 59ea280170Shappy-lx 60ea280170Shappy-lx 61ea280170Shappy-lx# parameters according to noop 62ea280170Shappy-lxNOOP_HOME = os.getenv("NOOP_HOME") 63*f2421014SYanqin LiXS_PROJECT_ROOT = os.getenv("XS_PROJECT_ROOT") 64*f2421014SYanqin Liif NOOP_HOME is None: 65*f2421014SYanqin Li print("Please set NOOP_HOME first.") 66*f2421014SYanqin Li exit(1) 67*f2421014SYanqin Liif XS_PROJECT_ROOT is None: 68*f2421014SYanqin Li print("Please set XS_PROJECT_ROOT first.") 69*f2421014SYanqin Li exit(1) 70ea280170Shappy-lxDIFF_PATH = os.path.join(NOOP_HOME, "ready-to-run", "riscv64-nemu-interpreter-so") 71ea280170Shappy-lx 72*f2421014SYanqin Li# get arguments 73*f2421014SYanqin Liif len(sys.argv) > 1: 74*f2421014SYanqin Li JSON_FILE_PATH=sys.argv[1] 75*f2421014SYanqin Lielse: 76*f2421014SYanqin Li print("Please specify the json file path") 77*f2421014SYanqin Li exit(1) 78*f2421014SYanqin Liif len(sys.argv) > 2: 79*f2421014SYanqin Li BUILD_PATH = sys.argv[2] 80*f2421014SYanqin Lielse: 81*f2421014SYanqin Li BUILD_PATH = os.path.join(NOOP_HOME, "build") 82*f2421014SYanqin Li 83*f2421014SYanqin LiEMU_PATH = os.path.join(BUILD_PATH, "emu") 84ea280170Shappy-lxCONFIG_FILE_PREFIX = ".constant_result_" 85ea280170Shappy-lxPERF_FILE_POSTFIX = "tmp" 86ea280170Shappy-lxMAXVAL = (1 << 63) - 1 87ea280170Shappy-lx 88ea280170Shappy-lxclass Constant: 89ea280170Shappy-lx def __init__(self, obj: dict) -> None: 90ea280170Shappy-lx self.name = obj['name'] 91ea280170Shappy-lx self.width = obj['width'] 92ea280170Shappy-lx self.guide = (1 << self.width - 1) - 1 if 'guide' not in obj.keys() else obj['guide'] 93ea280170Shappy-lx self.init = random.randint(0, self.guide) if 'init' not in obj.keys() else obj['init'] 94ea280170Shappy-lx def maxrange(self) -> int: 95ea280170Shappy-lx return (1 << self.width) - 1 96ea280170Shappy-lx 97ea280170Shappy-lx 98ea280170Shappy-lxclass Config: 99*f2421014SYanqin Li def __init__(self, constants, opt_target, population_num, iteration_num, crossover_rate, mutation_rate, emu_threads, concurrent_emu, max_instr, seed, work_load, tag) -> None: 100ea280170Shappy-lx self.constants = constants 101ea280170Shappy-lx self.opt_target = opt_target 102ea280170Shappy-lx self.population_num = int(population_num) 103ea280170Shappy-lx self.iteration_num = int(iteration_num) 104ea280170Shappy-lx self.crossover_rate = int(crossover_rate) 105ea280170Shappy-lx self.mutation_rate = int(mutation_rate) 106ea280170Shappy-lx self.emu_threads = int(emu_threads) 107ea280170Shappy-lx self.concurrent_emu = int(concurrent_emu) 108ea280170Shappy-lx self.max_instr = int(max_instr) 109ea280170Shappy-lx self.seed = int(seed) 110ea280170Shappy-lx self.work_load = work_load 111*f2421014SYanqin Li self.tag = tag 112ea280170Shappy-lx def get_ith_constant(self, i) -> Constant: 113ea280170Shappy-lx return self.constants[i] 114ea280170Shappy-lx def get_constain_num(self) -> int: 115ea280170Shappy-lx return len(self.constants) 116ea280170Shappy-lx 117ea280170Shappy-lx 118*f2421014SYanqin Lidef loadConfig(json_path, tag) -> Config: 119ea280170Shappy-lx obj = json.load(open(json_path, "r")) 120ea280170Shappy-lx constants = [Constant(obj['constants'][i]) for i in range(len(obj['constants']))] 121*f2421014SYanqin Li config = Config(constants, obj['opt_target'], obj['population_num'], obj['iteration_num'], obj['crossover_rate'], obj['mutation_rate'], obj['emu_threads'], obj['concurrent_emu'], obj['max_instr'], obj['seed'], obj['work_load'], tag) 122ea280170Shappy-lx return config 123ea280170Shappy-lx 124ea280170Shappy-lxclass RunContext: 125ea280170Shappy-lx def __init__(self, config: Config) -> None: 126ea280170Shappy-lx self.config = config 127*f2421014SYanqin Li def checkCoreFree(self) -> bool: 128ea280170Shappy-lx percent_per_core = psutil.cpu_percent(interval=1 ,percpu=True) 129ea280170Shappy-lx acc = 0 130ea280170Shappy-lx for i in range(self.config.concurrent_emu * self.config.emu_threads): 131ea280170Shappy-lx acc += percent_per_core[i] 132ea280170Shappy-lx if acc < (0.1 * (100 * self.config.concurrent_emu * self.config.emu_threads)): 133ea280170Shappy-lx return True 134ea280170Shappy-lx else: 135ea280170Shappy-lx print("no free {} core, core usage:".format(self.config.concurrent_emu * self.config.emu_threads)) 136ea280170Shappy-lx print(percent_per_core) 137ea280170Shappy-lx return False 138*f2421014SYanqin Li def get_free_cores(self) -> tuple[bool, int, int, int]: 139*f2421014SYanqin Li thread = self.config.emu_threads 140*f2421014SYanqin Li # return (Success?, numa node, start_core, end_core) 141*f2421014SYanqin Li num_core = psutil.cpu_count(logical=False) # SMT is not allowed 142*f2421014SYanqin Li core_usage = psutil.cpu_percent(interval=2, percpu=True) 143*f2421014SYanqin Li num_window = num_core // thread 144*f2421014SYanqin Li for i in range(num_window): 145*f2421014SYanqin Li start = i * thread 146*f2421014SYanqin Li end = (i + 1) * thread 147*f2421014SYanqin Li window_usage = core_usage[start:end] 148*f2421014SYanqin Li free = sum(window_usage) < 30 * thread and True not in map(lambda x: x > 80, window_usage) 149*f2421014SYanqin Li if free: 150*f2421014SYanqin Li return (True, int(start >= (num_core // 2)), start, end - 1) 151*f2421014SYanqin Li return (False, 0, 0, 0) 152ea280170Shappy-lx def getStdIn(self, population: list, id: int) -> str: 153ea280170Shappy-lx res = 'echo \"' 154ea280170Shappy-lx res += str(len(population[id])) 155ea280170Shappy-lx res += '\\n' 156ea280170Shappy-lx for item in population[id]: 157ea280170Shappy-lx res += item[0] + ' ' + str(item[1]) + '\\n' 158ea280170Shappy-lx res += '\"' 159ea280170Shappy-lx return res 160ea280170Shappy-lx 161*f2421014SYanqin Li def genRunCMD(self, population, id, numa = None, coreStart = None, coreEnd = None) -> str: 162ea280170Shappy-lx stdinStr = self.getStdIn(population, id) 163*f2421014SYanqin Li if None in [numa, coreStart, coreEnd]: 164*f2421014SYanqin Li return "{} | {} -i {} --diff {} -I {} -s {}".format(stdinStr, EMU_PATH, self.config.work_load, DIFF_PATH, self.config.max_instr, self.config.seed) 165*f2421014SYanqin Li return "{} | numactl -m {} -C {}-{} {} -i {} --diff {} -I {} -s {}".format(stdinStr, numa, coreStart, coreEnd, EMU_PATH, self.config.work_load, DIFF_PATH, self.config.max_instr, self.config.seed) 166*f2421014SYanqin Li 167*f2421014SYanqin Li def getOutPath(self, iterid, i): 168*f2421014SYanqin Li dirPath = os.path.join(BUILD_PATH, self.config.tag) 169*f2421014SYanqin Li if not os.path.exists(dirPath): 170*f2421014SYanqin Li os.mkdir(dirPath) 171*f2421014SYanqin Li return os.path.join(dirPath, f"{iterid}-{i}-out.txt") 172*f2421014SYanqin Li 173*f2421014SYanqin Li def getPerfPath(self, iterid, i): 174*f2421014SYanqin Li # return os.path.join(BUILD_PATH, CONFIG_FILE_PREFIX + str(i) + '.' + PERF_FILE_POSTFIX) 175*f2421014SYanqin Li dirPath = os.path.join(BUILD_PATH, self.config.tag) 176*f2421014SYanqin Li if not os.path.exists(dirPath): 177*f2421014SYanqin Li os.mkdir(dirPath) 178*f2421014SYanqin Li return os.path.join(dirPath, f"{iterid}-{i}-err.txt") 179ea280170Shappy-lx 180ea280170Shappy-lxclass Solution: 181ea280170Shappy-lx def __init__(self, config: Config) -> None: 182ea280170Shappy-lx self.config = config 183ea280170Shappy-lx self.context = RunContext(config) 184ea280170Shappy-lx def genFirstPopulation(self) -> list: 185ea280170Shappy-lx res = [] 186ea280170Shappy-lx used = [] 187ea280170Shappy-lx config = self.config 188ea280170Shappy-lx for i in range(config.population_num): 189ea280170Shappy-lx candidate = [[config.get_ith_constant(i).name, random.randint(0, config.get_ith_constant(i).maxrange()) % config.get_ith_constant(i).guide] for i in range(config.get_constain_num())] 190ea280170Shappy-lx while(candidate in used): 191ea280170Shappy-lx candidate = [[config.get_ith_constant(i).name, random.randint(0, config.get_ith_constant(i).maxrange()) % config.get_ith_constant(i).guide] for i in range(config.get_constain_num())] 192ea280170Shappy-lx used.append(candidate) 193ea280170Shappy-lx res.append(candidate) 194ea280170Shappy-lx assert(len(res) == config.population_num) 195ea280170Shappy-lx return res 196*f2421014SYanqin Li def profilling_fitness(self, iterid: int) -> list: 197ea280170Shappy-lx fitness = [] 198ea280170Shappy-lx lines = [] 199ea280170Shappy-lx for idx in range(self.config.population_num): 200*f2421014SYanqin Li with open(self.context.getPerfPath(iterid, idx), "r") as fp: 201ea280170Shappy-lx lines = fp.readlines() 202ea280170Shappy-lx res = 0 203ea280170Shappy-lx for line in lines: 204ea280170Shappy-lx for opt in config.opt_target: 205ea280170Shappy-lx if list(opt.keys())[0] in line: 206ea280170Shappy-lx # max and min policy 207ea280170Shappy-lx if list(opt.values())[0]['policy'] == 'max': 208ea280170Shappy-lx res += int(list(filter(lambda x: x != '', line.split(' ')))[-1]) - int(list(opt.values())[0]['baseline']) 209ea280170Shappy-lx elif list(opt.values())[0]['policy'] == 'min': 210ea280170Shappy-lx res += int(list(opt.values())[0]['baseline']) - int(list(filter(lambda x: x != '', line.split(' ')))[-1]) 211ea280170Shappy-lx fitness.append(res) 212ea280170Shappy-lx assert(len(fitness) == self.config.population_num) 213ea280170Shappy-lx return fitness 214*f2421014SYanqin Li def run_one_round(self, iterid: int, population: list) -> None: 215ea280170Shappy-lx procs = [] 216ea280170Shappy-lx i = 0 217ea280170Shappy-lx while i < len(population): 218ea280170Shappy-lx if i % self.config.concurrent_emu == 0: 219ea280170Shappy-lx for proc in procs: 220ea280170Shappy-lx proc.wait() 221ea280170Shappy-lx procs.clear() 222ea280170Shappy-lx print(population[i]) 223*f2421014SYanqin Li # while True: 224*f2421014SYanqin Li # (succ, numa, coreStart, coreEnd) = self.context.get_free_cores() 225*f2421014SYanqin Li # if succ: 226*f2421014SYanqin Li # with open(self.context.getOutPath(iterid, i), "w") as stdout, open(self.context.getPerfPath(iterid, i), "w") as stderr: 227*f2421014SYanqin Li # # print(self.context.genRunCMD(population, i, numa, coreStart, coreEnd), flush=True) 228*f2421014SYanqin Li # procs.append(Popen(args=self.context.genRunCMD(population, i, numa, coreStart, coreEnd), shell=True, encoding='utf-8', stdin=PIPE, stdout=stdout, stderr=stderr)) 229*f2421014SYanqin Li # break 230*f2421014SYanqin Li # print("no free {} core".format(self.config.concurrent_emu * self.config.emu_threads)) 231*f2421014SYanqin Li # time.sleep(5) 232*f2421014SYanqin Li ## only for tutorial 233*f2421014SYanqin Li with open(self.context.getOutPath(iterid, i), "w") as stdout, open(self.context.getPerfPath(iterid, i), "w") as stderr: 234*f2421014SYanqin Li procs.append(Popen(args=self.context.genRunCMD(population, i), shell=True, encoding='utf-8', stdin=PIPE, stdout=stdout, stderr=stderr)) 235ea280170Shappy-lx i += 1 236ea280170Shappy-lx for proc in procs: 237ea280170Shappy-lx proc.wait() 238ea280170Shappy-lx def mutation(self, item: list) -> list: 239ea280170Shappy-lx res = [] 240ea280170Shappy-lx for val in item: 241ea280170Shappy-lx width = 0 242ea280170Shappy-lx guide = 0 243ea280170Shappy-lx for constant in self.config.constants: 244ea280170Shappy-lx if(constant.name == val[0]): 245ea280170Shappy-lx width = constant.width 246ea280170Shappy-lx guide = constant.guide 247ea280170Shappy-lx mask = 1 << random.randint(0, width - 1) 248ea280170Shappy-lx if random.randint(0, 100) > self.config.mutation_rate: 249ea280170Shappy-lx res.append(val) 250ea280170Shappy-lx else: 251ea280170Shappy-lx val[1] = (((val[1] & mask) ^ mask) | val[1]) % guide 252ea280170Shappy-lx res.append(val) 253ea280170Shappy-lx assert(len(item) == len(res)) 254ea280170Shappy-lx return res 255ea280170Shappy-lx def crossover(self, poplulation: list) -> list: 256ea280170Shappy-lx res = [] 257ea280170Shappy-lx if len(poplulation) < 2: 258ea280170Shappy-lx return poplulation 259ea280170Shappy-lx for individual in poplulation: 260ea280170Shappy-lx indivi = [] 261ea280170Shappy-lx for (index, constant) in enumerate(individual): 262ea280170Shappy-lx const = constant 263ea280170Shappy-lx if random.randint(0, 100) < self.config.crossover_rate: 264ea280170Shappy-lx crossover_target_id = 0 265ea280170Shappy-lx while crossover_target_id == index: 266ea280170Shappy-lx crossover_target_id = random.randint(0, len(poplulation) - 1) 267ea280170Shappy-lx maskMax = 0 268ea280170Shappy-lx guide = 0 269ea280170Shappy-lx for config_const in self.config.constants: 270ea280170Shappy-lx if config_const.name == constant[0]: 271ea280170Shappy-lx maskMax = config_const.width 272ea280170Shappy-lx guide = config_const.guide 273ea280170Shappy-lx maskMax = int(math.log2(guide)) + 1 if (int(math.log2(guide)) + 1 < maskMax) else maskMax 274ea280170Shappy-lx maskLen = random.randint(1, maskMax) 275ea280170Shappy-lx mask = (1 << maskLen) - 1 276ea280170Shappy-lx shiftLen = random.randint(0, maskMax - maskLen) 277ea280170Shappy-lx mask = mask << shiftLen 278ea280170Shappy-lx const_now = const[1] 279ea280170Shappy-lx target_now = poplulation[crossover_target_id][index][1] 280ea280170Shappy-lx const_now = ((const_now & ~(mask)) | (target_now & mask)) % guide 281ea280170Shappy-lx const = [constant[0], const_now] 282ea280170Shappy-lx indivi.append(const) 283ea280170Shappy-lx res.append(indivi) 284ea280170Shappy-lx assert(len(poplulation) == len(res)) 285ea280170Shappy-lx return res 286ea280170Shappy-lx def genNextPop(self, curPop, fitness) -> list: 287ea280170Shappy-lx nextgen = [] 288ea280170Shappy-lx tmp = sorted(zip(curPop, fitness), key=lambda x : x[1], reverse=True) 289ea280170Shappy-lx print() 290ea280170Shappy-lx print("opt constant in this round is ", list(tmp)[0][0], " fitness is ", int(list(tmp)[0][1])) 291ea280170Shappy-lx cross = [] 292ea280170Shappy-lx for i in range(len(tmp)): 293ea280170Shappy-lx if i < (len(tmp) // 2): 294ea280170Shappy-lx # select 295ea280170Shappy-lx nextgen.append(tmp[i][0]) 296ea280170Shappy-lx else: 297ea280170Shappy-lx cross.append(tmp[i][0]) 298ea280170Shappy-lx # crossover 299ea280170Shappy-lx cross = self.crossover(cross) 300ea280170Shappy-lx nextgen = nextgen + cross 301ea280170Shappy-lx # mutation 302ea280170Shappy-lx for i in range(len(tmp)): 303ea280170Shappy-lx nextgen[i] = self.mutation(nextgen[i]) 304ea280170Shappy-lx assert(len(curPop) == len(nextgen)) 305ea280170Shappy-lx return nextgen 306ea280170Shappy-lx 307ea280170Shappy-lx class HashList: 308ea280170Shappy-lx def __init__(self, obj: list) -> None: 309ea280170Shappy-lx # obj: [['test1', 38], ['test2', 15]] 310ea280170Shappy-lx self.obj = obj 311ea280170Shappy-lx def __hash__(self) -> str: 312ea280170Shappy-lx res = '' 313ea280170Shappy-lx for const in self.obj: 314ea280170Shappy-lx res += ' '.join(map(lambda x : str(x), const)) 315ea280170Shappy-lx return hash(res) 316ea280170Shappy-lx def __eq__(self, __o: object) -> bool: 317ea280170Shappy-lx for (idx, const) in enumerate(self.obj): 318ea280170Shappy-lx if const != __o.obj[idx]: 319ea280170Shappy-lx return False 320ea280170Shappy-lx return True 321ea280170Shappy-lx 322ea280170Shappy-lx def gene_cal(self) -> None: 323ea280170Shappy-lx globalMap = dict() 324ea280170Shappy-lx if(self.config.population_num % 2 != 0): 325ea280170Shappy-lx print("gene algrithom must ensure that population_num is an even value") 326ea280170Shappy-lx return 327ea280170Shappy-lx parentPoplation = self.genFirstPopulation() 328ea280170Shappy-lx init_indiv = [] 329ea280170Shappy-lx for constant in self.config.constants: 330ea280170Shappy-lx const = [] 331ea280170Shappy-lx const.append(constant.name) 332ea280170Shappy-lx const.append(constant.init) 333ea280170Shappy-lx init_indiv.append(const) 334ea280170Shappy-lx parentPoplation.pop() 335ea280170Shappy-lx parentPoplation.append(init_indiv) 336ea280170Shappy-lx for i in range(self.config.iteration_num): 337ea280170Shappy-lx if i != 0: 338ea280170Shappy-lx print() 339ea280170Shappy-lx print("iteration ", i, " begins") 340ea280170Shappy-lx print() 341*f2421014SYanqin Li self.run_one_round(i, parentPoplation) 342*f2421014SYanqin Li fitness = self.profilling_fitness(i) 343ea280170Shappy-lx for (pop, fit) in zip(parentPoplation, fitness): 344ea280170Shappy-lx globalMap[self.HashList(pop)] = fit 345ea280170Shappy-lx parentPoplation = self.genNextPop(parentPoplation, fitness) 346*f2421014SYanqin Li 347ea280170Shappy-lx globalMap = zip(globalMap.keys(), globalMap.values()) 348ea280170Shappy-lx globalMap = sorted(globalMap, key=lambda x : x[1], reverse=True) 349ea280170Shappy-lx print("opt constant for gene algrithom is ", list(globalMap)[0][0].obj, " fitness", int(list(globalMap)[0][1])) 350ea280170Shappy-lx 351*f2421014SYanqin Litid = datetime.now().strftime("%m%d%H%M") 352*f2421014SYanqin Liconfig = loadConfig(JSON_FILE_PATH, f"constantin_{tid}") 353ea280170Shappy-lxSolution(config).gene_cal() 354