环保设备网站源码,萧山品牌网站建设,怎样推广小程序,怎么把做的网站发布最近接到一个任务#xff1a;把原本跑在英伟达GPU上的业务迁移到国产化昇腾#xff08;Ascend#xff09;平台。模型不大#xff0c;是个魔改版的MobileNetV2#xff0c;对时延非常敏感。原本在GPU上单次推理仅需25ms左右#xff0c;迁移后直接飙到50ms。经过深度的Profi…最近接到一个任务把原本跑在英伟达GPU上的业务迁移到国产化昇腾Ascend平台。模型不大是个魔改版的MobileNetV2对时延非常敏感。原本在GPU上单次推理仅需25ms左右迁移后直接飙到50ms。经过深度的Profiling分析我发现问题的核心不在算力而在“调度”。通过针对性的参数调优最终把推理时间压到18ms左右比原来还快了7ms左右。这篇文章不讲虚的直接复盘从环境搭建、踩坑、分析到最终优化的完整过程代码全部基于CANN 8.0实测可用。一、环境准备1.1 为啥会变慢在开始之前有必要说下背景。我们的业务逻辑包含复杂的预处理和后处理模型本身计算量其实不大MobileNet级别。昇腾910系列是为大规模训练和高密度计算设计的类似A100它的强项是高并发、大吞吐。而跑小batch、小模型时CPU下发任务的开销很容易超过GPU/NPU实际计算的时间。这就好比用一辆重型卡车去送一份外卖启动和换挡的时间比路上跑的时间还长。1.2 启动Notebook点击上方的“Notebook 工作区”进入你的云端开发环境管理页面首次使用会提示激活直接确认开通即可1.3 选择配置在启动页面选择以下配置硬件昇腾910B AI加速芯片32核VCPU 64GB内存镜像euler2.9-py38-torch2.1.0-cann8.0-openmind0.6-notebook存储50GB这个镜像预装了Python 3.8、PyTorch 2.1.0昇腾适配版、CANN 8.0驱动可以直接用。1.4 安装NPU库进入JupyterLab后新建一个ipynb文件执行!pip install torch-npu1.5 验证环境安装完后测试NPU是否可用注意必须先import torch_npuimport torch_npu import torch print(PyTorch版本:, torch.__version__) print(NPU可用:, torch.npu.is_available()) print(设备编号:, torch.npu.current_device())正常输出应该显示NPU可用输出结果如下二、问题复现2.1 准备模型用一个轻量级的MobileNetV2作为测试模型这个模型参数量适中能很好地暴露小模型在昇腾上的性能问题import torch import torch_npu import torchvision.models as models import time # 加载模型 model models.mobilenet_v2(pretrainedTrue) device torch.device(npu:0) model model.to(device) model.eval() # 准备输入 input_data torch.randn(1, 3, 224, 224).to(device) # 预热这步很重要不预热首次推理会很慢 with torch.no_grad(): for _ in range(10): _ model(input_data) print(模型加载完成)部署结果如图所示这就显示我们的模型已经加载成功2.2 性能测试分别测试推理和数据传输的耗时# 推理阶段计时 with torch.no_grad(): start time.time() output model(input_data) infer_time time.time() - start # 数据传输计时 start time.time() output_cpu output.to(cpu) transfer_time time.time() - start print(f推理耗时: {infer_time*1000:.2f}ms) print(f传输耗时: {transfer_time*1000:.2f}ms) print(f总耗时: {(infer_timetransfer_time)*1000:.2f}ms)在我的测试中输出结果如下我们可以看到结果是推理耗时: 45.23ms传输耗时: 8.67ms总耗时: 53.90ms看起来推理占大头但这个数字其实有问题。2.3 错误判断一开始觉得推理慢试了几种办法都没用调整batch size换tensor格式异步操作后来查阅文档才醒悟昇腾NPU的执行是异步的。Python代码跑完了指令只是下发到了任务队列NPU可能还在干活。三、Profiling分析3.1 采集数据既然不是带宽问题那时间到底去哪了我祭出了Ascend PyTorch Profiler。这是解决性能问题的杀手锏绝对不要靠猜。from torch_npu.profiler import profile # 创建profiler并采集数据 with profile( activities[ torch_npu.profiler.ProfilerActivity.CPU, torch_npu.profiler.ProfilerActivity.NPU ], record_shapesTrue, with_stackTrue ) as prof: with torch.no_grad(): # 执行推理 output model(input_data) result output.to(cpu) # 强制同步确保NPU执行完成 torch_npu.npu.synchronize() # 导出trace文件 prof.export_chrome_trace(./mobilenet_profile.json) print(Profiling数据已保存到 mobilenet_profile.json)运行代码结果如下这段代码会生成一个json文件记录了每个算子的详细执行情况这里要注意必须加同步如果不加torch_npu.npu.synchronize()导出的数据会不完整。因为CPU和NPU异步执行prof.stop()的时候NPU可能还没跑完。3.2 分析结果把生成的json文件下载到本地用MindStudio打开其时间线Timeline界面支持缩小、放大和左右移动等功能具体操作如下所示单击时间线Timeline界面树状图或者图形化窗格任意位置可以使用键盘中的W放大和S缩小键进行操作支持放大的最大精度为1ns。单击时间线Timeline界面树状图或者图形化窗格任意位置使用键盘中的A左移、D右移键或者方向键左键左移、右键右移进行左右移动也可使用方向键上键上移、下键下移进行上下移动。将鼠标放置在时间线Timeline界面树状图或者图形化窗格任意位置可以使用键盘中的Ctrl键加鼠标滚轮实现缩放操作。在图形化窗格中使用键盘中的Ctrl键加鼠标左键可以实现左右拖拽泳道图表。在Timeline视图里能看到.to(cpu)算子实际只花了5-8ms真正的时间消耗在卷积、BN等推理算子上算子之间有明显的调度间隙之前以为的传输慢其实是假象真正的问题是推理本身的效率不够。四、性能优化4.1 优化思路根据Timeline分析我们要做的就是减少CPU干预于是我做了两个部分的优化开启任务队列加速这是CANN 8.0的一个重要特性。开启后可以减少CPU和NPU之间的交互开销把一堆任务打包扔进队列。操作os.environ[TASK_QUEUE_ENABLE] 2关闭JIT动态编译默认情况下PyTorch可能会尝试动态编译算子。对于这种标准模型直接用昇腾内置的二进制算子库Binary速度最快没有编译开销。操作torch_npu.npu.set_compile_mode(jit_compileFalse)4.2 优化代码完整的优化版本import torch import torch_npu import torchvision.models as models import time import os # 第一步设置环境变量启用流水优化 os.environ[TASK_QUEUE_ENABLE] 2 # 第二步加载模型 model models.mobilenet_v2(pretrainedTrue) device torch.device(npu:0) model model.to(device) model.eval() # 第三步编译优化 torch_npu.npu.set_compile_mode(jit_compileFalse) torch_npu.npu.config.allow_internal_format False # 第四步充分预热 input_data torch.randn(1, 3, 224, 224).to(device) with torch.no_grad(): for _ in range(20): # 预热次数增加到20次 _ model(input_data) torch_npu.npu.synchronize() print(优化配置完成开始性能测试...) # 第五步性能测试 iterations 100 with torch.no_grad(): # 同步点确保之前操作都完成 torch_npu.npu.synchronize() start time.time() for _ in range(iterations): output model(input_data) result output.to(cpu) # 再次同步确保所有操作完成 torch_npu.npu.synchronize() end time.time() avg_time (end - start) / iterations print(f\n优化后性能:) print(f平均推理时间: {avg_time*1000:.2f}ms) print(f吞吐量: {1/avg_time:.2f} samples/sec)4.3 参数说明TASK_QUEUE_ENABLE2这个环境变量让NPU的任务队列管理更激进减少CPU-NPU之间的同步等待。在小batch场景下提升明显。jit_compileFalse关闭JIT编译强制使用预编译算子库。避免运行时编译带来的开销。allow_internal_formatFalse禁止算子内部格式转换比如自动转NZ格式减少不必要的layout变换。版本要求驱动固件23.0.3CANN工具包8.0.RC1老版本设置这些参数无效必须先升级。4.4 结果展示运行优化后的代码输出结果优化后性能中平均推理时间: 18.32ms吞吐量: 54.59 samples/sec这对比优化前的53.90ms性能提升了约65%。同时为了更好的对比在同一台910B机器上我运行了优化前后的对比脚本结果非常惊人。五、完整测试5.1 对比脚本写一个完整的对比测试包含优化前后的性能数据import torch import torch_npu import torchvision.models as models import time import os def test_baseline(): 基线测试未优化版本 model models.mobilenet_v2(pretrainedTrue) device torch.device(npu:0) model model.to(device) model.eval() input_data torch.randn(1, 3, 224, 224).to(device) # 简单预热 with torch.no_grad(): for _ in range(5): _ model(input_data) # 测试 iterations 100 with torch.no_grad(): torch_npu.npu.synchronize() start time.time() for _ in range(iterations): output model(input_data) _ output.to(cpu) torch_npu.npu.synchronize() end time.time() return (end - start) / iterations def test_optimized(): 优化版本 os.environ[TASK_QUEUE_ENABLE] 2 model models.mobilenet_v2(pretrainedTrue) device torch.device(npu:0) model model.to(device) model.eval() # 编译优化 torch_npu.npu.set_compile_mode(jit_compileFalse) torch_npu.npu.config.allow_internal_format False input_data torch.randn(1, 3, 224, 224).to(device) # 充分预热 with torch.no_grad(): for _ in range(20): _ model(input_data) torch_npu.npu.synchronize() # 测试 iterations 100 with torch.no_grad(): torch_npu.npu.synchronize() start time.time() for _ in range(iterations): output model(input_data) _ output.to(cpu) torch_npu.npu.synchronize() end time.time() return (end - start) / iterations # 执行对比测试 print( * 60) print(性能对比测试) print( * 60) print(\n测试基线版本...) baseline_time test_baseline() print(f基线平均耗时: {baseline_time*1000:.2f}ms) print(\n测试优化版本...) optimized_time test_optimized() print(f优化平均耗时: {optimized_time*1000:.2f}ms) improvement (baseline_time - optimized_time) / baseline_time * 100 print(f\n性能提升: {improvement:.1f}%) print( * 60)5.2 结果分析运行完整测试后输出结果如下指标优化前 (Baseline)优化后 (Optimized)变化幅度Kernel Latency53.84 ms18.27 ms↓ 66%FPS (QPS)~18.5~54.6↑ 2.95x稳定性波动较大非常稳定-优化效果非常明显推理时间从53ms降到18ms提升了66%这个提升主要归功于TASK_QUEUE和关闭JIT。Timeline显示优化后算子之间的Gap大幅缩短流水线变得紧凑。六、经验总结一开始觉得是.to(cpu)慢结果完全不是。性能问题必须用工具定位Python层面的计时在异步场景下不可靠。尤其是在涉及NPU/GPU异步计算时不加synchronize()测出来的时间都是假的。对于ResNet18、MobileNet这种小计算量模型调度开销是杀手开启任务队列优化是必须的。MindStudio的Timeline视图非常直观能精确到每个算子的微秒级耗时。这个工具一定要学会用。同时昇腾NPU首次推理会慢很多。这次优化让我对昇腾平台有了新认识。工具链确实没CUDA那么成熟但整体在快速进步。CANN 8.0之后很多之前需要手动hack的地方都有了官方方案。性能调优的核心还是找准瓶颈用对方法注明昇腾PAE案例库对本文写作亦有帮助。