xref: /XiangShan/scripts/top-down/top_down.py (revision 9473e04d5cab97eaf63add958b2392eec3d876a2)
1import csv
2import sys
3from pyecharts.charts import Page, Sunburst
4from pyecharts import options as opts
5
6
7class TopDown:
8    """TopDown node"""
9    def __init__(self, name, percentage):
10        self.name = name
11        if isinstance(percentage, TopDown):
12            self.percentage = percentage.percentage
13        else:
14            self.percentage = percentage
15        self.down = {}
16        self.top = None
17        self.level = 0
18
19    def __add__(self, rhs):
20        if isinstance(rhs, TopDown):
21            return self.percentage + rhs.percentage
22        return self.percentage + rhs
23
24    def __radd__(self, lhs):
25        if isinstance(lhs, TopDown):
26            return lhs.percentage + self.percentage
27        return lhs + self.percentage
28
29    def __sub__(self, rhs):
30        if isinstance(rhs, TopDown):
31            return self.percentage - rhs.percentage
32        return self.percentage - rhs
33
34    def __rsub__(self, lhs):
35        if isinstance(lhs, TopDown):
36            return lhs.percentage - self.percentage
37        return lhs - self.percentage
38
39    def __mul__(self, rhs):
40        if isinstance(rhs, TopDown):
41            return self.percentage * rhs.percentage
42        return self.percentage * rhs
43
44    def __rmul__(self, lhs):
45        if isinstance(lhs, TopDown):
46            return lhs.percentage * self.percentage
47        return lhs * self.percentage
48
49    def __truediv__(self, rhs):
50        if isinstance(rhs, TopDown):
51            return self.percentage / rhs.percentage
52        return self.percentage / rhs
53
54    def __rtruediv__(self, lhs):
55        if isinstance(lhs, TopDown):
56            return lhs.percentage / self.percentage
57        return lhs / self.percentage
58
59    def add_down(self, name, percentage):
60        """Add a leaf node
61
62        Args:
63            name (str): Name of leaf node
64            percentage (float): Percentage of leaf node
65
66        Returns:
67            TopDown: leaf
68        """
69        self.down[name] = TopDown(name, percentage)
70        self.down[name].top = self
71        self.down[name].level = self.level + 1
72        return self.down[name]
73
74    def draw(self):
75        """Draw the TopDown sunburst chart
76
77        Returns:
78            _type_: _description_
79        """
80        if not self.down:
81            return [opts.SunburstItem(name=self.name, value=self.percentage)]
82        items = []
83        for value in self.down.values():
84            items.append(value.draw()[0])
85        if self.top:
86            return [opts.SunburstItem(name=self.name, value=self.percentage, children=items)]
87        return items
88
89
90def process_one(path, head):
91    """Process one chart
92
93    Args:
94        path (String): csv path
95        head (String): chart head
96
97    Returns:
98        Sunburst chart
99    """
100    with open(path, encoding='UTF-8') as file:
101        csv_file = dict(csv.reader(file))
102
103    def use(name):
104        return float(csv_file[name])
105
106    csv_file['total_slots'] = use('total_cycles') * 6
107    csv_file['ifu2id_allNO_slots'] = use('ifu2id_allNO_cycle') * 6
108    csv_file['ifu2id_hvButNotFull_slots'] = use('fetch_bubbles') - use('ifu2id_allNO_slots')
109
110    stall_cycles_core = use('stall_cycle_fp') + use('stall_cycle_int') + use('stall_cycle_rob') + use('stall_cycle_int_dq') + use('stall_cycle_fp_dq') + use('ls_dq_bound_cycles')
111
112    top = TopDown("Top", 1.0)
113
114# top
115    frontend_bound = top.add_down("Frontend Bound", use('decode_bubbles') / use('total_slots'))
116    bad_speculation = top.add_down("Bad Speculation", (use('slots_issued') - use('slots_retired') + use('recovery_bubbles')) / use('total_slots'))
117    retiring = top.add_down("Retiring", use('slots_retired') / use('total_slots'))
118    backend_bound = top.add_down("Backend Bound", top - frontend_bound - bad_speculation - retiring)
119
120#top->frontend_bound
121    fetch_latency = frontend_bound.add_down("Fetch Latency", use('fetch_bubbles') / use('total_slots'))
122    fetch_bandwidth = frontend_bound.add_down("Fetch Bandwidth", frontend_bound - fetch_latency)
123
124# top->frontend_bound->fetch_latency
125    itlb_miss = fetch_latency.add_down("iTLB Miss", use('itlb_miss_cycles') / use('total_cycles'))
126    icache_miss = fetch_latency.add_down("iCache Miss", use('icache_miss_cycles') / use('total_cycles'))
127    stage2_redirect_cycles = fetch_latency.add_down("Stage2 Redirect", use('stage2_redirect_cycles') / use('total_cycles'))
128    if2id_bandwidth = fetch_latency.add_down("IF2ID Bandwidth", use('ifu2id_hvButNotFull_slots') / use('total_slots'))
129    fetch_latency_others = fetch_latency.add_down("Fetch Latency Others", fetch_latency - itlb_miss - icache_miss - stage2_redirect_cycles - if2id_bandwidth)
130
131# top->frontend_bound->fetch_latency->stage2_redirect_cycles
132    branch_resteers = stage2_redirect_cycles.add_down("Branch Resteers", use('branch_resteers_cycles') / use('total_cycles'))
133    robFlush_bubble = stage2_redirect_cycles.add_down("RobFlush Bubble", use('robFlush_bubble_cycles') / use('total_cycles'))
134    ldReplay_bubble = stage2_redirect_cycles.add_down("LdReplay Bubble", use('ldReplay_bubble_cycles') / use('total_cycles'))
135
136# top->bad_speculation
137    branch_mispredicts = bad_speculation.add_down("Branch Mispredicts", bad_speculation)
138
139# top->backend_bound
140    memory_bound = backend_bound.add_down("Memory Bound", backend_bound * (use('store_bound_cycles') + use('load_bound_cycles')) / (
141        stall_cycles_core + use('store_bound_cycles') + use('load_bound_cycles')))
142    core_bound = backend_bound.add_down("Core Bound", backend_bound - memory_bound)
143
144# top->backend_bound->memory_bound
145    stores_bound = memory_bound.add_down("Stores Bound", use('store_bound_cycles') / use('total_cycles'))
146    loads_bound = memory_bound.add_down("Loads Bound", use('load_bound_cycles') / use('total_cycles'))
147
148# top->backend_bound->core_bound
149    integer_dq = core_bound.add_down("Integer DQ", core_bound * use('stall_cycle_int_dq') / stall_cycles_core)
150    floatpoint_dq = core_bound.add_down("Floatpoint DQ", core_bound * use('stall_cycle_fp_dq') / stall_cycles_core)
151    rob = core_bound.add_down("ROB", core_bound * use('stall_cycle_rob') / stall_cycles_core)
152    integer_prf = core_bound.add_down("Integer PRF", core_bound * use('stall_cycle_int') / stall_cycles_core)
153    floatpoint_prf = core_bound.add_down("Floatpoint PRF", core_bound * use('stall_cycle_fp') / stall_cycles_core)
154    lsu_ports = core_bound.add_down("LSU Ports", core_bound * use('ls_dq_bound_cycles') / stall_cycles_core)
155
156# top->backend_bound->memory_bound->loads_bound
157    l1d_loads_bound = loads_bound.add_down("L1D Loads", use('l1d_loads_bound_cycles') / use('total_cycles'))
158    l2_loads_bound = loads_bound.add_down("L2 Loads", use('l2_loads_bound_cycles') / use('total_cycles'))
159    l3_loads_bound = loads_bound.add_down("L3 Loads", use('l3_loads_bound_cycles') / use('total_cycles'))
160    ddr_loads_bound = loads_bound.add_down("DDR Loads", use('ddr_loads_bound_cycles') / use('total_cycles'))
161
162# top->backend_bound->memory_bound->loads_bound->l1d_loads_bound
163    l1d_loads_mshr_bound = l1d_loads_bound.add_down("L1D Loads MSHR", use('l1d_loads_mshr_bound') / use('total_cycles'))
164    l1d_loads_tlb_bound = l1d_loads_bound.add_down("L1D Loads TLB", use('l1d_loads_tlb_bound') / use('total_cycles'))
165    l1d_loads_store_data_bound = l1d_loads_bound.add_down("L1D Loads sdata", use('l1d_loads_store_data_bound') / use('total_cycles'))
166    l1d_loads_bank_conflict_bound = l1d_loads_bound.add_down("L1D Loads\nBank Conflict", use('l1d_loads_bank_conflict_bound') / use('total_cycles'))
167    l1d_loads_vio_check_redo_bound = l1d_loads_bound.add_down("L1D Loads VioRedo", use('l1d_loads_vio_check_redo_bound') / use('total_cycles'))
168
169
170    return (
171        Sunburst(init_opts=opts.InitOpts(width="1000px", height="1200px"))
172        .add(series_name="", data_pair=top.draw(), radius=[0, "90%"])
173        .set_global_opts(title_opts=opts.TitleOpts(title=head))
174        .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}")))
175
176
177title = sys.argv[1]
178directory = sys.argv[2]
179suffix = sys.argv[3]
180print(title)
181(
182    Page(page_title=title, layout=Page.SimplePageLayout)
183    .add(process_one(directory + "/csv/" + title + ".log.csv", title + "_" + suffix))
184    .render(directory + "/html/" + title + ".html"))
185