避免滥用Qt信号与槽——改进taskBus 平台以吞吐20M IQ采样带宽

news2024/11/20 9:42:36

taskBus 软件无线电平台是一款依靠 stdin-stdout进行数据吞吐的教学平台。在平台创建之初,主要使用 RTL-SDR进行简单的窄带接收应用,并没有考虑采样率超过1.8M的情况。引入 USRP B210/B205mini后,采样率瞬间提高到2M以上,此时,使用信号与槽进行吞吐时,性能问题就暴露无遗了。

前期使用 Qt5 编译的版本,偷懒直接把装有IQ数据的 QByteArray 作为参数,通过信号-槽来周转,在2.5MHz IQ 采样率下,双向PING延迟325ms左右,勉强够用。上周准备把学校工具链全部更新到 Qt 6.5.1,发现平均双向PING延迟高达 2000 毫秒,其中数据吞吐就花去1960毫秒,导致传递QByteArry这种巨无霸时,性能显著下降了。信号与槽并没有被设计成适配 IQ 数据吞吐这种高流量、高频次的调用。这一篇文章对该问题有所提及:

Compared to callbacks, signals and slots are slightly slower because of the increased flexibility they provide, although the difference for real applications is insignificant. In general, emitting a signal that is connected to some slots, is approximately ten times slower than calling the receivers directly, with non-virtual function calls.

那么,如何解决呢?我们先看看正规工业软件的思路。

1. 正规工业软件的处理思路

在使用软件进行SDR处理的领域,比较高性能的软件基本都遵循如下准则:

  1. 全流程静态内存、环形缓冲器
  2. 滤波等操作使用SIMD加速的专业工具库

比如我们通过第一个技术,即可在PC上跑满 USRP B210的56MHz带宽。

BUGMAX
用这种思路改进 taskBus 是未来的方向。但这牵扯到整体架构的改进,并无法在实验课快要结束时,临时补救。

2. 跨线程信号-槽性能问题

现有架构下,使用信号与槽吞吐数据的原因是负责 stdio 的各个线程是独立的,需要根据数据生产、消费的关系,动态的流转数据。这种方式最好的实现是全局环状队列群,每个专题一个队列,消费者去追赶感兴趣的队列进度。但不幸的是,taskBus实现之初并没有考虑性能问题,只想着跑通教学例子就够了。这个吞吐策略的原理如下图:

sig_lsots
上图显示的是1组生产-消费关系。红色的生产者通过第一组信号-槽,把一包数据交给路由线程;路由线程查阅映射表,把数据通过第二组信号-槽,交给两个消费者。

由于信号-槽不是点对点的连接,而是一种广播机制,导致实质上第二组递交是广播给了所有进程,只是由于携带了目的ID,其他的进程管理者收到后不处理罢了。此外,由于对进程的广播携带的目的ID是唯一的,有几个消费者,就广播几组,造成了递交次数爆炸性增长。设进程数为N,本包的消费者数为Mi,则单包数据周转触发的跨线程信号-槽递交次数:

T i = 1 + N M i T_i=1+NM_i Ti=1+NMi

1秒内,总共生产的包数为K,则总的触发次数

T = K + N ∑ i = 0 K M i T=K+N\sum_{i=0}^{K}M_i T=K+Ni=0KMi

在高采样率情况下,如5MHz采样率下,单包2500样点,则整体的吞吐量会奇高,如下图所示:

奇高的吞吐此时,有N=9个进程,粗略估计 K=52000 + 5100 = 10500,有8个生产者只有1个消费者消费,1个生产者有3路消费者消费

T = 10500 + 9 ( ∑ i = 0 2000 3 + ∑ i = 0 8500 1 ) = 141000 T=10500+9(\sum_{i=0}^{2000}{3} + \sum_{i=0}^{8500}{1})=141000 T=10500+9(i=020003+i=085001)=141000

由于采用 QueuedConnection 进行连接,在 takbus 的主控程序里会产生大量排队,延迟很大, 且采样率越大,越来不及吞吐。同时,信号中传输的是 QByteArray:

void sig_new_errmsg(QByteArrayList);

