Skip to content

Commit 121726a

Browse files
authored
Timeline viewer: Add more label presentation options (#127)
1 parent 74954f7 commit 121726a

File tree

2 files changed

+89
-15
lines changed

2 files changed

+89
-15
lines changed

lglpy/timeline/data/processed_trace.py

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@
3535
MetadataASTransfer, GPUStreamID, GPUStageID
3636

3737
LABEL_HEURISTICS = True
38-
LABEL_MAX_LEN = 60
38+
LABEL_MAX_LEN = 42
39+
# Where to add ellipsis if label is too long
40+
# - left = ...X
41+
# - center = X...X
42+
# - right = X...
43+
LABEL_SPLIT = 'left'
3944

4045

4146
class GPUWorkload:
@@ -78,30 +83,30 @@ def __init__(
7883
self.submit = None
7984
self.label_stack = None
8085
self.parsed_label_name: Optional[str] = None
86+
self.parsed_label_name_full: Optional[str] = None
8187

8288
if metadata:
8389
self.submit = metadata.submit
8490
self.label_stack = metadata.label_stack
8591

86-
def get_label_name(self) -> Optional[str]:
92+
def get_label_name_full(self) -> Optional[str]:
8793
'''
88-
Get a cleaned up label name for a workload.
89-
90-
Warning: The heuristics here are not robust.
94+
Get a cleaned up label name for a workload with no shortening.
9195
9296
Returns:
93-
A modified label for use in the UI.
97+
A label for use in the UI.
9498
'''
99+
# Cached label already parsed
100+
if self.parsed_label_name_full is not None:
101+
return self.parsed_label_name_full
102+
95103
# No label to parse
96104
if not self.label_stack:
97105
return None
98106

99-
# Cached label already parsed
100-
if self.parsed_label_name is not None:
101-
return self.parsed_label_name
102-
103107
if not LABEL_HEURISTICS:
104-
return self.label_stack[-1]
108+
self.parsed_label_name_full = self.label_stack[-1]
109+
return self.parsed_label_name_full
105110

106111
# Create a copy we can edit ...
107112
labels = list(self.label_stack)
@@ -141,7 +146,36 @@ def get_label_name(self) -> Optional[str]:
141146
else:
142147
label = '.'.join(labels)
143148

144-
if len(label) > LABEL_MAX_LEN:
149+
self.parsed_label_name_full = label
150+
return self.parsed_label_name_full
151+
152+
def get_label_name(self) -> Optional[str]:
153+
'''
154+
Get a cleaned up label name for a workload.
155+
156+
Warning: The heuristics here are not robust.
157+
158+
Returns:
159+
A modified label for use in the UI.
160+
'''
161+
# Cached label already parsed
162+
if self.parsed_label_name is not None:
163+
return self.parsed_label_name
164+
165+
label = self.get_label_name_full()
166+
167+
# No label to parse
168+
if not label:
169+
return None
170+
171+
# Apply ellipsis shortening
172+
if LABEL_SPLIT == 'left' and len(label) > LABEL_MAX_LEN:
173+
label = f'...{label[-LABEL_MAX_LEN:]}'
174+
175+
elif LABEL_SPLIT == 'right' and len(label) > LABEL_MAX_LEN:
176+
label = f'{label[0:LABEL_MAX_LEN]}...'
177+
178+
elif LABEL_SPLIT == 'center' and len(label) > LABEL_MAX_LEN:
145179
half_max = LABEL_MAX_LEN // 2
146180
prefix = label[0:half_max]
147181
postfix = label[-half_max:]
@@ -176,6 +210,26 @@ def get_workload_name(self) -> str:
176210

177211
return label
178212

213+
def get_workload_name_full(self) -> str:
214+
'''
215+
Get a name for the workload.
216+
217+
This is based on the application debug label if there is one, but
218+
with some heuristics to try and clean is up ...
219+
220+
Returns:
221+
Returns the label for use in the UI.
222+
'''
223+
label = None
224+
if self.label_stack:
225+
label = self.get_label_name_full()
226+
227+
# Default label if no label or get_label_name heuristics stripped it
228+
if not label:
229+
return self.get_workload_type_name()
230+
231+
return label
232+
179233
def get_long_label(self) -> str:
180234
'''
181235
Get the long form label for this workload.

lglpy/timeline/gui/timeline/info_widget.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from collections import defaultdict
3030

3131
from ...data.raw_trace import GPUStreamID, GPUStageID
32-
from ...data.processed_trace import GPUWorkload
32+
from ...data.processed_trace import GPUWorkload, LABEL_MAX_LEN
3333
from ...drawable.text_pane_widget import TextPaneWidget
3434

3535

@@ -39,6 +39,7 @@ class TimelineInfoWidget(TextPaneWidget):
3939
time ranges in the main timeline.
4040
'''
4141

42+
FULL_LABEL = True
4243
MAX_EVENTS = 5
4344
VALIDSORTS = ['flush', 'runtime']
4445

@@ -296,8 +297,27 @@ def compute_active_event_stats_single(self, event):
296297
metrics.append('')
297298
metrics.append('Workload properties:')
298299

299-
label = event.get_workload_name()
300-
metrics.append(f' Name: {label}')
300+
if self.FULL_LABEL:
301+
label = event.get_workload_name_full()
302+
else:
303+
label = event.get_workload_name()
304+
305+
# Chunk a long label into line-width sections
306+
chunk_size = LABEL_MAX_LEN + 3
307+
next_chunk_size = chunk_size + 4
308+
chunks = []
309+
while label:
310+
part = label[0: chunk_size]
311+
chunks.append(part)
312+
313+
label = label[chunk_size:]
314+
chunk_size = next_chunk_size
315+
316+
# Print a label in chunks
317+
metrics.append(f' Name: {chunks[0]}')
318+
for part in chunks[1:]:
319+
metrics.append(f' {part}')
320+
301321
metrics.append(f' Stream: {stream_name}')
302322
metrics.append(f' Stage: {stage_name}')
303323
metrics.append(f' Duration: {event.duration / 1000000.0:0.2f} ms')

0 commit comments

Comments
 (0)