性能调优

最优性能参数配置

配置类型

配置项

最优配置

调度配置

maxPrefillBatchSize

  • 使用maxPrefillTokens/数据集平均输入长度,得到maxPrefillBatchSize。
  • 替代方案:maxBatchSize的一半(可微调)。

maxPrefillTokens

  • 卡的理想输入token数(吞吐最大)。
  • 替代方案:maxPrefillBatchSize*70(可微调。如果允许数据泄漏,70可以设置成数据集平均输入长度)。

maxBatchSize

表6中根据公式计算出的最大值。

maxPreemptCount

10。

supportSelectBatch

true

模型配置

npuDeviceIds

[0, 1, 2, ..., worldSize-1]

worldSize

节点的最大可用卡数。

npuMemSize

表5中根据公式计算出的最大值,单位GB。

资源配置

preAllocBlocks

表1范围内适当调小。理论上为ceil(数据集平均输出长度/cacheBlockSize)最佳,但当前不开满可能会卡死。

maxBatchSize调优指导

在mindservice.log日志中,过滤测试时间段对应的“COMPLETED REQ ID”关键字如下:

...COMPLETED REQ ID:  363 , 5   , 593 , 81  , 512 , 12  , 60  , 1
...COMPLETED REQ ID:  377 , 5   , 237 , 87  , 150 , 12  , 60  , 1
以第一行日志为例,每一列数据的含义如表1所示。
表1 第一行日志“COMPLETED REQ ID”关键字各列数据含义

363

5

593

81

512

12

60

1

请求ID。

请求使用的Block Num。

总序列长度。

输入序列长度。

输出序列长度。

当前BatchSize大小。

当前batch使用的Block Num。

本次推理完成的请求个数。

性能的最优值就是根据第7列的NpuBlock使用个数和Total Block Num个数之间的关系调整BatchSize,具体调整策略如下:

操作步骤

