从零实现一个高并发内存池 - 1
C++ 高性能内存池解析
在 C++ 开发中,内存管理一直是影响程序性能的关键因素之一。传统的内存分配方式如 malloc
和 free
在高并发场景下往往存在性能瓶颈。为了解决这一问题,很多优秀的内存池方案应运而生,其中 Google 的 tcmalloc(Thread-Caching Malloc)是一个杰出的代表。本文将深入解析 tcmalloc 的核心原理,并探讨如何实现一个高性能的内存池。
其他的malloc相关实现
C/C++中我们要动态申请内存都是通过malloc去申请内存,但是我们要知道,实际我们不是直接去堆获取内存的,
而malloc就是一个内存池。malloc()相当于向操作系统“批发”了一块较大的内存空间,然后“零售”给程序用。当全部“售完”或程序有大量的内存需求时,再根据实际需求向操作系统“进货”。
malloc的实现方式有很多种,一般不同编译器平台用的都是不同的。比如windows的vs系列用的微软自己写的一套,linux gcc用的glibc中的ptmalloc。下面有几篇关于这块的文章,大概可以去简单看看了解一下,关于ptmalloc,学完我们的项目以后,有兴趣大家都可以去看看他的实现细节。
一文了解,Linux内存管理,malloc、free 实现原理
malloc()背后的实现原理 - 内存池
malloc的底层实现 - ptmalloc
windows和Linux下如何直接向堆申请⻚为单位的⼤块内存:
VirtuallAlloc()
brk() 和 mmap()
一、内存池的概念与作用
(一)什么是内存池
内存池是一种池化技术,程序预先从操作系统申请一块足够大的内存,此后,当程序中需要申请内存时,并不直接向操作系统申请,而是从内存池中获取;同理,释放内存时,也并非真正将内存返回给操作系统,而是返回内存池。当程序结束时,才会将之前申请的内存真正释放。
(二)内存池的主要作用(解决的主要问题)
-
提高内存分配效率 :每次向操作系统申请内存都有较大的开销,内存池通过预先申请过量的资源,避免频繁向操作系统申请和释放内存,大大提高了程序运行效率。
向操作系统申请内存就像我们找自己父母(管钱的)要生活费,拿到钱有两种方式,除去池化技术这种方式,剩下的就比如今天早上吃早餐花了5块钱,然后打电话给妈妈,转钱,中午吃午饭,一样,打电话,找妈妈要钱....也就是每一次要花钱都需要找爸爸妈妈,这些钱都是零碎的,频繁的向家里要钱到的,这样效率肯定是非常低的,都消耗在了每一次要前还需要向爸妈沟通,那么用池化技术该如何解决这个问题呢?就是大概我一个月花个1000块钱,那么就在月初直接拿1000块钱,存在自己的钱包里,这样这个月就不需要再向家里要钱了,这样每一次花钱的效率就高了。
-
解决内存碎片问题 :内存碎片分为外碎片和内碎片。外碎片是由于内存分配后剩余的空闲块太小,无法满足后续的内存分配请求;内碎片则是由于内存分配时需要满足对齐要求,导致分配出去的空间中一些内存无法被利用。内存池通过合理的管理策略,可以有效缓解这两种碎片问题。
在进程地址空间中,先申请了 256Byte 的 vector、256Byte 的 map、512Byte 的 mysql、128Byte 的 list 等不同大小的内存块,这些内存块在内存中是连续分配的。当 vector 和 list 对象销毁后,释放了各自占用的 256Byte 和 128Byte 空间,但这两个释放后的空间与之前未被释放的 mysql 占用的 512Byte 空间以及 map 占用的 256Byte 空间交替存在,导致内存中出现了多块不连续的空闲空间,从而产生了内存碎片。
现在需要申请超过 256Byte 的空间,但现有的空闲空间虽然总共有 384Byte(256Byte+128Byte),却因为碎片化而不连续。内存管理系统在分配内存时,通常需要找到一块连续的、足够大的空闲内存区域来满足申请。由于不存在一块连续的超过 256Byte 的空闲空间,所以无法成功申请到所需的内存。
二、开胃菜:定长内存池
作为程序员 (C/C++) 我们知道申请内存使用的是 malloc,malloc 其实就是一个通用的大众货,什么场景下都可以用,但是什么场景下都可以用就意味着什么场景下都不会有很高的性能,下面我们就先来设计一个定长内存池做个开胃菜,当然这个定长内存池在我们后面的高并发内存池中也是有价值的,所以学习它的目的有两层,先熟悉一下简单内存池是如何控制的,第二它会作为我们后面内存池的一个基础组件。
我们实现定长内存池的详细细节会体现在代码中的注释中!!!
#pragma once
#include<iostream>
#include<vector>
#include<ctime>
#include<windows.h>//方便,不使用using namespace std;是因为防止污染
using std::cout;
using std::endl;//实现代码中的穿插:
// 直接去堆上按页申请空间
inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else// linux下brk mmap等
#endifif (ptr == nullptr)throw std::bad_alloc();return ptr;
}//定长内存池//实现定长
//方法1
//template<size_t N>
//class ObjectPool
//{
// //..........
//};//方法2:我们上面的使用template<size_t N>是可以实现定长的,但是为了和后面的代码有更强的连接性,我们使用下面这种
template<class T>
class ObjectPool
{
public:T* New(){T* obj = nullptr;if (_freeList)//不为空{//说明_freeList当下还有正在“休息”的可用内存块,我们优先叫醒他,没必要再去切当前的内存块 --- 效率的提升!//进行对链表的头删void* next = *((void**)_freeList);//还要注意优先级哦,就是要加上括号哈!//当前的_freeList头部存在下一个节点的地址,我们先提出来,因为头删后,我们要保证_freeList位置的正确性obj = (T*)_freeList;_freeList = next;}else{//剩余内存不够一个对象大小时,需重新开辟大块空间if (_remainBytes < sizeof(T)){_remainBytes = 128 * 1024;//_memory = (char*)malloc(_remainBytes);//我们不要直接去malloc可以吗?直接不走malloc,直接去调用系统!可以的!!//Windows的API是使用VirtuallAlloc()//Linux是brk()或mmap()//这样更纯粹一点!_memory = (char*)SystemAlloc(_remainBytes >> 13);//一页是8K,所以右移1024*8==>>2的13次方if (_memory == nullptr){//申请失败,抛异常throw std::bad_alloc();}}obj = (T*)_memory; //向内存池,也就是申请出来的大块内存申请部分空间!//注意:如果T是char呢?或者任何小于指针长度的类型呢?那么指针不就存不下了吗?这是我们应该要注意到的!size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);_memory += objSize; //大块内存被取走一部分了,当然要继续指向可用部分的开始,以便下一次申请的方便_remainBytes -= objSize; //使用了当然是要保证确实是使用了,剩下多少可用的了!}//注意!!!//仅仅将空间开辟出来还是有一点点不足的,像T是一个自定义类型等等,我们是开了空间,并没有初始化//对于一个开辟出空间的,我们可以调用构造函数进行初始化 --- 定位new:显式调用T的构造函数初始化new(obj)T;//后面Delete也是如此!return obj;}void Delete(T* obj){//显式调用析构函数清理对象obj->~T();//并不是释放obj!!!只是将T:如vector的开辟的空间销毁//画图去理解,理解好细节是很有帮助的!// ***************************************************************************************************// ***************************************************************************************************//if (_freeList == nullptr)//{// _freeList = obj;// //使用指针类型的特性来截取前4个比特位,来存放指向下一个节点的指针(32位下就是4位,64位下就是8位)// //*(int*)obj = nullptr;// //不过我们并不是每一台机器都是4位的,我们可以通过sizeof来进行 if - else,但是我们下面还有一个更加巧妙的方式!!!很重要// *(void**)obj = nullptr; //这样不管是32位还是64位下都没有问题:obj被强转成了(void**),然后前面一个*解引用,那么就是sizeof(void*)的大小了!!!//}//else//{// //我们使用头插是效率很高的!不然还需要遍历找尾!// //头插// *(void**)obj = _freeList;// _freeList = obj;//}// ***************************************************************************************************// ***************************************************************************************************//使用到了头插,所以我们也不需要if - else了:*(void**)obj = _freeList;_freeList = obj;}
private:char* _memory = nullptr; //指向大块内存的指针size_t _remainBytes = 0; //大块内存在切分过程中剩余字节数void* _freeList = nullptr; //从起初申请的内存归还回来过程中需要链接管理起来的管理者 - 自由链表的头指针
};
测试效率:
struct TreeNode
{int _val;TreeNode* _left;TreeNode* _right;TreeNode():_val(0), _left(nullptr), _right(nullptr){}
};void TestObjectPool()
{// 申请释放的轮次const size_t Rounds = 5;// 每轮申请释放多少次const size_t N = 100000;std::vector<TreeNode*> v1;v1.reserve(N);size_t begin1 = clock();for (size_t j = 0; j < Rounds; ++j){for (int i = 0; i < N; ++i){v1.push_back(new TreeNode);}for (int i = 0; i < N; ++i){delete v1[i];}v1.clear();}size_t end1 = clock();std::vector<TreeNode*> v2;v2.reserve(N);ObjectPool<TreeNode> TNPool;size_t begin2 = clock();for (size_t j = 0; j < Rounds; ++j){for (int i = 0; i < N; ++i){v2.push_back(TNPool.New());}for (int i = 0; i < N; ++i){TNPool.Delete(v2[i]);}v2.clear();}size_t end2 = clock();cout << "new cost time:" << end1 - begin1 << endl;cout << "object pool cost time:" << end2 - begin2 << endl;
}
这段代码的主要目的是比较使用传统的 new
和 delete
方式与使用对象池方式在申请和释放大量对象时的性能差异。通过多轮次的申请和释放操作,统计两种方式的耗时,从而展示对象池在频繁申请释放内存场景下的性能优势。
我们可以发现:
//Debug下
new cost time:213
object pool cost time:40//Release下
new cost time:39
object pool cost time:2
三、tcmalloc 的核心框架
(一)tcmalloc 简介
tcmalloc 是 Google 开源的一个高性能内存分配器,全称是 Thread-Caching Malloc,即线程缓存的 malloc。它是基于 ptmalloc(glibc 中的内存分配器)改进而来,专门针对多线程高并发场景进行了优化,用于替代系统的内存分配相关函数(malloc
、free
等)。(注意是在多线程环境下,普通环境下tcmalloc未必比malloc free高效)
现代很多的开发环境都是多核多线程,在申请内存的场景下,必然存在激烈的锁竞争问题。malloc
本身其实已经很优秀,那么我们的项目原型 tcmalloc
就是在多线程高并发的场景下更胜一筹。所以这次我们实现的内存池需要考虑以下几方面的问题:
-
性能问题
-
多线程环境下,锁竞争问题
-
内存碎片问题
(二)tcmalloc 的核心组件
tcmalloc 的内存分配框架主要由三个部分构成:thread cache(线程缓存)、central cache(中央缓存)和 page cache(页缓存)。(三层)
-
thread cache (线程缓存):每个线程都有一个独立的 thread cache,用于管理小于 256KB 的内存分配。线程从这里申请内存不需要加锁,因此效率极高(因为每一个线程独享一个thread cache)。当线程需要分配内存时,会先从自己的 thread cache 中获取;若 thread cache 中没有足够的内存,则会从 central cache 中批量获取。
-
central cache (中央缓存):中央缓存是所有线程共享的。线程缓存按需从中央缓存中获取对象。中央缓存在合适的时机回收线程缓存中的对象,避免一个线程占用了太多内存,而其他线程内存紧张,从而达到内存分配在多个线程中更均衡的按需调度的目的。由于中央缓存是共享的,从这里获取内存对象需要加锁。这里使用的是桶锁机制,并且由于线程缓存通常能够满足需求,只有在线程缓存没有内存对象时才会访问中央缓存,因此这里的锁竞争不会很激烈。(只有访问同一个桶的时候,因此锁竞争并没有那么激烈)。(thread cache没有内存了就向下一层去申请内存,这个中央缓存和我们上面实现的定长内存池类似)
-
page cache (页面缓存):页面缓存位于中央缓存之上,存储的内存是以页为单位进行分配和管理的。当中央缓存没有内存对象时,页面缓存会分配一定数量的页,并将其切割成固定大小的小块内存,分配给中央缓存。当一个 span(页面跨度)的所有对象都被回收后,页面缓存会回收中央缓存中符合条件的 span 对象,并且合并相邻的页面,组成更大的页面,从而缓解内存碎片的问题。
更多精彩在下文哦!
相关文章:
从零实现一个高并发内存池 - 1
C 高性能内存池解析 在 C 开发中,内存管理一直是影响程序性能的关键因素之一。传统的内存分配方式如 malloc 和 free 在高并发场景下往往存在性能瓶颈。为了解决这一问题,很多优秀的内存池方案应运而生,其中 Google 的 tcmalloc(T…...
在 STM32 上使用 register 关键字
在 STM32 上使用 register 关键字时,有一些特别需要注意的事项。虽然 register 是 C 语言中的一个标准关键字,它用于提示编译器将变量尽可能存放在寄存器中,以加速访问,但在 STM32 这样的嵌入式平台上,还是需要考虑几个…...
【入门】纸盒的最大体积是多少?
描述 在一张尺寸为 n * n 厘米的正方形硬纸板的四个角上,分别裁剪掉一个 m * m 厘米的小正方形,就可以做成一个无盖纸盒,请问这个无盖纸盒的最大体积是多少? 立方体的体积 v 底面积 * 高) 比如: n 5 &am…...
漏桶算法的实际应用案例:数据库批量写入流量控制
场景描述 假设有一个物联网平台,需要实时接收成千上万台设备上报的数据(如温度、湿度等),并将数据存入数据库。设备可能在某些时刻集中发送数据(例如定时批量上报),直接写入数据库可能导致以下问…...
c++函数参数列表后面的const的作用(常量成员函数)
c函数参数列表后面的const的作用 class Person { public://普通构造函数Person():age(0){}//常量成员函数void read_age() const {age100; //错误!常量成员函数不允许修改成员变量(除非成员变量用mutable修饰)std::cout << "age:…...
Java的While循环写的出票简单程序
import java.util.Scanner;public class Hello {public static void main(String[] args) {Scanner in new Scanner(System.in);int balance 0;while(true){System.out.print("请投币: ");int amount in.nextInt();balance balance amount;if(balance >10 )…...
时间序列基础【学习记录】
文章目录 1. 时间序列中的标签2. 时间序列中的窗口分割器2.1.概述2.2.窗口分割器demo 3. 时间序列的数据加载器3.1.概述3.2.时间序列的dataset3.3.Tensor类型3.4.测试完整流程demo 1. 时间序列中的标签 在目标检测领域的数据集中的图像会有一个标签**(标记一个物体是猫还是狗或…...
Andorid之TabLayout+ViewPager
文章目录 前言一、效果图二、使用步骤1.主xml布局2.activity代码3.MyTaskFragment代码4.MyTaskFragment的xml布局5.Adapter代码6.item布局 总结 前言 TabLayoutViewPager功能需求已经是常见功能了,我就不多解释了,需要的自取。 一、效果图 二、使用步骤…...
光谱相机的光电信号转换
光谱相机的光电信号转换是将分光后的光学信息转化为可处理的数字信号的核心环节,具体分为以下关键步骤: 一、分光后光信号接收与光电转换 分光元件作用 光栅/棱镜/滤光片等分光元件将入射光分解为不同波长单色光,投射至探测器阵列表面…...
MySQL历史版本下载及安装配置教程
1、访问官网下载mysql https://dev.mysql.com/downloads/mysql/ 2、找到历史版本 我这里的版本是最新的mysql8.0.42 下载完成之后,将压缩包进行解压 3、环境变量 在系统变量中找到Path,点击进入编辑,然后依次点击确定退出即可 注意!!&am…...
【ArcGIS】根据shp范围生成系列等距点:范围外等距点+渔网点(Python全代码)
【ArcGIS】根据shp范围生成系列等距点 目标1:生成边界外一定范围、并且等间距分布的点📁 所需数据:操作步骤-ArcGIS代码处理-Python 目标2:生成等距渔网点📁 所需数据:代码处理-Python 参考 目标1ÿ…...
基于FPGA的视频接口之千兆网口(六GigE纯逻辑)
协议简介 相信大家只有对于GigE有所了解的读者,才能找到这篇文章,所谓的GigE协议包含两个方面分别是视频协议(GVSP)和控制协议(GVCP)。 在本文我们重点讲的是基于FPGA纯逻辑实现阉割版的GigE协议,也就是说在很多视频传输过中,只用到了视频流传输,并没有控制方面的要求…...
android 权限配置
在AOSP 14的ROM定制中,<exceptions>和<privapp-permissions>是用于管理特权应用权限的两种不同机制,主要区别在于作用范围、配置方式和权限授予逻辑。以下是具体分析: 1. <privapp-permissions> 标签 作用: 用…...
广告推荐算法入门 day1 --项目选型
文章目录 0 前言1 广告推荐的基本流程2 场景和baseline初步框定2.1召回场景2.2排场景2.3精排场景 3 一个入门小例子感受--淘宝用户购物行为数据可视化分析3.1 数据集介绍3.2 数据分析目标1.时间维度2.产品维度3.行为维度4.用户维度 4 基础项目选型4.1场景 推荐资料后记 0 前言 …...
【Qt】之音视频编程2:QtAV的使用篇
QtAV 基本播放控制功能实现(C & QML) QtAV 提供了完整的播放控制 API,支持 播放、暂停、停止、快进快退、截屏 等功能。以下是具体实现方法: 1. C 控制方式 基本播放控制 #include <QtAV> #include <QtAV/AVPlaye…...
技术视角下的TikTok店铺运营:从0到1的5个关键点
在当今数字化时代,TikTok Shop作为新兴的电商平台,为众多商家带来了新的机遇。从技术运营的角度来看,以下5个关键点是每个TikTok店铺运营者都需要注意的: 1、规则先行,技术助力合规开店 地区选择:技术分析显…...
机器学习 --- 特征工程(一)
机器学习 — 特征工程(一) 文章目录 机器学习 --- 特征工程(一)一,特征工程概念二,特征工程API三,DictVectorizer 字典列表特征提取四,CountVectorizer 文本特征提取4.1 API4.2 英文…...
cocos creator 3.8 下的 2D 改动
在B站找到的系统性cocos视频教程,纯2D开发入门,链接如下: zzehz黑马程序员6天实战游戏开发微信小程序(Cocos2d的升级版 CocosCreator JavaScript)_哔哩哔哩_bilibili黑马程序员6天实战游戏开发微信小程序(Cocos2d的升级版 CocosCreator Ja…...
2025-05-13 表征学习
表征学习 表征学习(Representation Learning),又称特征学习,是机器学习领域中的一类技术,旨在自动的从原始数据中学习处有效的特征表示,使得后续的机器学习任务(分类、聚类、预测)能…...
【WebApi】YiFeiWebApi接口安装说明
YiFeiWebApi接口安装说明 一、 数据库配置文件修改二、 IIS环境配置(建议IIS7.0)三、 安装.NET 8.0 运行时四、 IIS配置站点五、 发布系统六、 测试接口七、测试服务器站点接口八、其他问题查看日志解决九、ApiPost项目文档 一、 数据库配置文件修改 说明: DSCSYSSq…...
亚马逊云科技:开启数字化转型的无限可能
在数字技术蓬勃发展的今天,云计算早已突破单纯技术工具的范畴,成为驱动企业创新、引领行业变革的核心力量。亚马逊云科技凭借前瞻性的战略布局与持续的技术深耕,在全球云计算领域树立起行业标杆,为企业和个人用户提供全方位、高品…...
爬虫请求频率应控制在多少合适?
爬虫请求频率的控制是一个非常重要的问题,它不仅关系到爬虫的效率,还涉及到对目标网站服务器的影响以及避免被封禁的风险。合理的请求频率需要根据多个因素来综合考虑,以下是一些具体的指导原则和建议: 一、目标网站的政策 查看网…...
Rimworld Mod教程 武器Weapon篇 近战章 第二讲:生物可用的近战来源
本讲分析的是在原版(coreall dlc)环境下,一个Pawn可以用的Tools的所有来源。 重点要分析的是RimWorld.Pawn_MeleeVerbs下的方法GetUpdatedAvailableVerbsList,我把它贴在下面: public List<VerbEntry> GetUpdat…...
SAP汽配解决方案:无锡哲讯科技助力企业数字化转型
汽配行业面临的挑战与机遇 随着汽车行业的快速发展,汽配企业面临着激烈的市场竞争、供应链复杂化、成本压力增大等多重挑战。传统的管理模式已难以满足现代汽配企业对高效生产、精准库存、快速响应的需求。在此背景下,数字化转型成为汽配企业的必然选…...
day19-线性表(顺序表)(链表I)
一、补充 安装软件命令: sudo apt-get install (软件名) 安装格式化对齐:sudo apt-get install clang-format内存泄漏检测工具: sudo apt-get install valgrind 编译后,使用命令 valgrind ./a.out 即可看内存是…...
里氏替换原则:Java 面向对象设计的基石法则
一、原则起源与核心定义 20 世纪 80 年代,计算机科学家芭芭拉・里氏(Barbara Liskov)在一篇论文中首次提出了里氏替换原则(Liskov Substitution Principle,LSP),这成为面向对象设计的重要理论基…...
GBK与UTF-8编码问题(2)
1. 问题现象 在python代码中,用open函数打开文本文件并显示文本内容,中文显示乱码,代码如下。 from tkinter import * import tkinter.filedialogroot Tk() # 给窗口的可视化起名字 root.title(Open File Test)# 设定窗口的大小(长 * 宽) r…...
项目三 - 任务6:回文日期判断
本任务通过判断回文日期,深入学习了Java中日期和字符串处理的相关知识。通过输入年、月、日,生成8位日期字符串,利用StringBuffer的reverse()方法反转字符串,比较原字符串与反转后的字符串是否一致,从而判断是否为回文…...
从零开始掌握FreeRTOS(1)移植到STM32
目录 提前准备 源码文件移植 修改 stm32f10x_it.c 修改 FreeRTOS.h 本章思维导图。 提前准备 学习 FreeRTOS 的第一步就是有一份最工程能够跑在 STM32 上。本篇将记录本人从0搭建一个最基础的移植工程Demo。 要完成这份 Demo,首先我们需要预先准备以下东西&…...
esp32硬件支持AT指令
步骤1:下载AT固件 从乐鑫官网或Git鑫GitHub仓库(https://github.com/espressif/esp-at)获取对应ESP32型号的AT固件(如ESP32-AT.bin)。 步骤2:安装烧录工具 使用 esptool.py(命令行工具&#…...
【神经网络与深度学习】局部最小值和全局最小值
引言 在机器学习和优化问题中,目标函数的优化通常是核心任务。优化过程可能会产生局部最小值或全局最小值,而如何区分它们并选择合适的优化策略,将直接影响模型的性能和稳定性。 在深度学习等复杂优化问题中,寻找全局最小值往往…...
部署安装git-2.49.0.tar.xz
实验环境 git主机:8.10 所需软件 git-2.49.0.tar.xz 实验开始 实验目的:安装升级git2.49.0 编译安装 yum remove git -y --卸载旧版git cd /usr/local/src/ wget https://www.kernel.org/pub/software/scm/git/git-2.49.0.tar.xz …...
SpringBoot的单体和分布式的任务架构
在Spring Boot生态中,定时任务框架的选择需根据架构类型(单体或分布式)和功能需求进行权衡。以下从框架特性、适用场景及Spring Boot集成方式等角度,详细梳理主流的定时任务框架及其分类: 一、单体架构下的定时任务框架…...
第四章 部件篇之按钮矩阵部件
第四章 部件篇之按钮矩阵部件 在 LVGL中, 按钮矩阵部件相当于一系列伪按钮的集合,它按一定的序列来排布这些按钮。值得注意的是,这些伪按钮并不是真正的按钮部件(lv_btn) , 它们只是具有按钮外观的图形&…...
二分查找算法的思路
二分查找思路总结 明确目标与单调性特点: 核心目标:寻找满足某种条件的答案(如最小/最大值)。单调性要求:需要证明你的判断函数具有单调性——即如果某个答案 T 可行,那么大于 T 的答案通常也是可行的&…...
Shell脚本与Xshell的使用、知识点、区别及原理
Shell脚本与Xshell的使用、知识点、区别及原理 Shell脚本 基本概念 Shell脚本是一种为Shell编写的脚本程序,通常用于自动化执行一系列命令。它是在Unix/Linux系统下的命令行解释器与用户交互的接口。 主要知识点 脚本结构:以#!/bin/bash开头…...
【PmHub后端篇】PmHub中基于Redis加Lua脚本的计数器算法限流实现
1 限流的重要性 在高并发系统中,保护系统稳定运行的关键技术有缓存、降级和限流。 缓存通过在内存中存储常用数据,减少对数据库的访问,提升系统响应速度,如浏览器缓存、CDN缓存等多种应用层面。降级则是在系统压力过大或部分服务…...
【递归、搜索与回溯】专题一:递归(二)
📝前言说明: 本专栏主要记录本人递归,搜索与回溯算法的学习以及LeetCode刷题记录,按专题划分每题主要记录:(1)本人解法 本人屎山代码;(2)优质解法 优质代码…...
【Linux】操作系统入门:冯诺依曼体系结构
引言:从一次QQ聊天说起 你是否好奇,当你在键盘上敲下一行文字发送给好友时,计算机内部发生了什么?为什么鼠标点击后程序就能瞬间响应?这一切的答案,都藏在计算机的“心脏”——冯诺依曼体系结构中。 一、硬…...
量化感知训练与 PyTorch 的哪些事
大家好呀!今天咱们要来聊聊一个超厉害的技术——量化感知训练(Quantization-Aware Training,简称 QAT) 在神经网络的世界里,我们总是想方设法地让模型变得更准确、更高效,毕竟谁不想自己的模型在边缘设备上…...
【Mac 从 0 到 1 保姆级配置教程 15】- Python 环境一键安装与配置,就是这么的丝滑
文章目录 前言安装 Python 环境VSCode 配置Python 环境NeoVim 配置 Python 环境(选看)1. Python LSP 配置2. 打开 python 语言支持 最后参考资料系列教程 Mac 从 0 到 1 保姆级配置教程目录,点击即可跳转对应文章: 【Mac 从 0 到 …...
前端学习(3)—— CSS实现热搜榜
效果展示 具体的展示效果如下,可以直接在浏览器显示: 页面分为两部分,一部分是 body 标签里的 html 结构,一部分是 style 标签里的CSS代码(页面布局的部分数据直接在代码里显示了) 一,html结…...
大数据——解决Matplotlib 字体不足问题(Linux\mac\windows)
1、将下载好的字体文件放到文件夹中 谷歌官方字体 import matplotlib print(matplotlib.matplotlib_fname())cp NotoSansSC-Regular.ttf /data/home/miniconda3/envs/python3128/lib/python3.12/site-packages/matplotlib/mpl-data/fonts/ttf/cp wqy-zenhei.ttc /data/home/m…...
嵌入式培训之数据结构学习(二)顺序表与单向链表
目录 一、顺序表 (一)顺序表的基本操作 1、创建顺序表 2、销毁顺序表 3、遍历顺序表 4、尾插,在顺序表的最后插入元素 5、判断表是否满 6、判断表是否空 7、按指定位置插入元素 8、查找元素,根据名字 9、根据名字修改指…...
PyInstaller 打包后 Excel 转 CSV 报错解决方案:“excel file format cannot be determined“
一、问题背景 在使用 Python 开发 Excel 转 CSV 工具时,直接运行脚本(python script.py)可以正常工作,但通过 PyInstaller 打包成可执行文件后,出现以下报错: excel file format cannot be determined, you must specify an engine manually 该问题通常发生在使用pandas…...
鸿蒙 PC 发布之后,想在技术上聊聊它的未来可能
最近鸿蒙 PC 刚发布完,但是发布会没公布太多技术细节,基本上一些细节都是通过自媒体渠道获取,首先可以确定的是,鸿蒙 PC 本身肯定是无法「直接」运行 win 原本的应用,但是可以支持手机上「原生鸿蒙」的应用,…...
HarmonyOS 【诗韵悠然】AI古诗词赏析APP开发实战从零到一系列(一、开篇,项目介绍)
诗词,作为中国传统文化的瑰宝,承载着中华民族几千年的思想智慧和审美情趣。然而,在现代社会快节奏的生活压力下,诗词文化却逐渐被忽视,更多的人感到诗词艰涩深奥,难以亲近。与此同时,虽然市场上…...
实物工厂零件画图案例(上)
文章目录 滑台气缸安装板旋转气缸安装板张紧调节块长度调节块双轴气缸安装板步进电机安装板梯形丝杆轴承座 简介:案例点击此处下载,这次的这几个案例并没有很大的难度,练习这几个案例最为重要的一点就是知道:当你拿到一个实物的时…...
js中的同步方法及异步方法
目录 1.代码说明 2.async修饰的方法和非async修饰的方法的区别 3.不使用await的场景 4.总结 1.代码说明 const saveTem () > {// 校验处理const res check()if (!res) {return}addTemplateRef.value.openModal() } 这段代码中,check方法返回的是true和fal…...
C 语言_基础语法全解析_深度细化版
一、C 语言基本结构 1.1 程序组成部分 一个完整的 C 程序由以下部分组成: 预处理指令:以#开头,在编译前处理 #include <stdio.h> // 引入标准库 #define PI 3.14159 // 定义常量全局变量声明:在所有函数外部定义的变量 int globalVar = 10; // 全局变量函数定义…...