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

C++ 智能指针底层逻辑揭秘:优化内存管理的核心技术解读

目录

0.为什么需要智能指针?

1.智能指针的使用及原理

RAII:

智能指针的原理:

2.智能指针有哪些?

std::auto_ptr

std::unique_ptr

std::shared_ptr

std::weak_ptr


0.为什么需要智能指针?

        想要回答这个问题,首先要来看一个没有智能指针存在的场景:

int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}
void Func()
{int* p1 = new int;int* p2 = new int;cout << div() << endl;delete p1;delete p2;
}
int main()
{try{Func();}catch (exception& e){cout << e.what() << endl;}return 0;
}

思考:如果p1这里new 抛异常会如何?如果p2这里new 抛异常会如何?如果div调用又会抛异常会如何?毫无疑问,如果new开辟空间时抛出异常亦或是div调用时候抛出异常都会导致p1p2就没有及时释放,造成内存泄露!但如果我们依次在抛出异常前手动释放掉资源又会显得繁琐,这时就需要智能指针

1.智能指针的使用及原理

        先来介绍一下智能指针运用到了什么原理:

RAII:

        RAII(Resource Acquisition Is Initialization)是一种 利用对象生命周期来控制程序资源 (如内存、文件句柄、网络连接、互斥量等等)的简单技术。
         在对象构造时获取资源 ,接着控制对资源的访问使之在对象的生命周期内始终保持有效, 最后在对象析构的时候释放资源 借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:不需要显式地释放资源采用这种方式,对象所需的资源在其生命期内始终保持有效。
// 使用RAII思想设计的SmartPtr类
template<class T>
class SmartPtr {
public:SmartPtr(T* ptr = nullptr): _ptr(ptr){}~SmartPtr(){if(_ptr)delete _ptr;}private:T* _ptr;
};

        上面就是根据RAII思想大致设计出的智能指针,指针内部存储的是对象的地址,这样就相当于把资源的地址交给一个类对象管理,当这个类销毁时调用析构函数就不需要手动释放了!

智能指针的原理:

        上面其实与真正的智能指针的框架大差不差,但是因为还要具备指针的行为,因此需要重载一下operator*以及operator->两个操作符函数:

template<class T>
class SmartPtr {
public:SmartPtr(T* ptr = nullptr): _ptr(ptr){}~SmartPtr(){if(_ptr)delete _ptr;}T& operator*() {return *_ptr;}T* operator->() {return _ptr;}
private:T* _ptr;
};

        这样就有了一个完整智能指针的雏形了~
 

2.智能指针有哪些?

std::auto_ptr

        C++98版本的库中就提供了auto_ptr的智能指针,让我们先来看一下关于auto_ptr的一些介绍:

        但其实auto_ptr是许多程序员不喜欢使用的智能指针,因此存在指针悬空的问题:

void test()
{//c++98中提供auto_ptrauto_ptr<A> ap1(new A(1));auto_ptr<A> ap2(new A(2));auto_ptr<A> ap3(ap1);//这里用sp1去构造sp3就会出现指针悬空的问题(ap1->a)++;//sp1被置为空后再去访问就会报错(ap2->a)++;}

        在上面的场景中就出现了指针悬空的问题,这里进行了管理权转移,ap1将管理权交给了ap3,那么ap3构造的时候会将ap1置为nullptr,如果此时再去对ap1进行访问就属于非法的!

下面来简单实现一下auto_ptr,以便我们明了具体结构:

思路:

---1--- 其实大致框架已经在前面给出了,完成了operator->以及operator*函数的重写,这里需要完成的是拷贝构造函数

---2--- 在拷贝构造函数中,需要把地址传给成员变量,将参数的地址置为nullptr

template<class T>
class auto_ptr
{
public:auto_ptr(T* ptr):_ptr(ptr){ }auto_ptr(auto_ptr<T>& ptr):_ptr(ptr._ptr){ptr._ptr = nullptr;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;
};

         如果不想要这种危险的情况发生,可以使用std::unique_ptr:

std::unique_ptr

        顾名思义,unique则代表的是唯一,也就是说此智能指针不能拷贝构造一个新的同类型对象,先让我们看看介绍:

        那么如果你试图用一个unique_ptr对象去拷贝构造一个新对象,这种行为在编译时就会报错:

void test()
{unique_ptr<A> up1(new A(1));unique_ptr<A> up2(new A(2));//unique_ptr直接进行了反拷贝操作,禁止了这种指针拷贝行为unique_ptr<A> up3(up1);
}

 为什么会有删除函数这样的报错,是因为unique_ptr的底层实现了反拷贝:

        我们来模拟实现一下unique_ptr:

思路:

---1--- 大致框架还是相同,不同的是这样要如何实现反拷贝?

---2--- 如果你使用C++98那么反拷贝应该将拷贝构造函数以及赋值函数只声明不实现

---3--- 如果你使用C++11那么只需将这两个函数设置为=delete即可,这也是为什么报错显示函数已经被删除

template<class T>
class unique_ptr
{
public:unique_ptr(T* ptr):_ptr(ptr){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private://unique这里用到了反拷贝,c++98中也可以只声明不实现,将声明放到private中unique_ptr(unique_ptr<T>& ptr) = delete;unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;T* _ptr;
};

         那么如果我就是要实现可以拷贝构造和赋值构造的智能指针呢?登场的是std::shared_ptr:

std::shared_ptr

        顾名思义,shared就是可以共享,那么就代表此类型的智能指针可以实现多个指针指向同一块内存,先来看看介绍:

        那么如果你使用shared_ptr去构造和赋值,都是可以通过编译的:

void test()
{bit::shared_ptr<A> sp1(new A(1));bit::shared_ptr<A> sp2(new A(2));bit::shared_ptr<A> sp3(sp1);//这里支持拷贝,是因为实现了引用计数sp2 = sp3;
}

         话不多说,来看看底层实现:

思路:

---1--- 框架没有变化,但是如果要实现shared_ptr的拷贝构造和赋值构造就要一点难度了:

---2--- 为什么shared_ptr可以支持多个对象指向同一内存?这里运用了引用计数,每个对象销毁时计数-1,直到减为0时,这块内存发生析构释放,这里最适合来进行技术的是int*类型的对象,为什么不是static或者是int,static只会实例化一份,无法实现多个对象的引用计数,int又会导致每个对象的引用计数都是拷贝,无法从根本修改计数

---3--- 默认构造:先new int,初始化为1即可

---4--- 拷贝构造:将指针赋值,将计数+1,并把地址赋值一下

---5--- 赋值构造:不仅仅是计数的增加以及指针的赋值,还要考虑原指针指向的内存是否应该释放

template<class T>
class shared_ptr
{
public:shared_ptr(T* ptr=nullptr):_ptr(ptr), _pcount(new int(1)){}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr){++(*(sp._pcount));_pcount = sp._pcount;}shared_ptr<T>& operator=(const shared_ptr<T>& sp){//在改变被赋值对象的指针之前,先要考虑是否要释放被赋值对象管理的内存资源//也就是说改变指针之前要对原理管理资源的pcount--,如果减到0就需要释放//首先要考虑指向通过一块内存的两个指针相互赋值,如果不同名只是麻烦一点,如果同名那么就会造成释放后再去访问的问题if (sp._ptr == _ptr){return *this;}//对于被赋值的要记得减去引用计数来决定是否释放内存,防止造成内存泄漏if (--(*_pcount) == 0){delete _ptr;delete _pcount;}_ptr = sp._ptr;++*(sp._pcount);_pcount = sp._pcount;return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get()const//返回指针{return _ptr;}size_t use_count()const//返回引用计数的个数{return *_pcount;}~shared_ptr(){if (--(*_pcount) == 0){_del(_ptr);delete _pcount;}}
private:T* _ptr;//shared_ptr可以支持多个指针指向同一块空间,怎么实现?//引用计数,如何实现?使用static int不可行,因为多个对象只能有一个static,//但是每一块资源都需要一个计数int* _pcount;};

        但是,shared_ptr也会产生相应的问题,那就是引用循环:

        引用循环(Reference Cycle),也被称为循环引用,它会引发内存泄漏等问题。

        定义:引用循环指两个或多个对象之间相互持有对方的引用,形成一个闭环引用结构,导致这些对象无法被正常释放,造成内存资源的浪费。例如,对象 A 持有对象 B 的引用,而对象 B 又持有对象 A 的引用,这样就形成了引用循环。

        假设存在两个类AB ,它们的定义如下:

#include <memory>
class B;
class A {
public:std::shared_ptr<B> b_ptr;~A() {std::cout << "A destroyed" << std::endl;}
};
class B {
public:std::shared_ptr<A> a_ptr;~B() {std::cout << "B destroyed" << std::endl;}
};void test() {std::shared_ptr<A> a = std::make_shared<A>();std::shared_ptr<B> b = std::make_shared<B>();a->b_ptr = b;b->a_ptr = a;
}

        在test函数结束时,ab所指向的对象本应被释放,但由于它们相互引用,引用计数都不会降为 0,这里值得细说一下:a_ptr由b管理,当b析构时a_ptr发生析构,但是b_ptr又管理b,当b_ptr发生析构,b才会析构,那么b_ptr由谁管理呢?a,当a发生析构b_ptr才会析构,但是a又被a_ptr管理着,这就构成了死循环,最后引用计数由2->1,会导致内存泄漏。

        如何解决?weak_ptr登场:不过在这之前先要补充定制删除器:如果让智能指针管理开辟的数组,该如何释放呢?shared_ptr提供了这样的接口:

这里的D可以传入函数指针,仿函数亦或是lambda表达式

void test_sp4()
{//如果new了一个数组,又该如何释放呢?//了解定制删除器:new A[10]//template <class U, class D> shared_ptr (U* p, D del);//template <class D> shared_ptr(nullptr_t p, D del);//库中用了模板D来控制,其实是仿函数,同时也可以接收lameda表达式std::shared_ptr<A> sp1(new A[10], [](const A* ptr) { delete[] ptr; });std::shared_ptr<A> sp2((A*)malloc(sizeof(A)*10), Destroy<A>());std::shared_ptr<FILE> sp3(fopen("SmartPtr.hpp", "r"), [](FILE* ptr) {return fclose(ptr); });//ps:这里的A是作者自定义的类
}

 这是怎么做到的呢?其实是用到了U实例化后的包装器来接收传入的函数指针,仿函数亦或是lambda表达式,并使用模板来处理,这样在析构函数的时候使用包装器传入要释放资源的指针即可:(了解一下,理解即可)

template<class T>
class shared_ptr
{
public:template<class D>shared_ptr(T* ptr,D del):_ptr(ptr),_pcount(new int(1)),_del(del){}function<void(T*)> _del;//使用包装器来解决模板参数无法涉及析构函数的问题
};

std::weak_ptr

        weak_ptr可以理解为是专门用于处理循环引用的问题的指针,但请注意并没有采用RAII思想来搭建,所以属于非常特殊的智能指针

可以看到,weak_ptr并不支持传参构造,通常用shared_ptr去构造weak_ptr,那么如果这样使用weak_ptr就可以正常析构:

#include <memory>
class B;
class A {
public:std::weak_ptr<B> b_ptr;//这里改成了weak_ptr~A() {std::cout << "A destroyed" << std::endl;}
};
class B {
public:std::weak_ptr<A> a_ptr;//这里改成了weak_ptr~B() {std::cout << "B destroyed" << std::endl;}
};void test() {std::shared_ptr<A> a = std::make_shared<A>();std::shared_ptr<B> b = std::make_shared<B>();a->b_ptr = b;b->a_ptr = a;
}

原理是什么呢?其实weak_ptr并没有处理shared_ptr中的引用计数部分,但内部存在强弱引用计数,负责了指针的拷贝,并且使用了控制块,这才使得循环引用得以解决,请注意weak_ptr与RAII没有任何关联,可以参与资源的访问,但是不参与资源的释放!!!

模拟实现一下:

思路:

---1--- 简单实现一个简易版,其实很简单,只用实现shared_ptr构造和默认构造即可,直接给出:

template<class T>
class weak_ptr
{
public:weak_ptr()//不支持传参构造:_ptr(nullptr){}weak_ptr(const shared_ptr<T>& sp)//支持sharedptr构造:_ptr(sp._ptr){ }weak_ptr<T>& operator=(const weak_ptr<T>& wp){_ptr = wp._ptr;return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;
};

补充:weak_ptr的底层实现:(了解即可)

控制块(Control Block):强 / 弱计数的载体

当 shared_ptr 首次指向动态对象时,会创建一个控制块(内存中与对象分离),包含:

  • 强引用计数(strong count):记录当前活跃的 shared_ptr 数量(决定对象生命周期)。
  • 弱引用计数(weak count):记录指向该对象的 weak_ptr 数量(决定控制块的生命周期)。
  • 自定义删除器、分配器(若有)。

weak_ptr 的核心实现逻辑:

1. 绑定 shared_ptr 时:

  • weak_ptr 不增加强计数,仅增加弱计数。
  • 控制块通过原子操作(如 std::atomic<int>)保证线程安全。

2. 对象销毁时机(强计数为 0 时)

  • 控制块自身内存被释放(若通过 make_shared 创建,控制块与对象内存合并,优化缓存)。

相关文章:

C++ 智能指针底层逻辑揭秘:优化内存管理的核心技术解读

目录 0.为什么需要智能指针&#xff1f; 1.智能指针的使用及原理 RAII&#xff1a; 智能指针的原理&#xff1a; 2.智能指针有哪些&#xff1f; std::auto_ptr std::unique_ptr std::shared_ptr std::weak_ptr 0.为什么需要智能指针&#xff1f; 想要回答这个问题&…...

Vue接口平台学习七——接口调试页面请求体

一、实现效果图及简单梳理 请求体部分的左边&#xff0c;展示参数&#xff0c;分text和file类型。 右边部分一个el-upload的上传文件按钮&#xff0c;一个table列表展示&#xff0c;一个显示框&#xff0c;用于预览选择的文件&#xff0c;点击可大图展示。 二、页面内容实现 …...

小程序css实现容器内 数据滚动 无缝衔接 点击暂停

<view class"gundongBox"><!-- 滚动展示信息的模块 --><image class"imgWid" :src"imgurlgundong.png" mode"widthFix"></image><view class"gundongView"><view class"container&qu…...

内存条装机,无法启动

1、i5-9600k,主板技嘉z390gamingx&#xff1a; ①、插满4条DDR4-2666&#xff0c;无法启动&#xff1b; ②、两条DDR4-2666&#xff0c;插在2、4或者1、3插槽&#xff0c;可以启动&#xff1b; ③、三条DDR4-2666&#xff0c;一条DDR4-2400&#xff0c;插满4个内存插槽&…...

【力扣】day1

文章目录 27.移除元素26. 删除有序数组的重复项 27.移除元素 26. 删除有序数组的重复项 我们仔细看一下这两道题的最后的返回值,为什么第一题返回slow 而第二题返回slow1 最后的返回值该如何返回绝对不是凭感觉,我们自己分析一下第一个slow,从0位置开始, 遇到val值就开始和fas…...

图像预处理-色彩空间补充,灰度化与二值化

一.图像色彩空间转换 1.1 HSV颜色空间 HSV颜色空间使用色调&#xff08;Hue&#xff09;、饱和度&#xff08;Saturation&#xff09;和亮度&#xff08;Value&#xff09;三个参数来表示颜色 一般对颜色空间的图像进行有效处理都是在HSV空间进行的&#xff0c;然后对于基本…...

linux如何用关键字搜索日志

在 Linux 系统中搜索日志是日常运维的重要工作&#xff0c;以下是几种常用的关键字搜索日志方法&#xff1a; 1. 基础 grep 搜索 bash 复制 # 基本搜索&#xff08;区分大小写&#xff09; grep "keyword" /var/log/syslog# 忽略大小写搜索 grep -i "error&…...

Spring Boot项目中结合MyBatis实现MySQL的自动主从切换

原理解析 1. MySQL主从复制&#xff08;Master-Slave Replication&#xff09; 工作原理&#xff1a;MySQL主从复制通过二进制日志&#xff08;binary log&#xff09;来同步数据。主服务器记录所有更改操作到二进制日志中&#xff0c;从服务器读取这些日志并执行相应的SQL语…...

项目交接时信息遗漏,如何预防

项目交接时&#xff0c;信息遗漏可能导致任务延误、质量下降和团队混乱&#xff0c;因此&#xff0c;建立系统化的交接流程和使用专业的工具是防止信息遗漏的有效策略。交接过程中的信息丢失往往源自沟通不畅、文档不完整或者责任不明确等问题&#xff0c;这不仅影响项目的顺利…...

【AI量化第24篇】KhQuant 策略框架深度解析:让策略开发回归本质——基于miniQMT的量化交易回测系统开发实记

我是Mr.看海&#xff0c;我在尝试用信号处理的知识积累和思考方式做量化交易&#xff0c;应用深度学习和AI实现股票自动交易&#xff0c;目的是实现财务自由~ 目前我正在开发基于miniQMT的量化交易系统——看海量化交易系统。 本篇要讲到量化的核心了——策略。说白了每个投资者…...

向量数据库Qdrant 安装 不使用docker

一、导读 环境&#xff1a;Ubuntu 24.04、Windows 10、WSL 2、Qdrant 1.13.4 背景&#xff1a;换了新工作&#xff0c;使用qdrant作为向量库&#xff0c;需要不使用docker安装 时间&#xff1a;20250415 说明&#xff1a;初入职&#xff0c;不了解&#xff0c;暂且记下 二、…...

微电网与分布式能源:智能配电技术的场景化落地

安科瑞顾强 随着数字化转型与能源革命的加速推进&#xff0c;电力系统正经历从传统模式向智能化、网络化方向的深刻变革。用户侧的智能配电与智能用电技术作为这一变革的核心驱动力&#xff0c;正在重塑电力行业的生态格局。本文将从技术架构、应用场景及未来趋势等维度&#…...

实战指南:封装Whisper为FastAPI接口并实现高并发处理-附整合包

实战指南&#xff1a;封装Whisper为FastAPI接口并实现高并发处理 下面给出一个详细的示例&#xff0c;说明如何使用 FastAPI 封装 OpenAI 的 Whisper 模型&#xff0c;提供一个对外的 REST API 接口&#xff0c;并支持一定的并发请求。 下面是主要步骤和示例代码。 1. 环境准备…...

C#Winform程序将子窗体嵌入容器方法

private void OpenForm(Form childFrom) { //首先判断容器中是否有其他的窗体 foreach (Control item in this.panelRight.Controls) { if (item is Form) { ((Form)item).Close(); } } //嵌入新的窗体 childFrom.TopLevel false;//将子窗体设置成非顶级控件 childFrom.Parent…...

Web三漏洞学习(其一:文件上传漏洞)

靶场:云曦历年考核题 一、文件上传 在此之前先准备一个一句话木马 将其命名为muma.txt 23年秋期末考 来给师兄上个马 打开环境以后直接上传muma.txt&#xff0c;出现js弹窗&#xff0c;说明有前端验证 提示只能上传.png .jpg 和 .gif文件&#xff0c;那就把muma.txt的后缀…...

37-串联所有单词的子串

给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如&#xff0c;如果 words ["ab","cd","ef"]&#xff0c; 那么 "abcdef…...

ShardingSphere复合分片之hash槽算法

前言 上一篇《ShardingSphere复合分片》中有详细介绍多key多value的复合分片算法应该如何设计&#xff0c;在大部分情况下该算法是没有问题的&#xff0c;但是一旦涉及到数据迁移时&#xff0c;该算法的缺点就暴露无疑了。 为满足日益增长的用户或者订单的需求&#xff0c;在分…...

Web三漏洞学习(其二:sql注入)

靶场&#xff1a;NSSCTF 、云曦历年考核题 二、sql注入 NSSCTF 【SWPUCTF 2021 新生赛】easy_sql 这题虽然之前做过&#xff0c;但为了学习sql&#xff0c;整理一下就再写一次 打开以后是杰哥的界面 注意到html网页标题的名称是 “参数是wllm” 那就传参数值试一试 首先判…...

KrillinAI:视频跨语言传播的一站式AI解决方案

引言 在全球内容创作领域&#xff0c;跨语言传播一直是内容创作者面临的巨大挑战。传统的视频本地化流程繁琐&#xff0c;涉及多个环节和工具&#xff0c;不仅耗时耗力&#xff0c;还常常面临质量不稳定的问题。随着大语言模型(LLM)技术的迅猛发展&#xff0c;一款名为Krillin…...

gravity`(控制 View 内部内容的对齐方式)

文章目录 **1. 常用取值****示例** **2. layout_gravity&#xff08;控制 View 在父容器中的对齐方式&#xff09;****常用取值****示例** **3. gravity vs layout_gravity 对比****4. 注意事项****5. 总结** 作用对象&#xff1a;当前 View 的内部内容&#xff08;如 TextView…...

gitdiagram源码架构分析

https://github.com/ahmedkhaleel2004/gitdiagram 整体架构分析 前端请求入口&#xff1a; 后端对应接口&#xff1a; 后端调试 后端调试&#xff1a;会提示api_key失败的问题&#xff1a; 有两种方法解决&#xff1a; 1、注释掉下面的行代码&#xff1b; 方法二&#xff1…...

蓝光三维扫描:汽车冲压模具与钣金件全尺寸检测的精准解决方案

随着汽车市场竞争日趋激烈&#xff0c;新车型开发周期缩短&#xff0c;安全性能要求提高&#xff0c;车身结构愈加复杂。白车身由多达上百个具有复杂空间型面的钣金件&#xff0c;通过一系列工装装配、焊接而成。 钣金件尺寸精度是白车身装配精度的基础。采用新拓三维XTOM蓝光…...

《分布式软总线:网络抖动下的数据传输“定海神针”》

在当下&#xff0c;智能设备之间的互联互通已成为生活与工作的刚需。分布式软总线作为实现这一愿景的关键技术&#xff0c;正日益凸显其重要性。然而&#xff0c;网络环境的复杂性&#xff0c;尤其是网络抖动频繁的情况&#xff0c;给分布式软总线的数据传输带来了严峻挑战。如…...

WPS JS宏编程教程(从基础到进阶)-- 第八部分:字符串技术与WPS结合应用

目录 第8章 字符串技术与WPS结合应用8-1 字符串的3种引用方式场景:动态生成报表标题三种引用方式对比代码解析表模板字符串核心优势8-2 字符串处理之切片与搜索场景:提取身份证中的出生年份三大截取方法对比方法选择指南索引搜索实战8-3 字符串处理之修改与填充场景:规范商品…...

C++实用函数:bind

本篇来介绍了C++中bind功能。 1 std::bind 在 C++ 里,std::bind 是一个函数模板,其作用是创建一个可调用对象,该对象可绑定到一组参数上。std::bind 的函数原型如下: template< class F, class... Args > /*unspecified*/ bind( F&& f, Args&&...…...

深度学习占用大量内存空间解决办法

应该是缓存的问题&#xff0c;关机重启内存多了10G&#xff0c;暂时没找到别的方法 重启前 关机重启后...

完全无网络环境的 openEuler 系统离线安装 ClamAV 的详细步骤

准备工作&#xff08;在外网机器操作&#xff09; 1. 下载 ClamAV RPM 包及依赖 mkdir -p ~/clamav-offline/packages cd ~/clamav-offline/packages# 使用 yumdownloader 下载所有依赖包&#xff08;需提前安装 yum-utils&#xff09; sudo dnf install yum-utils -y sudo y…...

Matlab绘制函数方程图形

Matlab绘制函数方程图形&#xff1a; 多项式计算: polyval 函数 Values of Polynomials: polyval ( ) 绘制方程式图形&#xff1a; 代码如下&#xff1a; >> a[9,-5,3,7]; x-2:0.01:5; fpolyval(a,x); plot(x,f,LineWidth,2); xlabel(x); ylabel(f(x))…...

Unity UI 从零到精通 (第30天): Canvas、布局与C#交互实战

Langchain系列文章目录 01-玩转LangChain&#xff1a;从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块&#xff1a;四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain&#xff1a;从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...

在AMGX中使用MPI加载自定义分布式矩阵和向量

在AMGX中使用MPI加载自定义分布式矩阵和向量 AMGX是一个用于大规模并行代数多重网格求解的GPU加速库&#xff0c;支持MPI多线程环境。以下是加载用户自定义分布式矩阵和向量的方法&#xff1a; 1. 矩阵和向量分布的基本概念 在MPI环境中&#xff0c;AMGX使用行分布方式&…...