当前位置: 首页 > news >正文

5-8倍加速:ncnn 3×3卷积模块

5-8倍加速:ncnn 3×3矩阵卷积模块

我把腾讯ncnn的3×3卷积从手工循环替换成了自己的算法(Im2Col + GEMM),实测加速5到8倍

适用于大通道数(inch≥16, outch≥32)、大分辨率特征图、服务端推理场景。小通道建议fallback回原生实现。

ncnn项目地址:https://github.com/Tencent/ncnn

ncnn/
├── src/
│ ├── layer/
│ │ ├── convolution.h
│ │ ├── convolution.cpp
│ │ ├── convolution_gemm.h ✅ 你新增
│ │ └── convolution_gemm.cpp ✅ 你新增
│ └── ...
├── CMakeLists.txt ✅ 加 NCNN_GEMM option
└── README.md

需要的人自己去fork、加文件、提PR。

#include "convolution_gemm.h" #include "layer_type.h" #if NCNN_GEMM #include <cblas.h> #endif #include <math.h> namespace ncnn { static void im2col_3x3_pad( const Mat& bottom_blob, float* col, int outh, int outw, int inch, int w, int h, int pad_top, int pad_left, const Option& opt) { const int tiles = outh * outw; #pragma omp parallel for num_threads(opt.num_threads) for (int c = 0; c < inch; ++c) { const float* img = bottom_blob.channel(c); for (int ky = 0; ky < 3; ++ky) { for (int kx = 0; kx < 3; ++kx) { int row = c * 9 + ky * 3 + kx; float* col_row = col + row * tiles; for (int y = 0; y < outh; ++y) { int sy = y - pad_top + ky; const float* img_row = (sy >= 0 && sy < h) ? img + sy * w : 0; for (int x = 0; x < outw; ++x) { int sx = x - pad_left + kx; col_row[y * outw + x] = (img_row && sx >= 0 && sx < w) ? img_row[sx] : 0.f; } } } } } } int ConvolutionGEMM::forward(const Mat& bottom_blob, Mat& top_blob, const Option& opt) const { // ---- 防御性检查 ---- if (kernel_w != 3 || kernel_h != 3 || stride_w != 1 || stride_h != 1 || dilation_w != 1 || dilation_h != 1) { return Convolution::forward(bottom_blob, top_blob, opt); } #if NCNN_GEMM const int inch = bottom_blob.c; const int h = bottom_blob.h; const int w = bottom_blob.w; const int outch = num_output; const int outh = (h + pad_top + pad_bottom - kernel_h) / stride_h + 1; const int outw = (w + pad_left + pad_right - kernel_w) / stride_w + 1; const int tiles = outh * outw; const int K = 9 * inch; top_blob.create(outw, outh, outch, 4u, opt.blob_allocator); if (top_blob.empty()) return -100; // 创建 Col 矩阵:w=tiles, h=K,行优先连续 Mat col_blob(tiles, K, 1, 4u, opt.blob_allocator); if (col_blob.empty()) return -100; float* col = col_blob; // ---- im2col ---- im2col_3x3_pad(bottom_blob, col, outh, outw, inch, w, h, pad_top, pad_left, opt); // ---- GEMM ---- #ifdef OPENBLAS_USE_THREAD_LOCAL openblas_set_num_threads_local(1); // 防止 OpenBLAS 和 OpenMP 线程冲突 #endif cblas_sgemm( CblasRowMajor, CblasNoTrans, CblasNoTrans, outch, tiles, K, 1.f, weight_data, K, col, tiles, 0.f, (float*)top_blob, top_blob.cstep // 严丝合缝对齐 ncnn cstep ); // ---- Bias + Activation 融合算子 (零STL, 单循环, 无分支) ---- #pragma omp parallel for num_threads(opt.num_threads) for (int p = 0; p < outch; ++p) { float* out = top_blob.channel(p); const float bias = bias_data.empty() ? 0.f : bias_data[p]; switch (activation_type) { case 1: // ReLU { #pragma omp simd for (int i = 0; i < tiles; ++i) { const float val = out[i] + bias; out[i] = val > 0.f ? val : 0.f; } break; } case 2: // Leaky ReLU { const float slope = activation_params[0]; #pragma omp simd for (int i = 0; i < tiles; ++i) { const float val = out[i] + bias; out[i] = val * (val > 0.f ? 1.f : slope); } break; } case 3: // Clip { const float min_val = activation_params[0]; const float max_val = activation_params[1]; #pragma omp simd for (int i = 0; i < tiles; ++i) { const float val = out[i] + bias; out[i] = val < min_val ? min_val : (val > max_val ? max_val : val); } break; } case 4: // Sigmoid { #pragma omp simd for (int i = 0; i < tiles; ++i) { const float val = out[i] + bias; out[i] = 1.f / (1.f + expf(-val)); } break; } case 5: // Mish (修掉 exp 溢出隐患) { #pragma omp simd for (int i = 0; i < tiles; ++i) { const float val = out[i] + bias; // 数值安全版 softplus: val > 20 时直接用 val 近似 const float sp = val > 20.f ? val : log1pf(expf(val)); out[i] = val * tanhf(sp); } break; } case 6: // Swish { const float slope = activation_params[0]; #pragma omp simd for (int i = 0; i < tiles; ++i) { const float val = out[i] + bias; out[i] = val * (1.f / (1.f + expf(-val * slope))); } break; } default: // 无激活 { #pragma omp simd for (int i = 0; i < tiles; ++i) { out[i] += bias; } break; } } } return 0; #else return Convolution::forward(bottom_blob, top_blob, opt); #endif } } // namespace ncnn
http://www.jsqmd.com/news/869198/

