Debugging Delegation#
We provide a list of util functions to give users insights on what happened to the graph modules during the to_backend()
stage.
Get delegation summary#
The get_delegation_info()
method provides a summary of what happened to the model after the to_backend()
call:
import torch
from executorch.backends.xnnpack.partition.xnnpack_partitioner import XnnpackPartitioner
from executorch.exir import to_edge_transform_and_lower
from torch.export import Dim, export
from torchvision.models.mobilenetv2 import MobileNet_V2_Weights
import torchvision.models as models
# Dependency needed for debugging delegates
from executorch.devtools.backend_debug import get_delegation_info
from tabulate import tabulate
model = models.mobilenetv2.mobilenet_v2(weights=MobileNet_V2_Weights.DEFAULT).eval()
sample_inputs = (torch.randn(1, 3, 224, 224), )
et_program = to_edge_transform_and_lower(
torch.export.export(model, sample_inputs),
partitioner=[XnnpackPartitioner()]
)
graph_module = et_program.exported_program().graph_module
delegation_info = get_delegation_info(graph_module)
# print the summary like the number of delegated nodes, non-delegated nodes, etc
print(delegation_info.get_summary())
df = delegation_info.get_operator_delegation_dataframe()
# print the table including op_type, occurrences_in_delegated_graphs, occurrences_in_non_delegated_graphs
print(tabulate(df, headers="keys", tablefmt="fancy_grid"))
Example printout:
Total delegated subgraphs: 2
Number of delegated nodes: 203
Number of non-delegated nodes: 4
op_type |
occurrences_in_delegated_graphs |
occurrences_in_non_delegated_graphs |
|
---|---|---|---|
0 |
aten__native_batch_norm_legit_no_training_default |
52 |
0 |
1 |
aten_add_tensor |
10 |
0 |
2 |
aten_convolution_default |
52 |
0 |
3 |
aten_hardtanh_default |
35 |
0 |
4 |
aten_linear_default |
1 |
0 |
5 |
aten_mean_dim |
1 |
0 |
6 |
aten_view_copy_default |
0 |
1 |
7 |
dim_order_ops__clone_dim_order_default |
0 |
1 |
8 |
getitem |
52 |
2 |
9 |
Total |
203 |
4 |
From the table, the operator aten_view_copy_default
appears 0 times in delegate graphs and 1 times in non-delegated graphs. Users can use information like this to debug. get_item node
is a special case, it means getting the output from the delegate subgraph.
Visualize delegated graph#
To see a more detailed view, use the format_delegated_graph()
method to get a string representation of the entire graph or use print_delegated_graph()
to print directly:
from executorch.exir.backend.utils import format_delegated_graph
graph_module = et_program.exported_program().graph_module
print(format_delegated_graph(graph_module)) # or call print_delegated_graph(graph_module)
It will print the whole model as well as the subgraph consumed by the backend. The generic debug function provided by fx like print_tabular()
or print_readable()
will only show call_delegate
and hide the subgraph consumed by the backend, while this function exposes the contents inside the subgraph.
In the example printout below, observe that there are two subgraphs, aten_view_copy_default
is not delegated, while most of the others ops are delegated.