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

基于准静态自适应环型缓存器(QSARC)的taskBus万兆吞吐实现

文章目录

    • 概要
    • 整体架构流程
    • 技术名词解释
    • 技术细节
      • 1. 数据结构
      • 2. 自适应计算队列大小
      • 3. 生产者拼接缓存
      • 4. 高效地通知消费者
    • 小结
      • 1. 性能表现情况
      • 2. 主要改进和局限
      • 3. 源码和发行版

概要

准静态自适应环形缓存器(Quasi-Static Adaptive Ring Cache)是taskBus用于数据吞吐的软件结构。

  • 准静态:缓存器的大小并不是静态分配,而是随着吞吐需求的提高,缓慢增长,并最终适应最高峰时的内存消耗。当缓存器已经达峰后,不再有堆内存分配。
  • 自适应:根据包大小的不同,缓存器在恒定的最大峰值内存容积下,根据统计获得包的大小数据,决定环状缓存队列元素的个数、每个元素的大小。
  • 环形: 队列收尾相接形成环状,生产者、消费者使用两个时钟前后相随,时钟本身采用atomic保护,无需额外的锁。

使用该缓存器,基于增强管道数据流转技术(EPDR)的业余软线无线电平台taskBus可在Linux 系统 i7 6700K 主频 4GHz下达到3GBps(24Gbps)的总交换能力。该交换能力被各个通道均分,共同支撑taskBus平台按照工程的连接关系,把各个生产者产出的数据包及时、完整、有序地输送给消费者。尽管采用了本技术,但与采用内核层面的zero-copy函数进行管道直接交换的性能极限还差了20倍。

整体架构流程

taskBus的整体架构是一种多进程的合作机制,平台管理N个子进程,各个进程可以充当消费者和生产者的角色。平台收集各个子进程的stdout输出,并按照用户给定的消费关系,送入消费者的stdout.在这样的整体架构下,数据流转流程如下:

stdout
stdin
stdin
stdin
stdout
生产者1
平台 QSARC
消费者1
消费者2
消费者2
生产者2

在202409版本之前,taskBus使用的是消费者队列。由1个生产者产出的数据,会复制N份进入各个消费者的队列。这种情况下,平台既要为生产者部位保留一个用于包拼接的缓存,又要做N次memcpy。在消费者显著多于生产者时,吞吐能力下降的很厉害。

202409版本之后,设计成每份包只有唯一的1个副本,存储在生产者队列。消费者根据索引去拉取:

  • 平台为每个生产者维护1个环状缓存队列。
  • 平台按照消费关系,把每个包的队列位置索引播发给各个消费者。
  • 播发时,会维护一个待消费计数器,将值加1
  • 消费者消费包后,会将计数器的值减1
  • 如果生产者队列绕了一圈,发现下一个位置的计数器不为0,说明消费的速度赶不上生产速度,此时平台不再接受生产者的数据,等待。

整个流程如下图所示(动图):

Queue
注意的是,上图中队列的大小是4个包,即使包消费完毕,可用的容积也不会释放,这样,随着程序的运行,渐渐地动态内存分配会越来越少,速度会越来越快。当然,如果包长是固定的,则不存在此问题,可以提前全部分配。

值得注意的是,在时钟11时,因为下一个位置依旧有消费者在驻留,因此生产被暂停等待。这种架构的缺点是整体的吞吐能力存在“木桶效应”,即由消费最慢的消费者决定1秒内能够流转多少包。

技术名词解释

  • 包:用于一次功能操作的数据单位,可以理解为1段连续内存的数据。包由包头、数据段组成。包头含有长度指示,正常的数据包之间是紧密衔接的。
  • 时钟:用于控制生产、消费的整形变量,从0开始无限增长。每处理1个包,时钟会加1。
  • 当前位置:生产者、消费者操作队列的当前位置,数值= 时钟 % 队列大小。

技术细节

