Xplace_for_ICCAD/src/run_placement_nesterov.py

308 lines
13 KiB
Python
Raw Normal View History

2022-10-20 22:46:17 +08:00
from utils import *
from src import *
from functools import partial
def run_placement_main_nesterov(args, logger):
data, rawdb, gpdb = load_dataset(args, logger)
device = torch.device(
"cuda:{}".format(args.gpu) if torch.cuda.is_available() else "cpu"
)
assert args.use_eplace_nesterov
logger.info("Use Nesterov optimizer!")
if args.scale_design:
logger.warning("Eplace's nesterov optimizer cannot support normalized die. Disable scale_design.")
args.scale_design = False
data = data.to(device)
data = data.preprocess()
logger.info(data)
logger.info(data.node_type_indices)
# args.num_bin_x = args.num_bin_y = 2 ** math.ceil(math.log2(max(data.die_info).item() // 25))
init_density_map = get_init_density_map(data, args, logger)
data.init_filler()
mov_lhs, mov_rhs = data.movable_index
mov_node_pos, mov_node_size, expand_ratio = data.get_mov_node_info()
mov_node_pos = mov_node_pos.requires_grad_(True)
node_pos_lb = mov_node_size / 2 + data.die_ll + 1e-4
node_pos_ub = data.die_ur - mov_node_size / 2 + data.die_ll - 1e-4
def trunc_node_pos_fn(x):
x.data.clamp_(min=node_pos_lb, max=node_pos_ub)
return x
conn_fix_node_pos = data.node_pos.new_empty(0, 2)
if data.fixed_connected_index[0] < data.fixed_connected_index[1]:
lhs, rhs = data.fixed_connected_index
conn_fix_node_pos = data.node_pos[lhs:rhs, ...]
conn_fix_node_pos = conn_fix_node_pos.detach()
def overflow_fn(mov_density_map):
overflow_sum = ((mov_density_map - args.target_density) * data.bin_area).clamp_(min=0.0).sum()
return overflow_sum / data.total_mov_area_without_filler
overflow_helper = (mov_lhs, mov_rhs, overflow_fn)
ps = ParamScheduler(data, args, logger)
density_map_layer = ElectronicDensityLayer(
unit_len=data.unit_len,
num_bin_x=data.num_bin_x,
num_bin_y=data.num_bin_y,
device=device,
overflow_helper=overflow_helper,
sorted_maps=data.sorted_maps,
expand_ratio=expand_ratio,
).to(device)
# fix_lhs, fix_rhs = data.fixed_index
# info = (0, 0, data.design_name + "_fix")
# fix_node_pos = data.node_pos[fix_lhs:fix_rhs, ...]
# fix_node_size = data.node_size[fix_lhs:fix_rhs, ...]
# draw_fig_with_cairo(
# None, None, fix_node_pos, fix_node_size, None, None, data, info, args
# )
obj_and_grad_fn = partial(
calc_obj_and_grad,
constraint_fn=trunc_node_pos_fn,
mov_node_size=mov_node_size,
init_density_map=init_density_map,
density_map_layer=density_map_layer,
conn_fix_node_pos=conn_fix_node_pos,
ps=ps,
data=data,
args=args,
)
evaluator_fn = partial(
fast_evaluator,
constraint_fn=trunc_node_pos_fn,
mov_node_size=mov_node_size,
init_density_map=init_density_map,
density_map_layer=density_map_layer,
conn_fix_node_pos=conn_fix_node_pos,
ps=ps,
data=data,
args=args,
)
optimizer = NesterovOptimizer(
[mov_node_pos],
lr=0,
)
# initialization
init_params(
mov_node_pos, trunc_node_pos_fn, mov_lhs, mov_rhs, conn_fix_node_pos,
density_map_layer, mov_node_size, init_density_map, optimizer, ps, data, args
)
# init learnig rate
init_lr = estimate_initial_learning_rate(obj_and_grad_fn, trunc_node_pos_fn, mov_node_pos, args.lr)
for param_group in optimizer.param_groups:
param_group["lr"] = init_lr.item()
torch.cuda.synchronize()
gp_start_time = time.time()
logger.info("start gp")
# def trace_handler(prof):
# print(prof.key_averages().table(
# sort_by="self_cuda_time_total", row_limit=-1))
# prof.export_chrome_trace("test_trace_" + str(prof.step_num) + ".json")
# with torch.profiler.profile(
# activities=[
# torch.profiler.ProfilerActivity.CPU,
# torch.profiler.ProfilerActivity.CUDA,
# ], schedule=torch.profiler.schedule(
# wait=2,
# warmup=2,
# active=2),
# on_trace_ready=trace_handler
# ) as p:
# for iter in range(6):
# # optimizer.zero_grad()
# obj = optimizer.step(obj_and_grad_fn)
# hpwl, overflow = evaluator_fn(mov_node_pos)
# # update parameters
# ps.step(hpwl, overflow, mov_node_pos, data)
# if ps.need_to_early_stop():
# break
# p.step()
# exit(0)
for iteration in range(args.inner_iter):
# optimizer.zero_grad() # zero grad inside obj_and_grad_fn
obj = optimizer.step(obj_and_grad_fn)
hpwl, overflow = evaluator_fn(mov_node_pos)
# update parameters
ps.step(hpwl, overflow, mov_node_pos, data)
if ps.need_to_early_stop():
break
if iteration % args.log_freq == 0 or iteration == args.inner_iter - 1:
log_str = (
"iter: %d | masked_hpwl: %.2E overflow: %.4f obj: %.4E "
"density_weight: %.4E wa_coeff: %.4E"
% (
iteration,
hpwl,
overflow,
obj,
ps.density_weight,
ps.wa_coeff,
)
)
logger.info(log_str)
if args.draw_placement:
info = (iteration, hpwl, data.design_name)
node_pos_to_draw = mov_node_pos[mov_lhs:mov_rhs, ...].clone()
node_size_to_draw = mov_node_size[mov_lhs:mov_rhs, ...].clone()
node_pos_to_draw = torch.cat(
[node_pos_to_draw, data.node_pos[mov_rhs:, ...].clone()], dim=0
)
node_size_to_draw = torch.cat(
[node_size_to_draw, data.node_size[mov_rhs:, ...].clone()], dim=0
)
if args.use_filler:
node_pos_to_draw = torch.cat(
[node_pos_to_draw, mov_node_pos[mov_rhs:, ...].clone()], dim=0
)
node_size_to_draw = torch.cat(
[node_size_to_draw, mov_node_size[mov_rhs:, ...].clone()], dim=0
)
draw_fig_with_cairo_cpp(
node_pos_to_draw, node_size_to_draw, data, info, args
)
# Save best solution
best_res = ps.get_best_solution()
if best_res[0] is not None:
best_sol, hpwl, overflow = best_res
mov_node_pos.data.copy_(best_sol)
node_pos = mov_node_pos[mov_lhs:mov_rhs]
node_pos = torch.cat([node_pos, data.node_pos[mov_rhs:]], dim=0)
torch.cuda.synchronize()
gp_end_time = time.time()
gp_time = gp_end_time - gp_start_time
gp_per_iter = gp_time / (iteration + 1)
logger.info("GP Stop! #Iters %d masked_hpwl: %.4E overflow: %.4f GP Time: %.4fs perIterTime: %.6fs" %
(iteration, hpwl, overflow, gp_time, gp_time / (iteration + 1))
)
# Eval
hpwl, overflow = evaluate_placement(
node_pos, density_map_layer, init_density_map, data, args
)
hpwl, overflow = hpwl.item(), overflow.item()
info = (iteration + 1, hpwl, data.design_name)
if args.draw_placement:
draw_fig_with_cairo_cpp(node_pos, data.node_size, data, info, args)
logger.info("After GP, best solution eval, exact HPWL: %.4E exact Overflow: %.4f" % (hpwl, overflow))
ps.visualize(args, logger)
gp_hpwl = hpwl
iteration += 1 # increase 1 For DP drawing
# Write placement
if args.write_placement and args.load_from_raw:
res_root = os.path.join(args.result_dir, args.exp_id)
gp_prefix = os.path.join(res_root, args.output_dir, "%s_%s_gp" %(args.output_prefix, args.design_name))
if not os.path.exists(os.path.dirname(gp_prefix)):
os.makedirs(os.path.dirname(gp_prefix))
start_write_time = time.time()
if data.dataset_format == "lefdef":
exact_node_pos = torch.round(node_pos * data.die_scale + data.die_shift).cpu()
gpdb.apply_node_pos(exact_node_pos)
gpdb.write_placement(gp_prefix)
elif data.dataset_format == "bookshelf":
logger.info("Use python to generate .pl file")
data.write_pl(node_pos, gp_prefix)
else:
raise NotImplementedError("Dataset format %s unsupported" % data.dataset_format)
logger.info("Write global placement in %s. Time: %.4f" % (gp_prefix, time.time() - start_write_time))
dp_start_time = None
dp_end_time = None
dp_hpwl = -1
top5overflow = -1
if args.detail_placement and args.load_from_raw:
# TODO: too ugly...
post_fix = None
if data.dataset_format == "lefdef":
post_fix = ".def"
elif data.dataset_format == "bookshelf":
post_fix = ".pl"
gp_out_file = gp_prefix + post_fix
if args.dp_engine == "ntuplace3":
dp_out_file = gp_out_file.replace("_gp%s" % post_fix, "")
dp_engine = "./thirdparty/placers/ntuplace3/ntuplace3"
aux_input = data.dataset_path["aux"]
target_density_cmd = ""
if args.target_density < 1.0:
target_density_cmd = " -util %f" % (args.target_density)
cmd = "%s -aux %s -loadpl %s %s -out %s -noglobal" % (
dp_engine, aux_input, gp_out_file, target_density_cmd, dp_out_file)
logger.info(cmd)
# os.system(cmd)
dp_start_time = time.time()
output = os.popen(cmd).read()
dp_end_time = time.time()
dp_hpwl = float(output.split("========\n HPWL=")[1].split("Time")[0].strip())
dp_out_file = dp_out_file + ".ntup%s" % post_fix
elif args.dp_engine == "rippledp":
dp_out_file = gp_out_file.replace("_gp", "")
dp_engine = "./thirdparty/placers/ripple/bin/placer"
aux_input = data.dataset_path["aux"]
MLLMaxDensity = int(round(args.target_density * 1000.0))
cmd = "%s -flow dac2016 -bookshelf ispd2005 -aux %s -pl %s -MLLMaxDensity %s -cpu %s -output %s" % (
dp_engine, aux_input, gp_out_file, MLLMaxDensity, args.num_threads, dp_out_file)
dp_start_time = time.time()
os.system(cmd)
dp_end_time = time.time()
elif args.dp_engine == 'ntuplace_4dr':
dp_out_file = gp_out_file.replace(".gp.def", "")
dp_engine = "./thirdparty/placers/ntuplace4dr/ntuplace4dr_binary/placer"
cmd = dp_engine
tech_lef = data.dataset_path["tech_lef"]
cell_lef = data.dataset_path["cell_lef"]
cmd += " -tech_lef %s" % tech_lef
cmd += " -cell_lef %s" % cell_lef
benchmark_dir = os.path.dirname(tech_lef)
cmd += " -floorplan_def %s" % (gp_out_file)
cmd += " -out ntuplace_4dr_out"
cmd += " -placement_constraints %s/placement.constraints" % (benchmark_dir)
cmd += " -noglobal; "
cmd += "mv ntuplace_4dr_out.fence.plt %s.fence.plt ; " % (dp_out_file)
cmd += "mv ntuplace_4dr_out.init.plt %s.init.plt ; " % (dp_out_file)
cmd += "mv ntuplace_4dr_out %s.ntup.def ; " % (dp_out_file)
cmd += "mv ntuplace_4dr_out.ntup.overflow.plt %s.ntup.overflow.plt ; " % (dp_out_file)
cmd += "mv ntuplace_4dr_out.ntup.plt %s.ntup.plt ; " % (dp_out_file)
if os.path.exists("%s/dat" % (os.path.dirname(dp_out_file))):
cmd += "rm -r %s/dat ; " % (os.path.dirname(dp_out_file))
cmd += "mv dat %s/ ; " % (os.path.dirname(dp_out_file))
logger.info("%s" % (cmd))
dp_start_time = time.time()
output = os.popen(cmd).read()
dp_end_time = time.time()
# consider site_width
dp_hpwl = float(output.split("=======\n HPWL=")[1].split("Time")[0].strip())
top5overflow = float(output.split("[CONG] Top 5 Overflow")[1].split("\n")[0].strip())
else:
raise NotImplementedError("DP Engine %s unsupported" % args.dp_engine)
logger.info("External detailed placement takes %.2f seconds" %
(dp_end_time - dp_start_time))
logger.info("After DP, HPWL: %.4E" % dp_hpwl)
logger.info("Write detail placement in %s" % dp_out_file)
del gpdb, rawdb
# logger.info("Evaluating detail placement result...")
# data, rawdb, gpdb = load_dataset(args, logger, dp_out_file)
# data = data.to(device).preprocess()
# hpwl, overflow = evaluate_placement(
# data.node_pos, density_map_layer, init_density_map, data, args
# )
# hpwl, overflow = hpwl.item(), overflow.item()
# info = (iteration + 1, hpwl, data.design_name)
# draw_fig_with_cairo_cpp(data.node_pos, data.node_size, data, info, args)
# logger.info("After DP, HPWL: %.4E Overflow: %.4f" % (hpwl, overflow))
gp_time = gp_end_time - gp_start_time
dp_time = dp_end_time - dp_start_time if dp_end_time is not None else 0.0
logger.info("GP Time: %.4f DP Time: %.4f" % (gp_time, dp_time))
return dp_hpwl, gp_hpwl, top5overflow, overflow, gp_time, dp_time, gp_per_iter