Skip to content

Commit b52f6c1

Browse files
tarun292facebook-github-bot
authored andcommitted
Add delegate time scale converter to Inspector (#3240)
Summary: The time scale of delegate events reported might be different from the timescale of CPU events. This diff adds support for providing a callable that can be invoked by Inspector to modify the timescale of delegated events to ensure consistency in timescales across delegated and non-delegated events. Reviewed By: Jack-Khuu Differential Revision: D55298701
1 parent 719b368 commit b52f6c1

File tree

4 files changed

+100
-7
lines changed

4 files changed

+100
-7
lines changed

examples/apple/coreml/scripts/inspector_cli.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import argparse
88
import json
99

10-
from typing import Any, Dict, Final, List, Tuple
10+
from typing import Any, Dict, Final, List, Tuple, Union
1111

1212
from executorch.sdk import Inspector
1313
from executorch.sdk.inspector._inspector_utils import compare_results
@@ -34,6 +34,12 @@ def parse_coreml_delegate_metadata(delegate_metadatas: List[str]) -> Dict[str, A
3434
return {}
3535

3636

37+
def convert_coreml_delegate_time(
38+
event_name: Union[str, int], input_time: Union[int, float]
39+
) -> Union[int, float]:
40+
return input_time / (1000 * 1000)
41+
42+
3743
def main() -> None:
3844
parser = argparse.ArgumentParser()
3945
parser.add_argument(
@@ -60,6 +66,7 @@ def main() -> None:
6066
etrecord=args.etrecord_path,
6167
debug_buffer_path=args.debug_buffer_path,
6268
delegate_metadata_parser=parse_coreml_delegate_metadata,
69+
delegate_time_scale_converter=convert_coreml_delegate_time,
6370
)
6471
inspector.print_data_tabular(include_delegate_debug_data=True)
6572
if args.compare_results:

sdk/inspector/_inspector.py

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,9 @@ class Event:
312312
_instruction_id: Optional[int] = None
313313

314314
_delegate_metadata_parser: Optional[Callable[[List[str]], Dict[str, Any]]] = None
315+
_delegate_time_scale_converter: Optional[
316+
Callable[[Union[int, str], Union[int, float]], Union[int, float]]
317+
] = None
315318

316319
@cached_property
317320
def delegate_debug_metadatas(self) -> Union[List[str], Dict[str, Any]]:
@@ -391,6 +394,9 @@ def _gen_from_inference_events(
391394
delegate_metadata_parser: Optional[
392395
Callable[[List[str]], Dict[str, Any]]
393396
] = None,
397+
delegate_time_scale_converter: Optional[
398+
Callable[[Union[int, str], Union[int, float]], Union[int, float]]
399+
] = None,
394400
) -> "Event":
395401
"""
396402
Given an EventSignature and a list of Events with that signature,
@@ -411,6 +417,7 @@ def _gen_from_inference_events(
411417
name="",
412418
_instruction_id=signature.instruction_id,
413419
_delegate_metadata_parser=delegate_metadata_parser,
420+
_delegate_time_scale_converter=delegate_time_scale_converter,
414421
)
415422

416423
# Populate fields from profile events
@@ -476,14 +483,35 @@ def _populate_profiling_related_fields(
476483
f"Expected exactly one profile event per InstructionEvent when generating Inspector Event, but got {len(profile_events)}"
477484
)
478485

486+
profile_event = profile_events[0]
487+
479488
# Scale factor should only be applied to non-delegated ops
480-
scale_factor_updated = 1 if ret_event.is_delegated_op else scale_factor
489+
if (
490+
ret_event.is_delegated_op
491+
and ret_event._delegate_time_scale_converter is not None
492+
):
493+
scaled_time = ret_event._delegate_time_scale_converter(
494+
ret_event.name,
495+
profile_event.end_time,
496+
# pyre-ignore
497+
) - ret_event._delegate_time_scale_converter(
498+
ret_event.name, profile_event.start_time
499+
)
500+
# If it's not a delegated op then we can just use the raw time values
501+
# and then scale them according to the scale factor that was passed in.
502+
elif not ret_event.is_delegated_op:
503+
scaled_time = (
504+
float(profile_event.end_time - profile_event.start_time)
505+
/ scale_factor
506+
)
507+
# If there was no scale factor passed in just take a difference of the
508+
# end and start times.
509+
else:
510+
scaled_time = float(
511+
profile_event.end_time - profile_event.start_time
512+
)
481513

482-
profile_event = profile_events[0]
483-
data.append(
484-
float(profile_event.end_time - profile_event.start_time)
485-
/ scale_factor_updated
486-
)
514+
data.append(scaled_time)
487515
delegate_debug_metadatas.append(
488516
profile_event.delegate_debug_metadata
489517
if profile_event.delegate_debug_metadata
@@ -646,6 +674,9 @@ def _gen_from_etdump(
646674
delegate_metadata_parser: Optional[
647675
Callable[[List[str]], Dict[str, Any]]
648676
] = None,
677+
delegate_time_scale_converter: Optional[
678+
Callable[[Union[int, str], Union[int, float]], Union[int, float]]
679+
] = None,
649680
) -> List["EventBlock"]:
650681
"""
651682
Given an etdump, generate a list of EventBlocks corresponding to the
@@ -743,6 +774,7 @@ class GroupedRunInstances:
743774
scale_factor,
744775
output_buffer,
745776
delegate_metadata_parser,
777+
delegate_time_scale_converter,
746778
)
747779
for signature, instruction_events in run_group.items()
748780
]
@@ -875,6 +907,9 @@ def __init__(
875907
delegate_metadata_parser: Optional[
876908
Callable[[List[str]], Dict[str, Any]]
877909
] = None,
910+
delegate_time_scale_converter: Optional[
911+
Callable[[Union[int, str], Union[int, float]], Union[int, float]]
912+
] = None,
878913
enable_module_hierarchy: bool = False,
879914
) -> None:
880915
r"""
@@ -930,6 +965,7 @@ def __init__(
930965
self._target_time_scale,
931966
output_buffer,
932967
delegate_metadata_parser=delegate_metadata_parser,
968+
delegate_time_scale_converter=delegate_time_scale_converter,
933969
)
934970

935971
# Connect ETRecord to EventBlocks

sdk/inspector/tests/TARGETS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ python_unittest(
99
"//executorch/exir:lib",
1010
"//executorch/sdk:lib",
1111
"//executorch/sdk/debug_format:et_schema",
12+
"//executorch/sdk/etdump:schema_flatcc",
1213
"//executorch/sdk/etrecord/tests:etrecord_test_library",
1314
"//executorch/sdk/inspector:inspector",
1415
"//executorch/sdk/inspector:lib",

sdk/inspector/tests/inspector_test.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,15 @@
1717
from executorch.exir import ExportedProgram
1818
from executorch.sdk import generate_etrecord, parse_etrecord
1919
from executorch.sdk.debug_format.et_schema import OperatorNode
20+
from executorch.sdk.etdump.schema_flatcc import ProfileEvent
2021
from executorch.sdk.etrecord.tests.etrecord_test import TestETRecord
2122

2223
from executorch.sdk.inspector import _inspector, Event, EventBlock, Inspector, PerfData
24+
from executorch.sdk.inspector._inspector import (
25+
InstructionEvent,
26+
InstructionEventSignature,
27+
ProfileEventSignature,
28+
)
2329

2430

2531
OP_TYPE = "aten::add"
@@ -183,6 +189,49 @@ def test_inspector_associate_with_op_graph_nodes_multiple_debug_handles(self):
183189
expected_ops = ["op_0", "op_1"]
184190
self.assertEqual(event_with_multiple_debug_handles.op_types, expected_ops)
185191

192+
def test_inspector_delegate_time_scale_converter(self):
193+
def time_scale_converter(event_name, time):
194+
return time / 10
195+
196+
event = Event(
197+
name="",
198+
_delegate_metadata_parser=None,
199+
_delegate_time_scale_converter=None,
200+
)
201+
event_signature = ProfileEventSignature(
202+
name="",
203+
instruction_id=0,
204+
delegate_id_str="test_event",
205+
)
206+
instruction_events = [
207+
InstructionEvent(
208+
signature=InstructionEventSignature(0, 0),
209+
profile_events=[
210+
ProfileEvent(
211+
name="test_event",
212+
chain_index=0,
213+
instruction_id=0,
214+
delegate_debug_id_int=None,
215+
delegate_debug_id_str="test_event_delegated",
216+
start_time=100,
217+
end_time=200,
218+
delegate_debug_metadata=None,
219+
)
220+
],
221+
)
222+
]
223+
Event._populate_profiling_related_fields(
224+
event, event_signature, instruction_events, 1
225+
)
226+
# Value of the perf data before scaling is done.
227+
self.assertEqual(event.perf_data.raw[0], 100)
228+
event._delegate_time_scale_converter = time_scale_converter
229+
Event._populate_profiling_related_fields(
230+
event, event_signature, instruction_events, 1
231+
)
232+
# Value of the perf data after scaling is done. 200/10 - 100/10.
233+
self.assertEqual(event.perf_data.raw[0], 10)
234+
186235
def test_inspector_get_exported_program(self):
187236
# Create a context manager to patch functions called by Inspector.__init__
188237
with patch.object(

0 commit comments

Comments
 (0)