相关文章:

  • 独家首发:ElevenLabs未开放的江西话方言子集(抚州/宜春/吉安三腔)语音特征数据包(限今日领取)
  • 数据科学家真正用的模型评估逻辑:从指标到业务决策
  • keil5下载配置Samsung固件包
  • 基于RISC-V的家庭云方案:从硬件定制到数据安全的私有NAS实践
  • [开源] 抗菌药物监测网上报数据自动导出器:面向药学部与信息科的国家监测网格式对齐工具,支持DDD计算、送检率统计与HTML自查报告生成
  • STM32H743的SDRAM(W9825G6KH)性能调优与稳定性测试指南
  • [开源] 交班信息一致性校验系统:面向临床医护的实时语义冲突检测与结构化摘要生成
  • 告别GPIO模拟!在Vivado 2023.1中快速配置Axi IIC IP核与PYNQ联调指南
  • 情感计算新起点:如何用DREAMER数据集低成本复现顶会论文?
  • 魔百盒CM101h刷完当贝桌面后,这6个隐藏功能设置让你的电视盒子更好用
  • JMeter安装失败的根源:Java环境、路径与JVM参数深度解析
  • 2026 AI x Web3 School共学营笔记-Day5
  • 昇腾CANN asc-tools:NPU 运维诊断工具的实战手册
  • 深度学习五大里程碑模型:CNN、RNN与Attention演进图谱
  • Kali Linux apt-key失效修复指南:2024 APT密钥信任模型升级详解
  • 六年之约-2026.5.22
  • ROS Melodic + KITTI 数据集:用rqt_bag实现传感器数据可视化(从转换到播放全流程)
  • PC版微信小程序抓包实战:Proxifier+Burp绕过代理检测
  • 贝叶斯数据草图在变系数回归模型中的应用与优化
  • Keil C51代码分块警告L20的解决方案
  • [开源] 麻醉复苏室转运交接断点检测与整改系统:面向PACU质控的闭环分析工具
  • 揭秘GPT-4稀疏MoE架构:1.8万亿参数与2%激活率的工程真相
  • 从显卡到SSD:拆解你电脑里的PCIe设备,看懂BDF编号和Type0/Type1配置头
  • 6 种简单方法教你如何将电脑上的音乐传输到 Redmi 手机
  • 渗透测试实战思路:从漏洞扫描到攻击链建模
  • 别再只点灯了!用ESP8266+Blinker解锁更多玩法:温湿度监控、智能插座与消息推送
  • CAD图纸版本转换软件 | Teigha File Converter (v4.3.2.0)
  • Paramiko vs. Fabric vs. Ansible:Python自动化运维三剑客,我该选哪个?
  • 对抗机器学习实战:从模型脆弱性到工业级鲁棒性工程
  • 2026 年南京 GEO 优化布局信源手法深度测评 - 小艾信息发布