尽管使用了隐式共享,但在判断包是不是自己需要的专题时,会调用QByteArray 的非常量函数,发生深度拷贝,导致动态内存的分配、清理次数显著提高。

3 采用跨线程直接调用取代信号与槽

既然主要的耗时花在了数据吞吐上,我们就把IQ数据吞吐部分拿出来,直接使用函数调用直连。跨线程的直接调用,最容易发生共享冲突。查看代码,发现生产消费映射表存在大量复杂的索引容器:

	/*!
	  * 索引成员变量,会在refresh_idxes调用时生成
	  * Index member variables that is generated when refresh_idxes is called
	  */
private:
	QMap< unsigned int, int> m_idx_instance2vec;
	QMap< taskNode * , int> m_idx_node2vec;
	QMap< unsigned int, QVector<unsigned int> > m_idx_in2instances;
	QMap< unsigned int, QVector<QString> > m_idx_in2names;
	QMap< unsigned int, QVector<unsigned int> > m_idx_out2instances;
	QMap< unsigned int, QVector<QString> > m_idx_out2names;
private:
	QMap< unsigned int, unsigned int> m_hang_in2instance;
	QMap< unsigned int, QString> m_hang_in2name;
	QMap< unsigned int, QString> m_hang_in2fullname;
	QMap< QString, unsigned int> m_hang_fullname2in;
	QMap< unsigned int, unsigned int> m_hang_out2instance;
	QMap< unsigned int, QString> m_hang_out2name;
	QMap< unsigned int, QString> m_hang_out2fullname;
	QMap< QString ,unsigned int> m_hang_fullname2out;
	QMap< unsigned int, unsigned int> m_iface_inside2outside_in;
	QMap< unsigned int, unsigned int> m_iface_inside2outside_out;
	QMap< unsigned int, unsigned int> m_iface_outside2inside_in;
	QMap< unsigned int, unsigned int> m_iface_outside2inside_out;
private:
	std::function<taskCell * (void)> m_fNewCell;
	std::function<void (taskCell * pmod, taskNode * pnod,QPointF pt)> m_fInsAppended;
	std::function<void (void)> m_fIndexRefreshed;
	std::function<QPointF (int)> m_fGetCellPos;

好是头大!不过,仔细分析,发现这些索引是在工程创建时就已经固定,后续不再发生任何修改。如此一来,在各个进程的管理线程中,就能直接调用路由线程的路由函数,并向各个消费者精准推送数据:

直接调用
上图是Qt6的直接连接方式,相当于从生产者线程直接调用各个消费者线程管理的进程对象的QProcess::write,此时分析一下调用量:

对单个包而言,发生调用的次数就是消费者的个数 Mi

因此,整体调用次数:

T = ∑ i = 0 K M i T=\sum_{i=0}^{K}M_i T=i=0KMi

按照上面的例子的取值,为

T = ∑ i = 0 2000 3 + ∑ i = 0 8500 1 = 14500 T=\sum_{i=0}^{2000}{3} + \sum_{i=0}^{8500}{1}=14500 T=i=020003+i=085001=14500

假设信号与槽和函数调用的本身速度差为10倍,则整体性能提升:

$$ A=10*141000/14500=97 倍。

若原本的延迟为2秒,则优化后应在 20 ms左右,加上处理延迟,不会超过80ms。

4. Qt5 与 Qt6的灵活性差异

对于Qt5而言,从线程A中直接调用线程B管理的 QProcess 的write方法时,数据是写不进去的。Qt6显然是考虑到这个问题,允许这种调用。

为了解决Qt5临门一脚的问题,需要在管理者线程中使用一个异步的信号-槽调用,略微增加了延迟。

Qt5

5 测试结果

在1M 16bit IQ 采样率下,主要延迟在缓存与处理,延迟90ms。

1M
在2.5M IQ采样率下,平均延迟65ms左右
2.5M
把采样率提高到5M,平均延迟60ms左右。
5M在20M采样率下,进一步缩小到46ms

20M

6 改进建议

