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_typeoccurrences_in_delegated_graphsoccurrences_in_non_delegated_graphs
0aten__native_batch_norm_legit_no_training_default520
1aten_add_tensor100
2aten_convolution_default520
3aten_hardtanh_default350
4aten_linear_default10
5aten_mean_dim10
6aten_view_copy_default01
7dim_order_ops__clone_dim_order_default01
8getitem522
9Total2034

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.