# DJL官方优化技巧
本文档翻译自DJL官方文档:inference_performance_optimization (opens new window)
本文档涵盖了基于不同引擎调优推理性能的多种技巧,包括多线程支持、引擎线程配置以及如何启用DNNL(MKLDNN)。
# 多线程支持
Deep Java Library (DJL) 的优势之一是支持多线程推理。与 Python 相比,它能帮助提高多核 CPU 和 GPU 上的推理吞吐量,并减少内存消耗。
DJL 的 Predictor
并非设计为线程安全。由于某些引擎本身不是线程安全的,我们通过 Predictor 来屏蔽这些差异。对于线程安全的引擎,我们不做处理;对于非线程安全的引擎,我们会按需创建副本。
因此,我们建议为每个线程创建新的 Predictor (opens new window)。如果线程需要执行多次预测,应复用该 Predictor。此外,您也可以使用 Predictor 池或利用 DJL Serving 的 WorkLoadManager (opens new window)。
参考实现请查看 多线程基准测试 (opens new window)。
您可能还需要设置引擎特定的配置,具体细节如下。也可参考 DJL 所有系统配置列表 (opens new window)。
# Apache MXNet
# 引擎配置
要使用 Apache MXNet 引擎运行多线程,请完成以下步骤:
# 启用 Apache MXNet 的 NaiveEngine
默认情况下,MXNet 会延迟执行操作符,并通过内部多线程设计占用整个机器资源。这在训练期间或优先考虑延迟而非吞吐量时效果良好。如果在 Java 多线程推理场景中使用 MXNet 引擎,需要通过以下命令指定 'MXNET_ENGINE_TYPE' 环境变量:
export MXNET_ENGINE_TYPE=NaiveEngine
为获得最佳吞吐量,您可能还需要设置 'OMP_NUM_THREADS' 环境变量:
export OMP_NUM_THREADS=1
# PyTorch
# 图执行器优化
PyTorch 图执行器优化器(JIT tensorexpr fuser)默认启用。当使用新批次大小进行前几次推理时,torchscript 会为模型生成优化的执行图。
禁用图执行器优化会导致最大吞吐量和性能损失,具体取决于模型和硬件。使用 djl-bench (opens new window) 检查此优化对硬件的影响。仅当您没有时间使用给定批次大小对模型进行"预热",或需要使用动态批次大小时才应禁用它。
可通过设置以下系统属性全局禁用图执行器优化器:
-Dai.djl.pytorch.graph_optimizer=false
图执行器优化器是每线程配置。重要提示:必须在每个使用模型的线程上禁用它。如果忘记在某个线程上禁用,将触发优化过程并延迟所有正在运行/挂起的执行。
Engine.getEngine("PyTorch"); // 确保加载 PyTorch 引擎
JniUtils.setGraphExecutorOptimize(false);
# oneDNN(MKLDNN) 加速
与 TensorFlow 和 Apache MXNet 不同,PyTorch 默认不启用 MKLDNN。它被视作类似 CPU 和 GPU 的设备类型。可通过环境变量启用:
-Dai.djl.pytorch.use_mkldnn=true
如果数据类型或操作符不受 oneDNN 设备支持,可能会出现异常。
# 在 AWS Graviton3 上优化 oneDNN(MKLDNN)
AWS Graviton3(E)(如 c7g/m7g/r7g、c7gn 和 Hpc7g 实例)支持用于 ML 加速的 BF16 格式。可通过设置以下环境变量在 oneDNN 中启用:
grep -q bf16 /proc/cpuinfo && export DNNL_DEFAULT_FPMATH_MODE=BF16
为避免冗余原始创建延迟开销,可通过设置 LRU 缓存容量启用原始缓存。注意:此缓存特性会增加内存占用。建议根据具体用例调整容量至最优值:
export LRU_CACHE_CAPACITY=1024
除避免冗余分配外,可通过 Linux 透明大页 (THP) 优化张量内存分配延迟。启用 THP 分配需设置以下 torch 环境变量:
export THP_MEM_ALLOC_ENABLE=1
有关如何在 AWS Graviton3 实例上实现最佳 PyTorch 推理性能的更多详情,请参阅 PyTorch Graviton 教程 (opens new window)。
# CuDNN 加速
PyTorch 有专门用于 CNN 或相关网络加速的标志。如果输入大小不频繁变化,可在模型中启用此配置:
-Dai.djl.pytorch.cudnn_benchmark=true
如果输入形状频繁变化,此更改可能导致性能下降。更多信息请查看此文章 (opens new window)。
# 线程配置
可通过两个配置优化推理性能:
-Dai.djl.pytorch.num_interop_threads=[互操作线程数]
此配置设置 JIT 解释器分叉操作并行执行的数量。
-Dai.djl.pytorch.num_threads=[线程数]
此配置设置操作内部的线程数,默认设置为 CPU 核心数。
更多细节请参考 PyTorch 文档 (opens new window)。
# TensorFlow
# 多线程推理
使用 TensorFlow 引擎进行多线程推理的步骤与其他引擎相同。建议每个线程使用一个 Predictor
,避免每次推理调用都创建新 Predictor
。可参考我们的 多线程基准测试 (opens new window) 示例。以下是使用 TensorFlow 引擎运行的命令:
cd djl-serving
./gradlew benchmark --args='-e TensorFlow -c 100 -t -1 -u djl://ai.djl.tensorflow/resnet/0.0.1/resnet50 -s 1,224,224,3'
# oneDNN(MKLDNN) 加速
默认情况下,TensorFlow 引擎已启用 oneDNN,无需特殊配置。
# 线程配置
建议在多线程推理期间使用 1 个线程进行算子并行。可通过设置以下 3 个环境变量配置:
export OMP_NUM_THREADS=1
export TF_NUM_INTEROP_THREADS=1
export TF_NUM_INTRAOP_THREADS=1
# ONNXRuntime
# 线程配置
可在 Criteria 中使用以下设置进行线程优化:
Criteria.builder()
.optOption("interOpNumThreads", <线程数>)
.optOption("intraOpNumThreads", <线程数>)
...
技巧:开始时将两者都设为 1 观察性能,然后尝试将其中一个设置为 总核心数
/总 Java 推理线程数
观察性能变化。
# (GPU)TensorRT 后端
如果已安装 TensorRT,可在 Criteria 中尝试以下 ONNXRuntime 后端进行性能优化:
Criteria.builder()
.optOption("ortDevice", "TensorRT")
...