# 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")
    ...