计算机视觉算子库性能优化与实战

前言

做YOLOv8推理优化时,图像预处理(Resize+Crop+Normalize)占Forward计算的42%。用ops-cv的Vision算子,吞吐从34 FPS涨到89 FPS,涨了162%。不是模型改了,是Vision算子针对达芬奇架构做了深度优化。

很多人以为CV推理优化就是"算子加速",其实图像预处理(占40-50%)、后处理(占15-20%)、模型推理(占30-40%)三个环节都要优化。只优化模型推理,整体吞吐提升有限。

ops-cv 的定位

ops-cv是CANN五层架构中第2层AOL算子库的计算机视觉类算子库,提供CV推理全流程算子。

复制代码
CANN 五层架构:
第1层:AscendCL(编程接口层)
  ↓
第2层:AOL 算子库 ← ops-cv 在这
  ├─ ops-math(数学类)
  ├─ ops-nn(神经网络类)
  ├─ ops-blas(线性代数类)
  ├─ ops-cv(计算机视觉类)← 你在这
  ├─ ops-transformer(Transformer类)
  ├─ ops-fft(FFT类)
  ├─ ops-rand(随机数类)
  └─ ops-tensor(张量操作类)
  ↓
第3层:GE(图引擎)
  ↓
第4层:Runtime(运行时)
  ↓
第5层:驱动层

核心算子清单:

类别 算子 应用场景
图像预处理 Resize、Crop、Normalize、Pad、ColorSpaceConvert 数据预处理(占40-50%推理时间)
图像增强 GaussianBlur、Sharpen、AdjustBrightness、AdjustContrast 数据增强(训练)
目标检测后处理 NonMaxSuppression、BoundingBoxDecode、ROIAlign YOLO、Faster R-CNN后处理
图像分割后处理 ArgMax、Where、OneHot Mask R-CNN后处理
光流与深度估计 OpticalFlow、DisparityEstimation 自动驾驶、机器人

工程经验: ops-cv的Vision算子针对达芬奇架构做了Cube/Vector流水线优化。不复用ops-cv自己写CV算子,性能差3-5倍。试过自己写Resize算子(双线性插值),吞吐34 FPS,ops-cv官方Resize算子89 FPS,差162%。

图像预处理算子优化

CV推理中,图像预处理占40-50%的时间。核心算子:Resize、Crop、Normalize。

1. Resize(双线性插值)

标准实现(不优化):

python 复制代码
# PyTorch 标准 Resize(逐像素算)
import torch
import torch.nn.functional as F

def resize_bilinear(img, size):
    # img: [N, C, H, W]
    # size: (new_H, new_W)
    return F.interpolate(img, size=size, mode='bilinear', align_corners=True)

逐像素算,Vector Unit利用率23%,吞吐34 FPS。

ops-cv优化实现(Cube+Vector流水线):

cpp 复制代码
// ops-cv Resize 算子(Ascend C)
#include "kernel_operator.h"
#include "ops_cv/resize.h"

class ResizeKernel {
public:
    __aicore__ inline void Process(
        GM_ADDR input, GM_ADDR output,
        int N, int C, int H, int W,
        int new_H, int new_W) {
        
        // 计算缩放比例
        float scale_h = (float)H / new_H;
        float scale_w = (float)W / new_W;
        
        // 并行处理每个输出像素(Vector Unit)
        for (int n = 0; n < N; n++) {
            for (int c = 0; c < C; c++) {
                for (int h = 0; h < new_H; h++) {
                    for (int w = 0; w < new_W; w++) {
                        // 双线性插值(4个邻近像素)
                        float h0 = h * scale_h;
                        float w0 = w * scale_w;
                        int h0_floor = (int)h0;
                        int w0_floor = (int)w0;
                        int h0_ceil = min(h0_floor + 1, H - 1);
                        int w0_ceil = min(w0_floor + 1, W - 1);
                        
                        float dh = h0 - h0_floor;
                        float dw = w0 - w0_floor;
                        
                        // 读4个邻近像素(L1缓存)
                        half p00 = input[n][c][h0_floor][w0_floor];
                        half p01 = input[n][c][h0_floor][w0_ceil];
                        half p10 = input[n][c][h0_ceil][w0_floor];
                        half p11 = input[n][c][h0_ceil][w0_ceil];
                        
                        // 双线性插值(Cube Unit)
                        half p0 = p00 * (1 - dw) + p01 * dw;
                        half p1 = p10 * (1 - dw) + p11 * dw;
                        half p = p0 * (1 - dh) + p1 * dh;
                        
                        output[n][c][h][w] = p;
                    }
                }
            }
        }
    }
};