由于没有使用环形静态缓存与SIMD技术,使得对20M信号的软处理已经无法单核完成。对于用于工业应用情景下,挑战56M带宽需要更为严苛的底层优化技术。但通过避免信号与槽的滥用,已经把可用带宽提高了8-10倍。

相关代码参考

taskBus软件仓库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/680720.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot+vue项目中如何利用七牛云实现头像的上传

做了个前后端分离的项目&#xff0c;对于用户的头像修改一直不是很满意&#xff0c; 于是用了Vant4的组件库改成了文件点击上传&#xff0c;先是打算存到本地&#xff0c;了解到七牛云的方便后&#xff08;主要是免费&#xff09;&#xff0c;决定改成七牛云存储图片&#xff…

ElasticSearch的安装和访问

ElasticSearch的安装 前言: 本次下载是在Windows系统进行操作,版本为7.6.1,因为下周最新版本的8.1.2有问题 ElasticSearch基于Java开发,JDK最低1.8版本 ElasticSearch的版本要和之后引入的Maven的Jar包版本对应 1 下载ElasticSearch 官网:https://www.elastic.co/cn/ 产品…

01.2总线驱动设备设计思想

sysfs文件系统 sysfs文件系统是Linux2.6版本引入的虚拟文件系统。sysfs把连接在系统上的设备模型组织_ 成为一个分级的层次视图。并且可以向用户空间导出内核数据结构以及属性。 比如下面的图可以看出来当前支持的总线和相关的数据 在sys文件系统中每一个目录都对应着一个kob…

电商项目10:商品管理、仓库管理

电商项目10&#xff1a;商品管理、仓库管理 1、商品管理1.1、spu检索1.1.1、后端1.1.2、前端 1.2、sku检索1.2.1、后端 2、库存管理2.1、启动ware后端微服务2.2、仓库维护查询2.3、查询商品库存2.4、查询采购需求 1、商品管理 1.1、spu检索 1.1.1、后端 spu检索接口文档 S…

用了这么久IDEA其中中的Modules、Libraries、Facets、Artificats他们的作用到底是什么?

Modules Modules通常是说我们该项目当前拥有的模块。拿微服务来说&#xff0c;通常需要将功能分离出来&#xff0c;形成一个个模块&#xff0c;因此每个功能就需要一个模块Modules&#xff0c;即一个小项目。 里面有Sources、Paths、Dependencies。像Sources我们可以将我们想…

如何安装 kubernetes dashboard 让 k8s 的操作可视化

kubernetes dashboard 是 k8s 自带的 k8s 图形化管理工具。使用这个工具可以帮助用户通过图形化页面配置 k8s 资源&#xff0c;掌握 k8s 系统的整体使用情况&#xff0c;把用户的双手从敲 kubectl 命令中解放出来。 下面我们就介绍安装 kubernetes dashboard 的详细步骤。 下载…

d2l_notes_ch1-ch2

1. 引言 1&#xff09;机器学习 > 表示学习 > 深度学习 表示学习是机器学习子集&#xff0c;其研究重点是如何⾃动找到合适的数据表示方式。深度学习是通过学习多层次的转换来进⾏的多层次的表示学习。 深度学习是机器学习的一个子集&#xff0c;但相比传统的机器学习方…

DevOps系列文章之Docker部署web ssh工具sshwifty

一、介绍 1.sshwifty简介 sshwifty是一款Web SSH & Telnet&#xff08;WebSSH & WebTelnet 客户端工具。 2.shwifty 特点 shwifty 是为 Web 设计的 SSH 和 Telnet 连接器。它可以部署在您的计算机或服务器上&#xff0c;为任何兼容&#xff08;标准&#xff09;的网络…

自定义Spring Boot Starter

Spring Boot starter 我们知道Spring Boot大大简化了项目初始搭建以及开发过程&#xff0c;而这些都是通过Spring Boot提供的starter来完成的。在实际项目中一些基础模块其本质就是starter&#xff0c;所以我们需要对Spring Boot的starter有一个全面深入的了解&#xff0c;这是…

Codesys单轴控制实现多段速控制功能