1. 数据结构

生产者为taskNode类型,拥有如下成员变量维护自己的生产队列:

QVector<int> m_status_stdin;
QVector<QByteArray> m_array_stdin;
QVector<qsizetype> m_size_stdin;
QVector<QAtomicInteger<qsizetype> > m_cnt_stdin;

变量1 控制生产的状态。管道到来的数据,是一个无限有序无损流,类似TCP。但是,每次流可能会被切断,导致包头、数据块可能被切割到多次调用里。这个变量用于在各次调用中记忆上次的生产状态。

变量2 就是缓存的内存本身。这个列表的QByteArray元素个数是缓存的包个数,每个QByteArray会保持在历史最大包的高位。这样,只有到来的包大于当前体积时,才会重新分配内存。

变量3 存储缓存内当前的写入位置。如果包已经写完了,则等于包长。如果是空闲,则为0。

变量4 就是待消费计数器,每次生产完毕1个包,会根据消费者等待队列广播包的索引(时钟),并把计数设置为消费者个数。消费者消费完,会减一。

2. 自适应计算队列大小

这种队列存在1个重大的问题,就是很难控制内存的用量。由于各个QByteArray都只增加不释放,因此期望的用量是:

C = M ∗ N C = M * N C=MN

M是最大包大小,N是队列包个数。

由于taskBus设计时,建议包的大小小于64KB,在假设包的种类小于K时,可以预先统计前K个包的最大长度M,从而确定按照最大内存门限,如C=128MB,要设置的N。

N = C / M N = C/M N=C/M

当然,这种算法是极为简陋的。这是建立在taskBus的具体应用场景上的。对包变化幅度很大、无法简单统计的情况,要考虑一定的shink策略,如在每次消费指针归0时,裁剪队列的大小,并释放尾部的内存。

//Adjust buf sizeif (m_pos_stdin==8){qsizetype sza = 0;for (int ia = 0;ia<8;++ia)if (sza < m_size_stdin[ia])sza = m_size_stdin[ia];if (sza < 32)sza = 32;m_bufsize_adjust = taskCell::default_ringcache * 1024 * 1024 / sza;if (m_bufsize_adjust > m_bufsize_stdin)m_bufsize_adjust = m_bufsize_stdin;if (m_bufsize_adjust < 8)m_bufsize_adjust = 8;emit_message(QString("Adjusted buffer ring size : %1 MB / %2 Bytes = %3 frames.").arg(taskCell::default_ringcache).arg(sza).arg(m_bufsize_adjust).toUtf8());}

3. 生产者拼接缓存

在当前生产位置上,平台为生产者拼接一个完整的包。这里用到了状态机。状态机拥有如下状态:

状态名取值意义
头部捕获中0正在捕获头部
数据缓存中1正在根据头部指示的状态,缓存数据
数据缓存完毕2包接收完整,触发消费。
数据缓存完毕3触发消费完毕,待回收。

在每个状态上,都会进行进程管道读操作,不断的从stdout获取数据。直到状态2,会触发消费,并在全部消费通知播发后,进入状态3。当下一次生产者访问状态3的队列成员时,如果没有消费者还在消费这个包,则会把包位置归零,状态归零。否则,会阻塞生产者,直到消费者消费完毕为止。

