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.