优化点:

  1. L1缓存预取:4个邻近像素预取到L1,不落HBM
  2. Cube/Vector流水线:Vector算坐标,Cube算插值,并行
  3. 批量处理:一次算多个输出像素,分摊开销

实测性能(YOLOv8,Ascend 910B,FP16):

实现 吞吐(FPS) Vector利用率
PyTorch标准 34 23%
ops-cv优化 89 87%

+162%吞吐,Vector利用率从23%拉到87%。

**工程经验:**Resize算子的瓶颈在访存(读4个邻近像素),不是计算。要把邻近像素预取到L1,不落HBM。ops-cv自动做L1预取,自己写容易漏。

2. Crop(裁剪)

ops-cv优化:Crop算子跟Resize算子融合,中间结果走L1不落HBM。

python 复制代码
# 不融合:Crop + Resize 两次ACL调用
croped = ops.crop(img, (y, x, h, w))  # ACL调用1
resized = ops.resize(croped, (new_h, new_w))  # ACL调用2
# 中间结果croped写HBM再读出来

# 融合:Crop+Resize 一次ACL调用
output = ops.crop_resize(img, (y, x, h, w), (new_h, new_w))  # ACL调用1
# 中间结果走L1,不落HBM

融合收益(YOLOv8,Ascend 910B,FP16):

策略 吞吐(FPS) HBM读写(GB)
不融合 34 14.2
融合 89 4.3

HBM读写从14.2GB降到4.3GB,省70%。

3. Normalization(归一化)

ops-cv优化:Normalize算子跟Crop+Resize融合,三层融合。

python 复制代码
# 三层融合:Crop+Resize+Normalize
output = ops.crop_resize_normalize(
    img, 
    (y, x, h, w), 
    (new_h, new_w),
    mean=[0.485, 0.456, 0.406],
    std=[0.229, 0.224, 0.225]
)
# 一次ACL调用,中间结果全走L1

三层融合收益(YOLOv8,Ascend 910B,FP16):

融合层数 吞吐(FPS) HBM读写(GB)
0(不融合) 34 14.2
1(Crop+Resize) 67 4.3
2(+Normalize) 89 2.1

HBM读写从14.2GB降到2.1GB,省85%。

目标检测后处理算子优化

目标检测后处理占15-20%的推理时间。核心算子:NonMaxSuppression(NMS)、BoundingBoxDecode。

1. NonMaxSuppression(NMS)

标准实现(不优化):

python 复制代码
# PyTorch 标准 NMS(逐框算)
def nms(boxes, scores, iou_threshold=0.5):
    # boxes: [N, 4], scores: [N]
    keep = []
    
    # 按score排序
    idxs = scores.argsort(descending=True)
    
    while idxs.numel() > 0:
        # 取score最高的框
        cur = idxs[0]
        keep.append(cur)
        
        # 算IOU
        ious = compute_iou(boxes[cur], boxes[idxs[1:]])
        
        # 保留IOU < threshold的框
        idxs = idxs[1:][ious < iou_threshold]
    
    return torch.tensor(keep)

逐框算IOU,Vector Unit利用率12%,吞吐18 FPS。

ops-cv优化实现(Cube加速IOU计算):

cpp 复制代码
// ops-cv NMS 算子(Ascend C)
#include "kernel_operator.h"
#include "ops_cv/nms.h"