这里以llama-65b模型为例进行最优性能的配置,环境信息举例如下:

  1. 请参考表5中的“npuMemSize”参数计算出npuMemSize的值并根据计算结果调整配置中的该值,计算过程如下所示。

    1. 计算模型的权重大小,计算公式为:参数量*tensor字节数(这两个值请用户根据模型自行获取)。

      llama-65b模型的参数量为650亿,tensor字节数为2,通过计算公式得出llama-65b的权重约为130G。

    2. 计算npuMemSize的值,计算公式为:Floor[(总空闲-权重/NPU卡数-后处理占用)* 系数],系数取值为0.8。

      npuMemSize = Floor[ (64 - 3 - 130/8 - 2)] * 0.8 = 34G。

      “Floor”表示计算结果向下取整。

  2. 请参考表6中的“maxBatchSize”参数计算出maxBatchSize的值并根据计算结果调整配置中的该值,计算过程如下所示。

    根据计算公式:maxBatchSize = Total Block Num/Block Num,需要先计算出Total Block Num和Block Num的值。
    1. 计算Total Block Num的值。

      根据计算公式:Total Block Num = Floor[NPU显存/(模型网络层数*Block Size*模型注意力头数*注意力头大小*Cache类型字节数*Cache数)],公式中各参数的取值信息如表2所示。

      表2 Total Block Num公式中的参数值

      参数

      取值

      NPU显存

      1中npuMemSize的值。

      模型网络层数

      模型网络层数是模型权重文件config.json中的hidden_layers值,取值为:80。

      Block Size

      默认值:128。

      模型注意力头数

      “模型注意力头数*注意力头大小”的值为模型权重文件中config.json中的hidden_size值。

      说明:

      对于llama2模型,由于是GQA类模型(分组查询注意力类模型),需使用模型权重文件config.json中num_key_value_heads的值作为注意力头数,而不是num_attention_heads。所以每张卡上的“模型注意力头数*注意力头大小”的值不一定是用hidden_size/卡数。例如llama2-70b的“模型注意力头数*注意力头大小”的值应该为8*128=1024,而不是hidden_size中的值:8192。

      注意力头大小

      Cache类型字节数

      由模型config.json文件中的torch.dtype决定,一般为float16类型,取值为:2。

      Cache数

      默认值:2。

      将以上参数值代入公式,得到Total Block Num=Floor[34*1024*1024*1024/(80 * 128 * 128 * 64/8 * 2 * 2)] = 870。

      注:以上算式中64/8是由于有8张NPU卡,所以注意头数需均分在8张卡上。

      Total Block Num的值也可以通过跑一次性能后在python日志中的npuBlockNum获取,详情请参考•吞吐量最大化

    2. 计算Block Num的值。

      根据计算公式:Block Num = Ceil(输入Token数/Block Size)+Ceil(最大输出Token数/Block Size),公式中各参数的取值信息如表3所示。

      表3 Block Num公式中的参数值

      参数

      取值

      输入Token数

      首次一般参考数据集的平均输入,取值100。

      第二次则可以直接取返回结果中的average_input_length的值。

      最大输出Token数

      从config.json文件中的maxIterTimes参数获取,取值:512。

      Block Size

      默认值:128。

      将以上参数值代入公式,得到Block Num = Ceil(100/128)+Ceil(512/128) = 5。

      “Ceil”表示计算结果向上取整。

    3. 根据2.a2.b计算出的Total Block Num和Block Num值,然后使用公式maxBatchSize=Total Block Num/Block Num得到maxBatchSize的值。

      maxBatchSize = 870/5 = 174。

  3. 根据2中maxBatchSize的值调整maxPrefillBatchSize和maxPrefillTokens的值,并将supportSelectBatch参数设置为“true”,详情请参考最优性能参数配置

    preAllocBlocks的值根据ceil(数据集平均输出长度/cacheBlockSize) 进行计算,约为1。

  4. 根据本机环境和选用的模型需要调整npuDeviceIds和worldSize的值,详情请参考最优性能参数配置。进行第一次调试,调试方法请参见llm_engine_test

    数据集如需要转换请参见数据集使用章节。

  5. 调整npuMemSize,在调试过程中,重开一个窗口使用以下命令查看占卡情况,如果剩余空间还很大,可以调大npuMemSize的值,重复2~4后再次进行调试。

    watch npu-smi info
    • 当npuMemSize的值不够大时,可以继续调大空闲值,如图1
      图1 npuMemSize值不够大
    • 当npuMemSize的值过大时,则会报“Npu out of memory”错误,如图2所示。
      图2 npuMemSize过大的情况
    • 根据“Npu out of memory”报错信息,将npuMemSize的值调小(比如在原来的值上减2,避免卡死),则可以达到最优npuMemSize的值。如图3,大概是占据95%卡的状态。
      图3 npuMemSize最优值

      这时从性能返回结果可以看出来lpot会优于配置之前的值,并且generate speed也有很大的提升。

  6. 根据用户需求通过以下方式进行吞吐量最大化的配置或者非首token时延(lpot)最小化的配置。

    • 吞吐量最大化

      通过调整maxBatchSize的值使吞吐量最大化。首先根据结果日志(logs/mindservice.logs)使用以下命令查看最大Total Block Num的值,如图4所示。

      grep "COMPLETED" mindservice*.log
      图4 最大Total Block Num值

      从上图中查看Total Block Num的峰值为854,比计算得到的Total Block Num值小。即可适当调大maxBatchSize的值(同步调整对应的maxPrefillBatchSize、maxPrefillTokens和maxPreemptCount的值)再进行一次性能操作。

      也可以通过将set_env.sh环境变量中打开Python日志(export MIES_PYTHON_LOG_TO_FILE=1),并在/workspace/log/pythonlog.log.xxxxx下对应日志中通过搜索npuBlockNum找到,如图5所示,值为896。

      图5 python日志中的npuBlockNum显示
    • 非首token时延(lpot)最小化

      为了让非首token时延(lpot)最小化,则需要调小maxBatchSize来达到最优性能,暂时只能通过阶梯调整。

      根据3对maxBatchSize进行调整,并调整对应的maxPrefillBatchSize、maxPrefillTokens和maxPreemptCount的值,对比哪个值可以达到要求。

      maxPrefillTokens的值不能小于4096。

      比如baichuan2-7b模型在Atlas 300I Duo 推理卡上进行的测试,lpot要求是小于80ms,这时maxBatchSize的值选择87时可以得到最小化的时延要求以及该时延下最优吞吐量。

      表4 maxBatchSize对应的lpot和generate speed

      maxBatchSize

      lpot(时延)

      generate speed(吞吐量)

      200

      160

      1145

      100

      88

      1074

      90

      81

      1058

      89

      80.4

      1058

      88

      80.4

      1060

      87

      79.7

      1032