委托调试¶
委托后端 是设备上模型的一个重要组成部分,因为它们在定义行为方面具有灵活性。这种灵活性的一个副作用是它作为一个不透明的转换操作。这使得丰富的关联和突变变得模糊,而这些在后处理中是有价值的。
例如,如果在委托中发生两个不同的算子融合,则后处理将无法区分这两种变换。
具体来说,这使得通过委托图关联运行时信息(如性能分析结果)变得困难。Delegate Debug Identifiers 提供了一个框架,使委托作者能够传播此类信息并将其用于运行后分析。
准备工作分为三个阶段:
提前(AOT):委托作者生成调试句柄映射。
运行时:委托作者使用在调试句柄映射中 AOT 注册的委托调试标识符进行日志记录。
反序列化:委托作者为委托事件中的自定义元数据提供解析器。
即时集成¶
委托作者通过从后端实现返回调试句柄映射来传播在降低的后端中发生的转换。
生成调试句柄映射¶
调试句柄映射通过将委托调试标识符映射到调试句柄,来传达后端中发生的转换。
委托调试标识符 是用于表示运行时兴趣点的生成或用户提供的标识符。请记住,调试句柄是模型图中运算符实例的唯一标识符。
例如:
{ 0: (10, 11), 1: (11, 12) }: 运行时中的标识符 0 和 1 分别对应具有调试句柄 (10, 11) 和 (11, 12) 的运算符。
{ “fused_op_1_2_3”: (11, 12, 15) }:运行时中的标识符"fused_op_1_2_3"对应于具有调试句柄 (11, 12, 15) 的算子,其中 11、12 和 15 分别对应算子 1、算子 2 和算子 3。
注意
标识符是将运行时结果连接到模型图的一种手段;标识符的解释由委托作者定义。
调试句柄映射 通过使用 委托映射构建器 构建,并作为 PreprocessResult 的一部分返回。
class PreprocessResult:
processed_bytes: bytes = bytes()
debug_handle_map: Optional[
Union[Dict[int, Tuple[int]], Dict[str, Tuple[int]]]
] = None
PreprocessResult 定义在 这里。
DelegateMappingBuilder¶
DelegateMappingBuilder 是一个用于管理和构建调试句柄映射的辅助类。构建器的结果应在构造 PreprocessResult 时传入。
DelegateMappingBuilder 的定义位于 此处
一个 DelegateMappingBuilder 实例可以通过两种模式之一构建:手动标识符或生成的标识符。
# Manual Identifiers, Default
builder = DelegateMappingBuilder(generated_identifiers=False)
# Generated Identifiers
builder = DelegateMappingBuilder(generated_identifiers=True)
使用手动标识符时,用户在创建条目时需传入一个委托调试标识符。 使用自动生成标识符时,构建器将自动分配一个委托调试标识符。
要向调试句柄映射中添加条目,请使用insert_delegate_mapping_entry。它将其中一个fx.Node(s)或调试句柄(从节点.meta["debug_handle"]获取)与一个可选的代理调试标识符关联起来(用于手动标识符)。记录的标识符将从调用中返回。
def insert_delegate_mapping_entry(
self,
nodes: Optional[Union[Node, List[Node]]] = None,
handles: Optional[Union[int, List[int]]] = None,
identifier: Optional[Union[int, str]] = None,
) -> Union[int, str]:
要检索< strong>调试句柄映射,请使用get_delegate_mapping。
def get_delegate_mapping(
self,
) -> Union[Dict[int, Tuple[int]], Dict[str, Tuple[int]]]
AOT映射的演示可以在 这里 找到
运行时日志记录¶
对应于 AOT 映射,运行时随后定义了用于记录这些事件的功能。
实时日志¶
ExecuTorch 允许您进行实时日志记录。实时日志记录在执行过程中有可用时间戳时非常有用。它提供最小的开销,并且对作者来说调用起来很直观。
要实时记录事件(例如,明确表示分析的开始和结束),event_tracer_start_profiling_delegate 用于创建一个 EventEntry,而 event_tracer_end_profiling_delegate 用于为提供的 EventTracer 结束 EventEntry。
要开始使用 EventTracerEntry 和 event_tracer_start_profiling_delegate,需要将 **委托调试标识符**(由 AOT 提供给 debug_handle_map)作为名称或 delegate_debug_id 参数传递,具体取决于 **委托调试标识符** 的类型(字符串和整数分别对应)。
EventTracerEntry event_tracer_start_profiling_delegate(
EventTracer* event_tracer,
const char* name,
DebugHandle delegate_debug_id)
要得出一个EventTracerEntry,只需提供原始的event_tracer_end_profiling_delegate。
可选地,此时也可以记录额外的运行时 metadata。
void event_tracer_end_profiling_delegate(
EventTracer* event_tracer,
EventTracerEntry event_tracer_entry,
const void* metadata = nullptr,
size_t metadata_len = 0)
训练后日志记录¶
ExecuTorch 还允许您在事后时间进行记录。某些运行时设置在执行期间无法访问时间戳。事后日志记录使作者仍能够记录这些事件。
要在事件记录中进行后期处理(例如,同时记录开始和结束时间)event_tracer_log_profiling_delegate 会与实时日志记录 API 和时间戳使用的参数组合一起调用。
void event_tracer_log_profiling_delegate(
EventTracer* event_tracer,
const char* name,
DebugHandle delegate_debug_id,
et_timestamp_t start_time,
et_timestamp_t end_time,
const void* metadata = nullptr,
size_t metadata_len = 0)
运行时代码的演示可以在 这里 找到。
从委托事件中提取自定义元数据¶
如上面运行时日志API所示,用户可以记录一个字节数组以及他们的委托分析事件。我们通过
用户在创建 Inspector 实例时可以传入一个元数据解析器。该解析器是一个可调用对象,用于反序列化数据并返回字符串列表或包含键值对的字典。反序列化后的数据随后会被添加回事件块中对应的事件,供用户使用。以下是编写此解析器的示例:
注意:反序列器的输入是一个列表,其中每个条目是一系列字节(本质上每个条目是不可变的 bytearray)。用户需要遍历该列表,对每个条目进行反序列化,然后以预期格式返回结果,即字符串列表或字典。
Inspector(
etdump_path=etdump_path,
# Optional
etrecord=etrecord_path,
# Optional, only needed if debugging was enabled.
buffer_path=buffer_path,
delegate_metadata_parser=parse_delegate_metadata
)
def parse_delegate_metadata(delegate_metadatas: List[bytes]) -> Union[List[str], Dict[str, Any]]:
metadata_str = []
for metadata_bytes in delegate_metadatas:
metadata_str += str(metadata_bytes)
return metadata_str