b一、序言 在设备加工行业&#xff0c;很多工艺要用到多段速控制&#xff0c;比如切割&#xff0c;打孔&#xff0c;攻丝等&#xff0c;刀具未碰到工件时可以快速行走&#xff0c;刀具碰到工件时需要慢速加工&#xff0c;而有些工艺在加工时随着刀具越走越深&#xff0c;其进刀…

掌握Python的X篇_4_开发工具ipython与vscode的安装使用,作业

本篇将会介绍两个工具的安装及使用来提高Python的编程效率。 ipython&#xff1a;比python更好用的交互式开发环境vscode&#xff1a;本身是文本编辑器&#xff0c;通过安装相关的插件vscode可以作为python集中开发环境使用 掌握Python的X篇_4_开发工具ipython与vscode的安装使…

GIT保存记录原理之commit对象

GIT 中提交对象非常的重要&#xff0c;我们通过它记录代码提交过程、进行文件保存、回退等操作&#xff0c;那么它是怎样帮助我们记录这些信息的呢&#xff1f;其实就是都保存在项目根目录的 .git 文件夹中。 新建空项目 gitDemo使用 git init初始化&#xff0c;在文件夹根目录…

57 KVM工具使用指南-制作 LibcarePlus 热补丁

文章目录 57 KVM工具使用指南-制作 LibcarePlus 热补丁57.1 概述57.2 手动制作57.3 通过脚本制作 57 KVM工具使用指南-制作 LibcarePlus 热补丁 57.1 概述 LibcarePlus 支持如下方式制作热补丁&#xff1a; 手动制作通过脚本制作 手动制作热补丁的过程繁琐&#xff0c;对于…

java——反射与注解

文章目录 Java反射基础1. 概念详解2. 示例代码 Java反射进阶1. 框架设计2. 动态代理3. 模板方法 Java注解基础1. 概念2. 基本语法3. 自定义注解4. 反射获取注解信息 Java注解进阶1. 应用场景2. 内置注解3. 第三方注解库4. 总结 Java反射与注解实战1. 实战场景2. 代码实现 Java反…

AutoSAR系列讲解(入门篇)2.3-Ports的类型

Ports的类型 一、接口的类型 二、S/R接口 三、C/S接口 一、接口的类型 Ports是SWC和SWC做接口&#xff08;Interface&#xff09;通信使用&#xff0c;或者SWC通过RTE和BSW做接口&#xff08;Interface&#xff09;通信使用。 Ports主要分为5种类型&#xff0c;列在下面的图…

【UEFI实战】UEFI图形显示(显示驱动)

显示驱动 OVMF BIOS使用了这个作为显卡驱动&#xff0c;具体图形显示的底层实现不是重点&#xff0c;所以这里只是简单介绍。 QemuVideoDxe是一个UEFI Driver Model&#xff0c;对应的EFI_DRIVER_BINDING_PROTOCOL&#xff1a; EFI_DRIVER_BINDING_PROTOCOL gQemuVideoDriv…

【数据库】事务、事务并发问题、并发事务隔离级别、及sql演示

文章目录 一、事务1.1 事务简介 及 sql 操作1.2 事务的特性 二、事务并发问题三、事务隔离级别四、sql 演示4.1 脏读4.2 不可重复读4.3 幻读 五、演示代码 一、事务 1.1 事务简介 及 sql 操作 事务&#xff1a;数据库执行的一系列操作&#xff0c;这些操作要么全部执行&#x…

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南(内存模型技术专题)

深入探索Java特性中并发编程体系的原理和实战开发指南&#xff08; 线程进阶技术专题&#xff09; 前言介绍JVM内存模型运行时数据区域堆内存栈内存 内存访问规则原子性对象类型基本类型 可见性有序性&#xff08;Happen Before法则&#xff09;系统内存&#xff08;MESI协议&a…

【OS】【期末选择题】【2023春】【仅供参考】

文章目录 题型一、选择第一章(10)第二章(19)第三章(23)第四章(32)第五章(15)第六章(15) 二、填空题三、简答题1.信号量2.调度算法3.页面置换4.虚拟地址到物理地址的映射 Reference 题型 题型题量分值选择10%填空25%10%10%解答题210’大题215’ 一、选择 第一章(10) 操作系统…