大规模部署功能#
创建日期: 2019年7月24日 | 最后更新日期: 2025年7月15日
本笔记讨论了在更大的系统内运行 PyTorch 或在大型组织中运营多个使用 PyTorch 的系统时可能很有用的几个扩展点和技巧。
该笔记假定您要么从源代码构建 PyTorch,要么能够静态链接额外的代码以在 PyTorch 使用时加载。因此,许多钩子作为 C++ API 公开,可以在一个集中的地方(例如,在静态初始化代码中)触发一次。
全集群算子性能分析#
PyTorch 提供了 torch.autograd.profiler,能够按需测量单个算子所花费的时间。可以使用相同的机制对任何运行 PyTorch 的进程进行“始终开启”的测量。这可能有助于收集有关给定进程或整个机器集中的 PyTorch 工作负载的信息。
可以使用 torch::addGlobalCallback 添加对任何算子调用的新回调。钩子将使用 torch::RecordFunction 结构进行调用,该结构描述了调用上下文(例如,name)。如果启用,RecordFunction::inputs() 将包含表示为 torch::IValue 变体的函数的参数。请注意,输入日志记录相对昂贵,因此必须显式启用。
算子回调还可以访问 c10::ThreadLocalDebugInfo::get() 接口,该接口返回一个指向持有调试信息的结构体的指针。此调试信息可以通过使用 at::DebugInfoGuard 对象提前设置。调试信息会通过前向(包括异步 fork 任务)和后向传播,并可用于将应用程序较高层的一些额外信息(例如模型 ID)传递到底层算子回调。
调用回调会产生一些开销,因此通常只需要随机采样算子调用。这可以通过传递给 torch::addGlobalCallback 的可选采样率,在每个回调的基础上启用。
请注意,addGlobalCallback 不是线程安全的,并且只能在没有 PyTorch 算子运行时调用。通常,在初始化期间调用它们一次是一个好主意。
这是一个例子
// Called somewhere in the program beginning
void init() {
// Sample one in a hundred operator runs randomly
addGlobalCallback(
RecordFunctionCallback(
&onFunctionEnter,
&onFunctionExit)
.needsInputs(true)
.samplingProb(0.01)
);
// Note, to enable observers in the model calling thread,
// call enableRecordFunction() in the thread before running a model
}
void onFunctionEnter(const RecordFunction& fn) {
std::cerr << "Before function " << fn.name()
<< " with " << fn.inputs().size() << " inputs" << std::endl;
}
void onFunctionExit(const RecordFunction& fn) {
std::cerr << "After function " << fn.name();
}
API 使用情况日志记录#
在更广泛的生态系统中运行时,例如在托管作业调度器中,跟踪哪些二进制文件调用了特定的 PyTorch API 通常很有用。存在注入到几个重要 API 点的简单仪器,用于触发给定的回调。由于 PyTorch 通常在一次性 python 脚本中调用,因此回调为每个 API 在给定进程中只触发一次。
c10::SetAPIUsageHandler 可用于注册 API 使用情况仪器处理程序。传递的参数将是一个“api 密钥”,用于识别使用的点,例如 PyTorch 扩展导入的 python.import。
SetAPIUsageLogger([](const std::string& event_name) {
std::cerr << "API was used: " << event_name << std::endl;
});
给开发者的说明:可以在 C++ 中使用 C10_LOG_API_USAGE_ONCE("my_api") 或在 Python 中使用 torch._C._log_api_usage_once("my.api") 在代码中添加新的 API 触发点。
通用扩展点#
PyTorch API 通常是松耦合的,很容易用专用版本替换组件。通用扩展点包括
在 C++ 中实现的自定义算子 - 有关详细信息,请参阅 教程。
自定义数据读取通常可以直接通过调用相应的 python 库来集成。通过扩展
Dataset或IterableDataset,可以利用torch.utils.data的现有功能。