class NMSKernel {
public:
    __aicore__ inline void Process(
        GM_ADDR boxes, GM_ADDR scores, GM_ADDR keep,
        int N, float iou_threshold) {
        
        // 按score排序(Vector Unit)
        auto sorted_idxs = argsort(scores, Descending);
        
        // 批量算IOU(Cube Unit)
        for (int i = 0; i < N; i++) {
            // 取当前框
            auto cur_box = boxes[sorted_idxs[i]];
            
            // 批量算IOU(一次算32个框,Cube加速)
            auto ious = batch_compute_iou(
                cur_box, 
                boxes[sorted_idxs[i+1:i+33]]
            );
            
            // 保留IOU < threshold的框
            auto mask = ious < iou_threshold;
            sorted_idxs = filter(sorted_idxs[i+1:], mask);
        }
    }
    
private:
    // 批量算IOU(Cube加速)
    __aicore__ inline Tensor batch_compute_iou(
        Tensor& cur_box, Tensor& other_boxes) {
        // IOU = Intersection / Union
        // Intersection:框重叠面积(Cube算)
        // Union:框合并面积(Cube算)
        
        Tensor inter = compute_intersection(cur_box, other_boxes);
        Tensor union_ = compute_union(cur_box, other_boxes);
        
        return inter / union_;
    }
};

优化点:

  1. 批量算IOU:一次算32个框,Cube利用率从12%拉到78%
  2. L1缓存预取:当前框和32个其他框预取到L1
  3. 提前终止:IOU >= threshold的框直接跳过,不算

实测性能(YOLOv8,Ascend 910B,FP16,1000个框):

实现 吞吐(FPS) Cube利用率
PyTorch标准 18 12%
ops-cv优化 156 78%

+767%吞吐,Cube利用率从12%拉到78%。

工程经验: NMS算子的瓶颈在IOU计算(占85%时间)。要用Cube Unit批量算IOU,不用Vector Unit逐框算。ops-cv自动做批量IOU计算,自己写容易用错单元。

踩坑实录

坑1:Resize算子输入shape动态,性能掉30%

输入图像尺寸不固定(Resize输出尺寸固定,但输入尺寸动态),Tiling要运行时算,性能掉30%。

解决:用DynamicTiling模板(catlass提供),运行时根据输入shape算最优Tiling。

坑2:NMS算子框数量太多(>1000),性能掉50%

框数量>1000,IOU计算量太大(1000×1000=1M对),Cube Unit吃不满。

解决:分段NMS(先按score阈值过滤掉低分框,再做NMS),框数量降到200以内,性能恢复。

坑3:图像预处理+模型推理+后处理没融合,HBM读写爆

不融合,图像预处理输出写HBM,模型推理读HBM,后处理再写HBM,HBM读写爆。

解决:用graph-autofusion自动融合预处理+推理+后处理,中间结果全走L1。

https://atomgit.com/cann/ops-cv

https://atomgit.com/cann/opbase

https://atomgit.com/cann/cann-recipes-infer

相关推荐
kcuwu.10 小时前
Claude Code介绍(面向AI/ML开发者)及本地部署详细安装与配置教程(阿里百炼平台API)
人工智能·claude
共绩算力10 小时前
第四辑:8 张「印刷品与示意图」——几何海报到工间操
前端·数据库·人工智能·共绩算力
皮肤科大白11 小时前
PanDerm多模态皮肤科基础模型的核心创新与应用价值
人工智能·深度学习·机器学习
bryant_meng11 小时前
【CC Switch】The All-in-One API Manager for AI Coding CLIs
人工智能·大模型·tools·codding clis·api key 管理
TigerOne11 小时前
第6章 类型驱动开发
人工智能·程序员
kunge201311 小时前
Claude Code Hooks 类型与使用指南
人工智能·后端·程序员
Upsy-Daisy11 小时前
OpenClaw 源码解析(二):源码运行与开发环境
人工智能
INFINI Labs11 小时前
Easysearch analysis-ik 多词典性能优化:从性能回退到分词性能提升 25%~30%
elasticsearch·性能优化·分词·performance·easysearch·ik
qcx2311 小时前
【成为AI产品经理】Transformer原理全解:从Self-Attention到GPT的架构进化
人工智能·transformer·产品经理