void taskNode::slot_readyReadStandardOutput()
{LOG_PROFILE("IO","Start Recieving packs.");qsizetype total_sz = m_process->size();int badHeader = 0;while (total_sz){const qsizetype pos = m_pos_stdin % m_bufsize_adjust;QByteArray & curr_array = m_array_stdin[pos];qsizetype & readBufMax = m_size_stdin[pos];QAtomicInteger<qsizetype> & cnt = m_cnt_stdin[pos];int & stat = m_status_stdin[pos];//生产者被阻塞了,因为下一个缓存位置依旧有消费者在消费数据。if (cnt>0)break;//Old dataif (stat==3){readBufMax = 0;stat = 0;}auto * header =	reinterpret_cast<const TASKBUS::subject_package_header *>  (curr_array.data());//Headerif (stat==0){if (total_sz<sizeof(TASKBUS::subject_package_header))break;auto needRead = sizeof(TASKBUS::subject_package_header) - readBufMax ;auto red = m_process->read(curr_array.data()+readBufMax,needRead);readBufMax += red;if (readBufMax == sizeof(TASKBUS::subject_package_header)){if (header->prefix[0]==0x3C && header->prefix[1]==0x5A &&	header->prefix[2]==0x7E && header->prefix[3]==0x69){stat = 1;}else{++badHeader;readBufMax = 0;}}Q_ASSERT(readBufMax <= sizeof(TASKBUS::subject_package_header));}//dataif (stat==1){const qsizetype packAllSize = sizeof(TASKBUS::subject_package_header)+header->data_length;if (curr_array.size()<packAllSize){curr_array.resize(packAllSize);header = reinterpret_cast<const TASKBUS::subject_package_header *>  (curr_array.data());}auto needRead = packAllSize - readBufMax ;auto red = m_process->read(curr_array.data()+readBufMax,needRead);readBufMax += red;if (readBufMax==packAllSize){stat = 2;}Q_ASSERT(readBufMax <= packAllSize);}//Sendif (stat==2){const qsizetype pack_size = sizeof(TASKBUS::subject_package_header)+header->data_length;extern QAtomicInteger<quint64>  g_totalrev;g_totalrev += readBufMax;++m_spackage_sent;m_sbytes_sent += sizeof(TASKBUS::subject_package_header)+header->data_length;if (header->subject_id == TB_SUBJECT_CMD){//Command must endwith \0const char * pCmd = (const char *)header+sizeof(TASKBUS::subject_package_header);QString cmd = QString::fromUtf8(pCmd,header->data_length);QMap<QString, QVariant> map_z = taskCell::string_to_map(cmd);//remember uuidif (map_z.contains("source")){if(m_uuid.size()==0 )m_uuid = map_z["source"].toString();if (map_z.contains("destin"))emit sig_new_command(map_z);}}else if (m_currPrj)m_currPrj->routing_new_package(this,pos);if (m_bDebug)log_package(true,(char *)header,pack_size);stat = 3;++m_pos_stdin;//Adjust buf sizeif (m_pos_stdin==8){qsizetype sza = 0;for (int ia = 0;ia<8;++ia)if (sza < m_size_stdin[ia])sza = m_size_stdin[ia];if (sza < 32)sza = 32;m_bufsize_adjust = taskCell::default_ringcache * 1024 * 1024 / sza;if (m_bufsize_adjust > m_bufsize_stdin)m_bufsize_adjust = m_bufsize_stdin;if (m_bufsize_adjust < 8)m_bufsize_adjust = 8;emit_message(QString("Adjusted buffer ring size : %1 MB / %2 Bytes = %3 frames.").arg(taskCell::default_ringcache).arg(sza).arg(m_bufsize_adjust).toUtf8());}}total_sz = m_process->size();}if (badHeader)emit_message(QByteArray("Error header recieved. ""Header must be 0x3C, 0x5A, 0x7E,"" 0x69. Aborting."));LOG_PROFILE("IO","End Recieving packs.");
}

4. 高效地通知消费者

如果每个包很小,则QEvent触发的密度会很大,开销很大。我们设置一个消费者的索引队列,存储待消费的生产者队列、索引:

	QMutex m_mtx_queue;QList<taskNode *> m_write_queue;QList<qsizetype> m_write_pos;

而后,只在队列为0时,触发Event。

bool taskNode::enqueue_write(taskNode * node, qsizetype pos)
{m_mtx_queue.lock();int z = m_write_queue.size() + m_write_cmd.size();m_write_queue.push_back( node);m_write_pos.push_back(pos);m_mtx_queue.unlock();if (!z){QCoreApplication::postEvent(this,new QEvent(m_nPackEvent));}return  true;
}

同时,在消费时,一次性获取队列,并清空。这样减少锁的碰撞。

void taskNode::flush_write()
{m_mtx_queue.lock();QList<taskNode *> write_queue = m_write_queue;QList<qsizetype> write_pos = m_write_pos;QByteArrayList write_cmd = m_write_cmd;m_write_queue.clear();m_write_pos.clear();m_write_cmd.clear();//qDebug()<<write_queue.size();m_mtx_queue.unlock();while (write_queue.size()){taskNode * node = write_queue.first();qsizetype pos = write_pos.first();write_queue.pop_front();write_pos.pop_front();QByteArray & arr = node->get_stdin_array(pos);m_process->write(arr.constData(),sz);--cnt;}

小结

尽管采用了环形队列,由于在QProcess上还是存在mem-alloc,这使得峰值状态下CPU占用还是很大的。整体速率距离PCI总线和DDR4的能力还相去甚远,即使和bash直接管道连接相比,也有不小的差距。不过,为了构造灵活的管道吞吐能力,允许数据被多对多流转和反馈回环,损失一些性能也差强人意。

1. 性能表现情况

通过上述操作,taskBus的吞吐能力得到了保证,在只使用用户态的内存操作情况下,缓存64MB时,可以获得10Gbps以上的性能。在Linux下,可达 24Gbps,即3GBps1

平台系统峰值吞吐单路流量平均来回延迟
i7-10700U1Linux x643354MBps1340MBps1ms
i7-6700KLinux x642844MBps1050MBps2.2ms
i7-10700U2win10 home x641561MBps400MBps22ms
i7-6700Kwin10 home x641345MBps340MBps40ms
RaspberryPi 4(8GB)Rasbain 64263MBps102MBps6ms

上表是运行双进程点对点双向PING的状态下达到的。多进程下,总速率会稍高。可以发现,同样的硬件配置下:

  • windows下taskBus的吞吐能力要比Linux少1倍
  • windows下taskBus的带宽利用率要低于Linux,总速率/单路速率Linux更优。
  • windows下的延迟更大。

这是非常奇怪的现象,讲起来windows应该更快才对。对于里面的细节原因,只有后面慢慢研究了。

2. 主要改进和局限

与2024年8月版本相比,少了一层生产者–>消费者的memcpy,转而只是传递int类型的索引,在生产者:消费者个数=1:N的情况下,吞吐能力会得到较大提高。这种memcpy次数的降低,对于老旧CPU影响更大,即使在2进程互PING(1:1)的测试中,也能达到 20-30%的提速。

同时要注意到,3GBps已经很接近用户态内存的吞吐极限。若要追求极为苛刻的传输,需要按照文章开始的链接里的vmsplice zero-copy来定制,取得额外10-20倍的性能提升。

3. 源码和发行版

源码和发行版参考

GitCode.net

或者

GitCode.com


  1. i7-10700U是一个笔记本上的低功耗cpu,在最大睿频4GHz下的Manjaro Linux系统上,3GBps持续了5秒。由于温度上升,温度墙导致频率下降到1.8GHz,速度降低1倍。 ↩︎ ↩︎

  2. i7-10700U是一个笔记本上的低功耗cpu,通过在windows-10下去除温度墙,可以在97摄氏度的高温状态下,保持在4GHz,维持1.5GBps的流量。 ↩︎

相关文章:

基于准静态自适应环型缓存器(QSARC)的taskBus万兆吞吐实现

文章目录 概要整体架构流程技术名词解释技术细节1. 数据结构2. 自适应计算队列大小3. 生产者拼接缓存4. 高效地通知消费者 小结1. 性能表现情况2. 主要改进和局限3. 源码和发行版 概要 准静态自适应环形缓存器&#xff08;Quasi-Static Adaptive Ring Cache&#xff09;是task…...

C++笔记---指针常量和常量指针

巧记方法&#xff08;方法来自于网络出处忘记了&#xff09;&#xff1a;const读作常量&#xff0c;*读作指针&#xff0c;按顺序读即可。例如&#xff1a; const int * ptr; //const在前*在后读作常量指针 const * int ptr; //const在前*在后读作常量指针 int * const prt; /…...

Python习题 177:设计银行账户类并实现存取款功能

(编码题)Python 实现一个简单的银行账户类 BankAccount,包含初始化方法、存款、取款、获取余额等功能。 参考答案 分析需求如下。 Python 类 BankAccount,用于模拟银行账户的基本功能。该类应包含以下方法: 初始化方法: 接受两个参数:account_holder(账户持有人的姓…...

IPhone 16:它的 “苹果智能 “包括哪些内容?

IPhone 16 的发布让科技界看到了该公司的人工智能产品 “苹果智能”&#xff08;Apple Intelligence&#xff09;究竟能做些什么。 苹果公司发布了拥有人工智能硬件升级的最新款 iPhone 16&#xff0c;进一步进军人工智能领域。苹果公司首席执行官蒂姆-库克&#xff08;Tim Coo…...

【中国国际航空-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…...

【ArcGIS】栅格计算器原理及案例介绍

ArcGIS&#xff1a;栅格计算器原理及案例介绍 栅格计算器&#xff08;Raster Calculator&#xff09;原理介绍案例案例1&#xff1a;计算栅格数据平均值 参考 栅格计算器&#xff08;Raster Calculator&#xff09;原理介绍 描述&#xff1a;在类似计算器的界面中&#xff0c;…...

LOOKUP函数和VLOOKUP函数知识讲解与案例演示

〇、需求 在 Excel 文档中&#xff0c;根据查找值从查找域和结果域构成的数组中&#xff0c;找到对应的结果值。 一、知识点讲解 LOOKUP函数&#xff08;比较常用&#xff0c;推荐&#xff09;和VLOOKUP函数 两个公式都可以实现上述需求。 1. LOOKUP 函数 1.1 单个查询条件…...

Java技术深度探索:高并发场景下的线程安全与性能优化

Java技术深度探索:高并发场景下的线程安全与性能优化 在当今的软件开发领域,随着互联网应用的日益复杂和用户量的激增,高并发成为了一个不可忽视的技术挑战。Java,作为一门广泛应用于企业级开发的编程语言,其内置的并发支持机制如线程(Thread)、锁(Lock)、并发集合(…...

Vulnhub-RickdiculouslyEasy靶场(9个flag)

flag1 端口9090有一个flag flag2 13337端口 flag3 使用dirb进行扫描网站的80端口&#xff0c;发现一些敏感文件 访问80端口&#xff0c;没有发现有效信息 访问passwords目录 访问FLAG.txt 再返回访问passwords.html文件 查看页面源代码发现一个密码 flag4 之前扫描到了robo…...

Android Studio Menu制作

文章目录 在Activity上新建onCreateOptionsMenu新建menu目录及资源文件新建Menu一级菜单在Activity上加载Menu 在Activity上新建onCreateOptionsMenu Overridepublic boolean onCreateOptionsMenu(Menu menu) {return super.onCreateOptionsMenu(menu);}新建menu目录及资源文件…...

【mybatis】使用模糊查询时报错:Encountered unexpected token: “?“ “?“

报错信息如下&#xff1a; Mapper.xml报错代码&#xff1a; AND HILIST_NAME like %#{hilistName}% 解决方案&#xff1a; 把模糊查询的 sql 语句改为使用 CONCAT 命令拼接, 就不会报错了。 AND HILIST_NAME like CONCAT(%, #{hilistName},%)...

【Linux】文件权限与类型全解:你的文件安全指南

欢迎来到 CILMY23 的博客 &#x1f3c6;本篇主题为&#xff1a;文件权限与类型全解&#xff1a;你的文件安全指南 &#x1f3c6;个人主页&#xff1a;CILMY23-CSDN博客 &#x1f3c6;系列专栏&#xff1a;Python | C | C语言 | 数据结构与算法 | 贪心算法 | Linux | 算法专题…...

解析DNS查询报文,探索DNS工作原理

目录 1. 用 tcpdump工具监听抓包 2. 用 host 工具获取域名对应的IP地址 3. 分析DNS以太网查询数据帧 3.1 linux下查询DNS服务器IP地址 3.2 DNS以太网查询数据帧 &#xff08;1&#xff09;数据链路层 &#xff08;2&#xff09;网络层 &#xff08;3&#xff09;传输层…...

Unity让摄像机跟随物体的方法(不借助父子关系)

在Unity中&#xff0c;不使用子对象的方式让相机跟随物体移动&#xff0c;我们通过编写脚本来实现。下面放一个从工程中摘出来的的C#脚本示例&#xff0c;用于将相机绑定到一个Target对象上并跟随其移动&#xff1a; using UnityEngine; public class FollowCamera : MonoBeh…...

misc音频隐写

一、MP3隐写 &#xff08;1&#xff09;题解&#xff1a;下载附件之后是一个mp3的音频文件&#xff1b;并且题目提示keysyclovergeek;所以直接使用MP3stego对音频文件进行解密&#xff1b;mp3stego工具是音频数据分析与隐写工具 &#xff08;2)mp3stego工具的使用&#xff1a;…...

如何启动网络安全计划:首先要做的事情

目录 数据分类&#xff1a;网络安全的基石 为什么它很重要&#xff1f; 如何对数据进行分类&#xff1f; 风险分析 什么是风险分析&#xff1f; 如何进行风险分析&#xff1f; 业务影响分析 (BIA) BIA 的用途是什么&#xff1f; BIA 是如何进行的&#xff1f; 安全解…...

Java零基础-三维数组详解!

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云/阿里云/华为云/51CTO&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互…...

数据分析-20-时间序列预测之基于PyTorch的LSTM数据准备及模型训练流程

文章目录 1 数据加载2 去除异常值3 数据归一化4 切分窗口5 制作数据集加载器6 定义模型7 训练模型8 模型评估9 参考附录1 数据加载 参考数据集kaggle下载DailyDelhiClimate import pandas as pd import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] = SimHei # 设…...

vue2中使用web worker启动定时器

vue2中使用web worker启动定时器&#xff0c;避免浏览器最小化或切换标签页时定时器不能按设定周期执行【一般是周期小于60s时&#xff0c;大于60s一般可正常执行】 1、添加worker-loader2、修改vue.config.js3、创建timer.worker.js4、创建TimerWorker.js5、使用TimerWorker启…...

【Python 学习】Numpy的基础和应用

目录 1 数组基础1.1 Numpy简介1.2 Numpy数组基础1.3 创建数组1.3.1 使用np.array()函数生成数组1.3.2 利用内置函数产生特定形式的数组1.3.2.1 简单内置函数1.3.2.2 特殊内置函数 1.3.3 生成随机数组 1.4 数组的数据类型1.5 数组的迭代1.6数组的索引和切片1.6.1 一维数组的索引…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务&#xff1a; test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

【 java 虚拟机知识 第一篇 】

目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...

【Veristand】Veristand环境安装教程-Linux RT / Windows

首先声明&#xff0c;此教程是针对Simulink编译模型并导入Veristand中编写的&#xff0c;同时需要注意的是老用户编译可能用的是Veristand Model Framework&#xff0c;那个是历史版本&#xff0c;且NI不会再维护&#xff0c;新版本编译支持为VeriStand Model Generation Suppo…...

命令行关闭Windows防火墙

命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)​方法二:CMD命令…...