在 RK3588 多线程推理 YOLO 时,同时开启硬件解码和 RGA 加速的性能分析
一、前言
本文是基于RK3588的YOLO多线程推理多级硬件加速引擎框架设计项目的延申与拓展,单独分析所提出的方案4的性能和加速原理,即同时开启 RKmpp 硬件视频解码和 RGA 硬件图像缩放、旋转。
二、实验结果回顾
在项目的总览篇中,给出了该方案的推理效果,这里重新贴上:
这个组合方式本应该是最快的方案,但是同时开启硬件解码和 RGA 加速,帧率就下降:
使用 15 个推理线程(继续增加没有明显变化),帧数稳定在 139 帧左右,反倒是等于没有使用硬件加速,与纯 OpenCV 实现的方案结果相近。
不过好消息是,相比于第一个方案,CPU 和 NPU 的占用率都有下降,CPU 占用率下降了约 21 个百分点,NPU 下降约 8 个百分点,这说明方案4的上限还没到,还有优化的空间。下面是 RGA 的使用情况,使用率与之前保持相同:
三、代码逻辑分析
1、加载器
方案4的视频加载器是 FFmpeg,并且已经提前编译好源码,支持 RKmpp 解码器,只需要在初始化时选择 h264_rkmpp 即可,其他流程就是普通的 FFmpeg C++接口配置方法,具体详见下面的文章:
2、取出图像
主函数的 for 循环每次取出一帧,然后传递给线程池,由于实操过程发现前几帧总是非正常图像,于是做了一个跳过处理:
/* 跳过不需要的帧 */
if (!video_reader_ptr->readFrame(img)) {// 如果已经成功开始处理图像,则代表已处理完所有图像或读取错误if (fps != 0)break;elsecontinue;}// 放入 rknn 线程池
if (!img.empty())if (yolo_pool.put(img) != 0)break;
video_reader_ptr 是 FFmpeg 和 OpenCV 的多态指针,在执行程序时根据命令行参数选择具体的实例化类,在方案 4 中,使用的是 FFmpeg,调用 RKmpp。下面是 FFmpeg 类的 readFrame 函数:
/*** @Description: 读取一帧* @param {Mat&} frame: 取出的帧* @return {*}*/
bool FFmpegReader::readFrame(cv::Mat& frame) {// 读取帧/*if (av_read_frame(formatContext, packet) < 0) {return false; // 没有更多帧}*/while (av_read_frame(formatContext, packet) >= 0) {if (packet->stream_index != videoStreamIndex) {av_packet_unref(packet);continue;}break;}// 如果是视频流if (packet->stream_index != videoStreamIndex) {cerr << "Not a video stream: " << packet->stream_index << " != " << videoStreamIndex << endl;av_packet_unref(packet);return false; // 不是视频流}// 发送数据包到解码器if (avcodec_send_packet(codecContext, packet) < 0) {std::cerr << "Failed to send packet to decoder" << std::endl;av_packet_unref(packet);return false; // 发送数据包失败}// 接收解码后的帧if (avcodec_receive_frame(codecContext, tempFrame) < 0) {std::cerr << "Failed to receive frame from decoder" << std::endl;av_packet_unref(packet);return false;}// 成功读取一帧,保存在 tempFrame 中// 将帧数据转换为 cv::Mat BGR 格式if (this->NV12_to_BGR(frame) != 0) {std::cerr << "Failed to convert YUV420SP to BGR" << std::endl;av_packet_unref(packet);return false;}// 释放数据包av_packet_unref(packet);return true; // 处理完成
}
这里的 av_read_frame 和上面逻辑一样,目的是跳过部分帧,我实测发现会在视频开始的第一次需要跳过,正常播放后就不会再进入循环。需要关注的是 NV12_to_BGR 函数,它是我用于转换图像格式的接口:
/*** @Description: 转换格式,NV12 转 BGR* 该函数内有三种转换方式:* 1. FFmpeg SwsContext 软件转换 * 2. OpenCV 软件转换,可启用 opencl(目前区别不大)* 3. RGA 硬件加速转换* @param {Mat&} frame: * @return {*}*/
int FFmpegReader::NV12_to_BGR(cv::Mat& bgr_frame) {if (tempFrame->format != AV_PIX_FMT_NV12) {return -EXIT_FAILURE; // 格式错误}// 设置输出帧的尺寸和格式,防止地址无法访问bgr_frame.create(tempFrame->height, tempFrame->width, CV_8UC3);#if 0 // 方式1:使用 FFmpeg SwsContext 软件转换return this->FFmpeg_yuv420sp_to_bgr(bgr_frame);
#endif// 创建一个完整的 NV12 数据块(Y + UV 交错)cv::Mat nv12_mat(tempFrame->height + tempFrame->height / 2, tempFrame->width, CV_8UC1);// 将 AVFrame 内的数据,转换为 OpenCV Mat 格式保存this->AV_Frame_To_CVMat(nv12_mat);// 硬件加速if (this->accels_2d == ACCELS_2D::ACC_OPENCV) {// 方式2:使用 OpenCV 软件转换cv::cvtColor(nv12_mat, bgr_frame, cv::COLOR_YUV2BGR_NV12);return EXIT_SUCCESS;} else if (this->accels_2d == ACCELS_2D::ACC_RGA) {// 方式3:使用 RGA 硬件加速转换return RGA_yuv420sp_to_bgr((uint8_t *)nv12_mat.data, tempFrame->width, tempFrame->height, bgr_frame);}elsereturn -EXIT_FAILURE;
}
具体的细节在注释中都有说明,有三种转换方式,分别是
1. FFmpeg SwsContext 软件转换
2. OpenCV 软件转换
3. RGA 硬件加速转换
方案 4 使用 RGA 进行转换。
3、色彩空间转换
取出的原始帧格式为 RK_FORMAT_YCbCr_420_SP(NV12),由于 YOLO 的输入格式需要 RGB888,因此需要使用 RGA 的 imcvtcolor 接口进行一次图像色彩空间的转换,所以针对 NV12 转 RGB888 编写了一个模块,数据的传输依赖 cv::Mat 对象:
/*** @Description: 将 YUV420SP(NV12) 格式的图像转换为 BGR 格式* @param {uint8_t} *frame_yuv_data: 原始 YUV 图像数据(这里为了减少对 AVFrame 的依赖,只传入 data 数据)* @param {Mat} &bgr_image: 转换后的 BGR 图像* @return {*}*/
int RGA_yuv420sp_to_bgr(const uint8_t *frame_yuv_data, const int& width, const int& height, cv::Mat &bgr_image) {rga_buffer_t src_img, dst_img;rga_buffer_handle_t src_handle, dst_handle;int src_width, src_height, src_format;int dst_width, dst_height, dst_format;int src_buf_size, dst_buf_size;memset(&src_img, 0, sizeof(src_img));memset(&dst_img, 0, sizeof(dst_img));/* 输入输出大小 */ src_width = width;src_height = height;dst_width = bgr_image.cols;dst_height = bgr_image.rows;/* 将转换前后的格式 */// 选自 rk 官方 demo 中的格式src_format = RK_FORMAT_YCbCr_420_SP;dst_format = RK_FORMAT_BGR_888;/* 使用图像数据构建 rga_buffer_t 结构体,两个指针指向同一个数据 */src_img = wrapbuffer_virtualaddr((void *)frame_yuv_data, src_width, src_height, src_format);dst_img = wrapbuffer_virtualaddr((void *)bgr_image.data, dst_width, dst_height, dst_format);/* 图像检查 */IM_STATUS STATUS;/*STATUS = imcheck(src_img, dst_img, {}, {});if (IM_STATUS_NOERROR != STATUS) {printf("%d, check error! %s", __LINE__, imStrError(STATUS));return -1;}*//* 将需要转换的格式与rga_buffer_t格式的结构体src、dst⼀同传⼊imcvtcolor() */STATUS = imcvtcolor(src_img, dst_img, src_format, dst_format);/* 释放内存(正确和错误均执行) */if (src_handle)releasebuffer_handle(src_handle);if (dst_handle)releasebuffer_handle(dst_handle);if (IM_STATUS_SUCCESS != STATUS) {fprintf(stderr, "rga resize error! %s", imStrError(STATUS));return -1;}return 0;
}/*** @Description: 将 YUV420SP(NV12) 格式的图像转换为 BGR 格式* @param {uint8_t} *frame_yuv_data: 原始 YUV 图像数据(这里为了减少对 AVFrame 的依赖,只传入 data 数据)* @param {Mat} &bgr_image: 转换后的 BGR 图像* @return {*}*/
int RGA_handle_yuv420sp_to_bgr(const uint8_t *frame_yuv_data, const int& width, const int& height, cv::Mat &bgr_image) {rga_buffer_t src_img, dst_img;rga_buffer_handle_t src_handle, dst_handle;int src_width, src_height, src_format;int dst_width, dst_height, dst_format;int src_buf_size, dst_buf_size;memset(&src_img, 0, sizeof(src_img));memset(&dst_img, 0, sizeof(dst_img));/* 输入输出大小 */ src_width = width;src_height = height;dst_width = bgr_image.cols;dst_height = bgr_image.rows;/* 将转换前后的格式 */// 选自 rk 官方 demo 中的格式src_format = RK_FORMAT_YCbCr_420_SP;dst_format = RK_FORMAT_BGR_888;/* 计算图像所需要的 buffer 大小 */src_buf_size = src_width * src_height * get_bpp_from_format(src_format);dst_buf_size = dst_width * dst_height * get_bpp_from_format(dst_format);/* 将缓冲区对应的物理地址信息映射到RGA驱动内部,并获取缓冲区相应的地址信息 */src_handle = importbuffer_virtualaddr((void *)frame_yuv_data, src_buf_size);dst_handle = importbuffer_virtualaddr(bgr_image.data, dst_buf_size);/* 封装为RGA图像结构 */src_img = wrapbuffer_handle(src_handle, src_width, src_height, src_format);dst_img = wrapbuffer_handle(dst_handle, dst_width, dst_height, dst_format);/* 图像检查 */IM_STATUS STATUS;/*STATUS = imcheck(src_img, dst_img, {}, {});if (IM_STATUS_NOERROR != STATUS) {printf("%d, check error! %s", __LINE__, imStrError(STATUS));return -1;}*//* 将需要转换的格式与rga_buffer_t格式的结构体src、dst⼀同传⼊imcvtcolor() */STATUS = imcvtcolor(src_img, dst_img, src_format, dst_format);/* 释放内存(正确和错误均执行) */if (src_handle)releasebuffer_handle(src_handle);if (dst_handle)releasebuffer_handle(dst_handle);if (IM_STATUS_SUCCESS != STATUS) {fprintf(stderr, "rga resize error! %s", imStrError(STATUS));return -1;}return 0;
}
这里我放了两个函数,它们实现的是同一个功能,区别在于是否将数据导入 RGA 驱动内部。函数名带有 handle 代表使用 RGA 驱动内部进行数据处理,另一个函数则是直接操作用户提供的图像数据,详细的分析可以看这篇文章:
瑞芯微RKRGA(librga)Buffer API 分析-CSDN博客https://blog.csdn.net/plmm__/article/details/146571080?spm=1001.2014.3001.5501 在 YOLO 这样需要高频率取出图像并进行格式转换的场景,我自然选择直接操作原始的图像数据,我在实践后也确实如此,相比之下每帧的处理速度可以提高 2ms 左右(尺寸1024*720)。
4、适配模型输入
另一个使用到 RGA 库的就是模型的推理部分,由于输入 YOLO 模型的图像色彩空间需要 RGB888,而调用 OpenCV 的 imshow() 函数则需要 cv::Mat 的默认格式 BGR888,除非不显示图像检测结果(那就失去了目标检测的灵魂)。
原作者将图像的前处理和后处理都放在了推理函数中,目的就是可以将他们独立到各个线程中,提高多线程执行效率。由于后处理部分主要使用 BGR888 格式,因此推理函数中就需要同时存在这两种格式。本来想在解码时就直接转为 RGB888,但是后处理调试比较费时间,并且考虑到可以随时切换为 OpenCV 进行软件转换,提高代码的复用性,于是在推理线程中保留了格式转换的 RGA 接口:
// YOLO 推理需要 RGB 格式,后处理需要 BGR 格式// 即使前处理时提前转换为 RGB,后处理部分任然需要转换为 BGR,需要在本函数中保留两种格式if (this->config.accels_2d == ACCELS_2D::ACC_OPENCV) {cv::cvtColor(orig_img, rgb_img, cv::COLOR_BGR2RGB);}else if (this->config.accels_2d == ACCELS_2D::ACC_RGA) {if (RGA_bgr_to_rgb(orig_img, rgb_img) != 0) {cout << "RGA_bgr_to_rgb error" << endl;return cv::Mat();}}else {cout << "Unsupported 2D acceleration" << endl;return cv::Mat();}
如果输入尺寸不匹配,还会进行 resize 操作:
// 图像缩放if (orig_img.cols != width || orig_img.rows != height){// 如果需要缩放,再对 resized_img 申请大小,节约内存开销resized_img.create(height, width, CV_8UC3);if (this->config.accels_2d == ACCELS_2D::ACC_OPENCV){// 打包模型输入尺寸cv::Size target_size(width, height);float min_scale = std::min(scale_w, scale_h);scale_w = min_scale;scale_h = min_scale;letterbox(rgb_img, resized_img, pads, min_scale, target_size, this->config.opencl);}else if (this->config.accels_2d == ACCELS_2D::ACC_RGA){ret = RGA_resize(rgb_img, resized_img);if (ret != 0) {cout << "resize_rga error" << endl;}}else {cout << "Unsupported 2D acceleration" << endl;return cv::Mat();}inputs[0].buf = resized_img.data;}else{inputs[0].buf = rgb_img.data;}
这部分与原作者保持一致,都是来自瑞芯微官方的代码。
四、对比分析
以上大致梳理了方案 4 的整个调用过程,涉及一个硬件视频解码和三个 RGA 加速库的调用。因为单独使用硬件视频解码和 RGA 加速库都可以提高帧率,但是两个一起使用则反而降低了速度,所以问题应该是出在同时开启硬件视频解码和 RGA 加速库才会执行的代码,也就是 RGA_yuv420sp_to_bgr 这个函数,用于将 NV12 的格式转为 BGR888。
我在 NV12_to_BGR 函数中,将 RGA_yuv420sp_to_bgr 替换为 OpenCV 软件转换,其余正常保持硬件加速,发现帧率变得不稳定,从 100 到 160 之间来回跳变,上限确实是突破了目前最高的 150 帧:
不知道是不是 OpenCV 转换后的图像与 RGA 库转换的结果有数据对齐方面的问题,会导致图像数据错误,进而导致丢帧,因为我的 main 函数的逻辑有下面这条:
// 如果取出的图像为空,则跳过
if (img.empty())continue;
我不确定是否有直接关系,但是如果执行过这条语句,从我计算 FPS 的原理来讲,确实会导致帧率下降,目前也在一直查找问题。
目前可以确定的是,两种硬件加速的瓶颈就是 RGA 库的 RGA_yuv420sp_to_bgr 转换函数导致的,我准备继续研究 RGA 接口,优化代码。
代码会公布至 Github,如果有小伙伴感兴趣,欢迎一起讨论。
相关文章:
在 RK3588 多线程推理 YOLO 时,同时开启硬件解码和 RGA 加速的性能分析
一、前言 本文是基于RK3588的YOLO多线程推理多级硬件加速引擎框架设计项目的延申与拓展,单独分析所提出的方案4的性能和加速原理,即同时开启 RKmpp 硬件视频解码和 RGA 硬件图像缩放、旋转。 二、实验结果回顾 在项目的总览篇中,给出了该方案…...
sqli-labs靶场 less 8
文章目录 sqli-labs靶场less 8 布尔盲注 sqli-labs靶场 每道题都从以下模板讲解,并且每个步骤都有图片,清晰明了,便于复盘。 sql注入的基本步骤 注入点注入类型 字符型:判断闭合方式 (‘、"、’、“”…...
3.2 列表的常见函数
1.列表的常用函数 -如何查看一个不认识的内容 -help() -查看官方帮助文档 -dir() -查看内部函数 -随机数语句 import randomranom.randint(0,101) 随机生成0-100内的数字 list1 [1,2,3,4,5]list2 list([1,2,3,4,5,])list2 list("wang…...
Apache Doris 高频问题排查指南:从报错到性能优化
一、部署与配置问题 1. FE启动失败:Address already in use ERROR: fe.journal.Catalog constructor exception. port9010 is already used. 原因:端口被占用或残留进程未释放 解决: # 查找占用进程 lsof -i :9010 # 终止残留进程 kill…...
Hadoop/Spark 生态
Hadoop/Spark 生态是大数据处理的核心技术体系,专为解决海量数据的存储、计算和分析问题而设计。以下从底层原理到核心组件详细讲解,帮助你快速建立知识框架! 一、为什么需要 Hadoop/Spark? 传统单机瓶颈: 数据量超…...
51单片机的五类指令(二)——算术运算类指令
目录 一、加法指令 (一)不带进位加法指令(ADD) (二)带进位加法指令(ADDC) (三)加 1 指令(INC) (四)十进制…...
uniapp选择文件使用formData格式提交数据
1. Vue实现 在vue项目中,我们有个文件,和一些其他字段数据需要提交的时候,我们都是使用axios 设置请求头中的Content-Type: multipart/form-data,然后new FormData的方式来进行提交。方式如下: const sendRequest = () => {const formData = new FormData()formData…...
mac Python多版本第三方库的安装路径
终端查看python版本是 3.12,但是pycharm使用的python版本是 3.9 终端正常安装包以后,pycharm都可以正常使用,但是将 pycharm的python换成 3.12 版本,之前安装的库都没有了 通过终端查看安装库的位置,确实是安装到py…...
第 26 场 蓝桥月赛 部分题解
第 26 场 蓝桥月赛 2.灯笼猜谜3.元宵分配4.摆放汤圆5.元宵交友(运行超时 通过90%) 2.灯笼猜谜 分析:以当前位置为视角,要想移动的距离尽可能的少,按顺序猜谜语,给你一个区间,有三种情况…...
【Vue3知识】Vue3集成富文本编辑器TinyMCE
Vue3集成富文本编辑器TinyMce 集成一、安装依赖二、基础集成示例1. 组件封装:RichEditor.vue 三、关键配置说明1. **API Key 配置**2. **图片上传处理**3. **多语言支持** 四、完整本地化部署步骤(无 API Key)五、在父组件中使用六、常见问题…...
pod生命周期
1.init容器:做主容器运行前需要做的准备条件 2.探针 通俗易懂就是检测容器是否正常运行工作 启动探针startupProbe:检测应用是否完成启动 ,如果启动则禁用其他探测 直到成功为止,探测失败则杀死容器,容器服从重启策略…...
Oracle数据库数据编程SQL<3.3 PL/SQL 游标>
游标(Cursor)是Oracle数据库中用于处理查询结果集的重要机制,它允许开发者逐行处理SQL语句返回的数据。 目录 一、游标基本概念 1. 游标定义 2. 游标分类 二、静态游标 (一)显式游标 【一】不带参数,普通的显示游标 1. 显式…...
OLLAMA 未授权访问-漏洞挖掘
1.漏洞描述 Ollama存在未授权访问漏洞。由于Ollama默认未设置身份验证和访问控制功能,未经授权的攻击者可在远程条件下调用Ollama服务接口,执行包括但不限于敏感模型资产窃取、虚假信息投喂、模型计算资源滥用和拒绝服务、系统配置篡改和扩大利用等恶意…...
多线程—线程安全集合类与死锁
上篇文章: 多线程—JUChttps://blog.csdn.net/sniper_fandc/article/details/146713322?fromshareblogdetail&sharetypeblogdetail&sharerId146713322&sharereferPC&sharesourcesniper_fandc&sharefromfrom_link 目录 1 线程安全的集合类 …...
【鸿蒙5.0】鸿蒙登录界面 web嵌入(隐私页面加载)
在鸿蒙应用中嵌入 Web 页面并加载隐私页面,可借助 WebView 组件来实现。以下是一个完整示例,展示如何在鸿蒙 ArkTS 里嵌入 Web 页面并加载隐私政策页面。 在 HarmonyOS 应用开发中,如果你希望嵌入一个网页,并且特别关注隐私页面加…...
C++轻量HeaderOnly的JSON库
文章目录 1 nlohmann/json库说明2 nlohmann/json特点3 nlohmann/json库的使用方法3.1 引入头文件3.2 解析JSON字符串3.3 访问JSON数据3.4 生成JSON对象3.5 修改JSON数据3.6 将JSON写入文件3.7 遍历JSON对象 4 代码示例4.1 定义JSON数值类型4.2 从STL容器转换到json4.3 string序…...
打包python文件生成exe
下载PyInstaller 官网 pip install pyinstaller验证是否安装成功 pyinstaller --version打包 pyinstaller "C:\Documents and Settings\project\myscript.py"会生成.spec,build,dist三项,其中build,dist为文件夹,dist是最后的可执行文件&a…...
Nginx — Nginx安装证书模块(配置HTTPS和TCPS)
一、安装和编译证书模块 [rootmaster nginx]# wget https://nginx.org/download/nginx-1.25.3.tar.gz [rootmaster nginx]# tar -zxvf nginx-1.25.3.tar.gz [rootmaster nginx]# cd nginx-1.25.3 [rootmaster nginx]# ./configure --prefix/usr/local/nginx --with-http_stub_…...
《Mycat核心技术》第21章:高可用负载均衡集群的实现(HAProxy + Keepalived + Mycat)
作者:冰河 星球:http://m6z.cn/6aeFbs 博客:https://binghe.gitcode.host 文章汇总:https://binghe.gitcode.host/md/all/all.html 星球项目地址:https://binghe.gitcode.host/md/zsxq/introduce.html 沉淀,…...
Dynamic WallPaper-壁纸动态-Mac电脑-4K超高清
Dynamic WallPaper-壁纸动态-Mac电脑-4K超高清 文章目录 Dynamic WallPaper-壁纸动态-Mac电脑-4K超高清一、介绍二、效果三、下载 一、介绍 Dynamic Wallpaper for mac版,是一款4K超高清动态壁纸软件,告别单调的静态壁纸,拥抱活泼的动态壁纸…...
MySQL8.4 NDB Cluster 集群配置安装
文章目录 前置条件安装步骤环境准备下载 安装 RPM 包安装 NDB 组件与常见错误配置节点启用节点配置启动 MySQL 集群验证集群状态 关于 ndb_mgm集群管理备份与恢复集群配置管理日志相关 MySQL NDB Cluster 是一个分布式数据库解决方案,提供高可用性、数据分片和自动故…...
多线程开发中List的使用
由于ArrayList在多线程高并发情况下是不安全的,因此要慎用,那么此时如果涉及到集合操作,应该怎么选: 方案一:Vector: 特点:通过给所有方法都用 synchronized 修饰从而保证线程安全, 缺点&…...
Html 页面图标的展示列表
Html 页面中经常需要使用网页图标,这些图标的样式和名称都不容易记住。常用的网页图标展示页面链接记录如下: Material Design Icons 图标库 - FontAwesome 字体图标中文Icon...
Vue实现动态路由的后端控制
在传统开发后台管理系统时,都会涉及权限控制这一功能需求 即:根据不同登录的角色账号来使用该账号拥有的功能,也就是说系统左边的菜单栏不是固定不变的。 首先是基础路由配置带有component的。 const allRoutes [// 基础路由{path: /,name…...
AI训练中的专有名词大白话版
AI训练中的专有名词大白话版 1. 数据集(Dataset) 👉 人话:AI的“练习题题库”,包含一堆带答案的题目(比如猫狗照片标签)。 🔹 例子: 训练集(练习…...
kafka 与 RocketMQ对比
问题 1: 为什么使用消息队列?服务搭建KafkaRocketMQ编写 docker-compose.yml运行docker compose修改配置文件(解决网络问题)创建一个 topic运行skd尝试发送与接收功能 压力测试:Kafkabatch-size(批量大小)分区数:发送数据(MB/s)消费-线程数(15 分区) RocketMQ生产生产者数量批…...
instnatid模型加载器放在哪里
一般根据节点名称来放,如果没有就新建 ComfyUI\models\instantid...
Spring Boot自动配置原理解析
文章目录 前言一、SpringBootConfiguration二、EnableAutoConfiguration2.1、AutoConfigurationPackage2.2、Import(AutoConfigurationImportSelector.class) 三、ComponentScan四、自动配置源码4.1、获取所有候选的自动配置类4.2、过滤不满足条件的自动配置 总结 前言 在常规的…...
LlamaIndex实现(基于PDF|CSV文件)RAG检索增强生成:NaiveRAG
什么是 RAG? RAG(Retrieval-Augmented Generation,检索增强生成) 是一种结合 信息检索(Retrieval) 和 文本生成(Generation) 的AI技术,用于提升大语言模型(L…...
分布式系统面试总结:3、分布式锁(和本地锁的区别、特点、常见实现方案)
仅供自学回顾使用,请支持javaGuide原版书籍。 本篇文章涉及到的分布式锁,在本人其他文章中也有涉及。 《JUC:三、两阶段终止模式、死锁的jconsole检测、乐观锁(版本号机制CAS实现)悲观锁》:https://blog.…...
vue3搭建实战项目笔记三
vue3搭建实战项目笔记三 3.1.行高偏移问题3.2.谷歌浏览器上不能定位3.2.2 移动端css隐藏滚动条 3.3.获取列表的数据3.3.1 服务器返回十万条数据3.3.2 分页展示数据3.3.2 防止展示数据为空报错 3.4.上拉加载数据3.4.1 加载更多数据3.4.2 监听页面滚动到底部3.4.3 监听滚动的时机…...
【商城实战(101)】电商未来已来:新技术引领商城发展新航向
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配,乃至运营推广策略,102 章内容层层递进。无论是想…...
深入解析最大公约数(GCD)与最小公倍数(LCM)的C++实现
深入解析最大公约数(GCD)与最小公倍数(LCM)的C实现 一、GCD与LCM的数学定义 1. 最大公约数(GCD) 两个或多个整数共有约数中最大的一个。 例如: GCD(12, 18) 6GCD(21, 14) 7 2. 最小公倍数…...
低功耗LPWAN模块开发指南:远距离无线通信与边缘计算融合实战
在远程资产追踪、野外环境监测等场景中,稳定可靠的长距离通信与超低功耗是系统设计的核心挑战。eFish-SBC-RK3576通过 原生双UART接口 USB OTG扩展能力 ,可无缝集成主流LPWAN模组(LoRa/NB-IoT),实现“数据采集-边…...
【质量管理】纠正、纠正措施和预防的区别与解决问题的四重境界
“质量的定义就是符合要求”,我们在文章【质量管理】人们对于质量的五个错误观念-CSDN博客中提到过,这也是质量大师克劳士比所说的。“质量的系统就是预防”,防止出现产品不良而造成的质量损失。 质量问题的解决可以从微观和宏观两个方面来考…...
STM32F103_LL库+寄存器学习笔记12 - 提高串口通讯程序的健壮性:异常监控 + 超时保护机制
导言 首先,进行USART和DMA状态监测、记录异常状态并主动处理,是高健壮性嵌入式系统开发的核心思想之一。 这种机制看似复杂,实则能有效保障系统长期、稳定地运行: 提升通讯可靠性。降低维护成本。增强系统自恢复能力。 因此&…...
搜索-BFS
马上蓝桥杯了,最近刷了广搜,感觉挺有意思的,广搜题类型都差不多,模板也一样,大家写的时候可以直接套模板 这里给大家讲一个比较经典的广搜题-迷宫 题目问问能否走到 (n,m) 位置,假设最后一个点是我们的&…...
Keil调试(RTT Debug 断点)
调试 打印操作 方式接口优缺点串口打印TXRX简单,但是占用串口,速度慢,重定向fputc简单RTT打印SWDIOSWCLK速度快,不占额外接口,直接移植RTT库断点打印SWDIOSWCLKDebug的时候断点操作SWOSWDIOSWCLKSWO需要连接SWO引脚,重定向fputc简单 这里我只介绍RTT打印和断点打印; 一. RT…...
【jQuery】插件
目录 一、 jQuery插件 1. 瀑布流插件: jQuery 之家 http://www.htmleaf.com/ 2. 图片懒加载: jQuery 插件库 http://www.jq22.com/ 3. 全屏滚动 总结不易~ 本章节对我有很大收获,希望对你也是~~~ 一、 jQuery插件 jQuery 功能…...
leetcode 28 Find the Index of the First Occurrence in a String
直接用kmp算法 class Solution { public:int strStr(string haystack, string needle) {return kmp(haystack,needle);}int kmp(std::string &text,std::string &pattern){int n text.size();int m pattern.size();if(m 0)return 0;std::vector<int> next;ne…...
nginx 动静分离
一.动静分离 1.动静分离的好处 Apache Tocmat 严格来说是一款java EE服务器,主要是用来处理 servlet请求。处理css、js、图片这些静态文件的IO性能不够好,因此,将静态文件交给nginx处理,可以提高系统的访问速度,减少…...
1.2 斐波那契数列模型:LeetCode 面试题 08.01. 三步问题
动态规划解三步问题:LeetCode 面试题 08.01. 三步问题 1. 题目链接 LeetCode 面试题 08.01. 三步问题 题目要求:小孩上楼梯,每次可以走1、2或3步,计算到达第 n 阶台阶的不同方式数,结果需对 1e9 7 取模。 2. 题目描述…...
关于AutoMapper
AutoMapper 概述 AutoMapper 是一个基于约定的对象 - 对象映射库,主要用于在不同对象类型之间自动映射属性值。它能根据配置的映射规则,将源对象的属性值填充到目标对象中,避免了手动编写大量繁琐的对象映射代码。 作用 提升开发效率&…...
是否每一层之间都要线性变换和激活函数?
1. 神经网络层的基本组成 一个典型的神经网络层通常包含两个步骤: 线性变换(加权求和): z Wx} b 其中W 是权重矩阵,b是偏置向量,是输入,z 是线性输出。激活函数: 其中,…...
golang 的reflect包的常用方法
目录 reflect 包方法总结 类型 (Type) 方法 值 (Value) 方法 代码示例: reflect 包方法总结 p : Person{Name: "小明", Age: 22}t : reflect.TypeOf(&p)v : reflect.ValueOf(p) 类型 (Type) 方法 方法名描述示例 Na…...
CentOS 7 安装 EMQX (MQTT)
CentOS 7 安装 EMQX 通过 Yum 源安装 EMQX 支持通过 Yum 源安装,您可通过以下 Yum 命令从中自动下载和安装 EMQX。 通过以下命令配置 EMQX Yum 源: curl -s https://assets.emqx.com/scripts/install-emqx-rpm.sh | sudo bash安装以下依赖项ÿ…...
Flask项目部署:Flask + uWSGI + Nginx
目录 1,网络架构 2,环境安装 2.1,安装yum:Shell软件包管理器 2.2 安装python 2.3 安装uWSGI 2.4 安装Flask 3,上传工程包到服务器,打包Flask项目 4,创建和配置 uwsgi 配置文件 uwsgi.ini 4.1配置文件 4.2配置文件注释详解 5,启动服务 6,安装nginx 7,nginx配置 8,…...
软件工程面试题(十五)
1、servlet 创建过程以及ruquest,response,session的生命周期? Servlet的创建过程: 第一步 public class AAA extends HttpServlet{ 实现对应的doxxx方法 } 第二步: 在web.xml中配置 <servlet> <servlet-name></servlet-name> <servlet-c…...
当Kafka化身抽水马桶:论组件并发提升与系统可用性的量子纠缠关系
《当Kafka化身抽水马桶:论组件并发提升与系统可用性的量子纠缠关系》 引言:一场OOM引发的血案 某个月黑风高的夜晚,监控系统突然发出刺耳的警报——我们的数据发现流水线集体扑街。事后复盘发现:Kafka集群、Gateway、Discovery服…...
python和Java的区别
Python和Java是两种流行的编程语言,它们之间有一些重要的区别: 语法:Python是一种动态类型的脚本语言,语法简洁明了,通常使用缩进来表示代码块。Java是一种静态类型的编程语言,语法更为严格,需要…...