- 博客(64)
- 收藏
- 关注
原创 CUDA编程:其五、线程束
逻辑层面上,一个grid上的所有线程都是并行的,但这仅仅是逻辑层面,物理层面上肯定做不到百万级别的线程并行。就好像CPU线程,物理并行能力要看CPU核数,GPU的并行能力要看线程束(warp)的大小。
2024-05-18 23:14:04
162
1
原创 CUDA编程:其四、CUDA矩阵乘法
不同于C++,c语言对于矩阵的表示和一维数组没有区别。矩阵虽然是二维,但是在c语言内存空间里面仍然是线性存储,说白了先存第一行,再存第二行,以此类推。假如一个二维数组int array[100][100],那么元素a[50][50]按照指针的取值方法就是。假如有两个矩阵A和B,那么A×B可以运算的前提是,
2024-04-23 17:01:16
234
原创 CUDA编程:其三、CUDA向量加法
为什么CUDA并行计算特别适用于向量加法呢?因为计算向量加法的时候各个元素之间互相独立,互不影响。假如所我要计算两个1024长度的向量相加,那么就可以启动1024个线程,每个线程分别计算一个位置上的值。第一个参数表示传入的用来记录数据的指针的地址(有点拗口),第二次参数很好理解,表示申请的GPU空间的大小。这个函数负责在GPU申请内存,对应CPU之下的malloc函数。这表示在GPU上开辟了一个长度100的int数组。
2024-04-17 16:07:23
270
原创 CUDA编程:其一、核函数的基本概念
(2)<<<1,10>>>表示的是一个网格里面的block数量,每个block之下的线程数量。网格、block、线程数在后面章节有详细介绍。(3)cudaDeviceReset()表示同步等待过程,因为GPU和CPU是独立运行的,如果CPU先于GPU执行完的话,程序就会退出。说明这是一个核函数,供给GPU调用的。准确地说,__global__表示这个函数是从CPU处调用的GPU代码。核函数就是托管给GPU执行的函数,一般来说,并发量大,但是逻辑简单、分支少的函数适合交给GPU执行。
2024-04-14 18:34:30
358
原创 C++多线程编程:其八、future和aysnc函数
aysnc函数异步执行的原理也非常简单:thread绑定的函数返回值是void,aysnc函数绑定的函数返回值可以不是void,可以通过返回值判断异步执行结果。上一篇里面讲到了,对于控制线程之间协作,可以使用条件变量去完成。有没有更优雅简洁的方法呢?
2024-02-08 17:02:27
369
原创 C++多线程编程:其七、条件变量的使用
一句话:控制线程执行的顺序。如果一个线程的执行,必须要等待另一个线程的执行结果,就需要采用这种异步等待机制。
2024-02-07 09:20:54
400
原创 C++多线程编程:其六、unique_lock的使用
unique_lock在构造的时候传入mutex变量,对mutex变量加锁。在析构的时候对这个mutex变量解锁,从而实现了自动解锁。mutex对象需要手动解锁。但是如果在解锁之前抛出来异常,就会导致解锁逻辑没有执行。执行后会发现程序被卡主,因为发生了死锁。
2023-11-21 08:52:19
269
原创 C++多线程编程:其五、互斥量mutex
互斥量(Mutex)从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放锁。一个线程占有一个互斥量以后,不允许其他线程继续占有这个互斥量。其他试图访问这个互斥量的线程会被阻塞住。直到占有互斥量的线程主动解锁。本文重点介绍互斥量mutex的使用。既然mutex是给多个线程用的锁,所以这把锁,从内存的角度讲,要被多个线程共同访问。所以一般而言,mutex要定义在全局区和静态区。
2023-11-20 19:12:48
195
1
原创 C++多线程编程:其四、线程安全问题
通常情况下,指的是多个线程同时修改一块内存产生的预期以外的情况。i<10000;i<10;return 0;启动10个线程,每个线程对一个变量都累加10000次,最后结果显然是100000,但是运行结果一直不到100000,为什么会这样呢?因为++不是原子操作。++的行为可以拆解为三步:(1)从变量中拿到原始值赋给寄存器;(2)在寄存器中+1;(3)将寄存器中的值赋给原来的变量。
2023-11-18 16:29:18
189
原创 C++多线程编程:其三、detach函数
但是注意一点,detach以后,不是说被分离的线程就完全独立了。根据《unix编程手册》的说法,主线程退出之后,这个进程中的所有线程都会被退出。(2)和调用方线程分离,不等待,各执行各的。(1)同样的,将持有的资源移交给操作系统;(1)将持有的资源移交给操作系统;(2)调用方线程阻塞等待。
2023-11-11 21:11:46
112
原创 C++模板特化详解
有个有意思的事情,去掉base.display();根据《C++ primer》中的解释,对于类模板中的成员函数,即使在类模板实例化以后,如果这个函数不被调用,那么也不会被具体化。看起来能编译通过,因为cout
2023-11-11 10:56:41
340
原创 C++类模板继承关系
上面的种种现象,只需要记住一下几个点,全部都可以解释通:(1)子类构造对象的时候,必然要给父类构造对象;(2)构造对象的前提是明确类型,没有实例化的模板是不能构造对象的。(3)子类确定类型的时候,如果父类的类型不明确,那么就无法构造对象。
2023-11-10 00:01:01
201
原创 C++多线程编程:其一、thread类概述
thread是C++11版本中出现的线程对象,可以让程序员非常方便地创建线程。非空的thread对象创建以后,线程就会自动运行起来。简单地理解,一个线程对象中会传入一个函数指针,之后编译器会构造一个栈,将这个函数指针压栈。函数就可以视为任务,从而实现了任务并发。
2023-11-05 23:19:27
152
原创 C++编译与运行:其三、虚函数、虚表和多态
对于非虚非静态成员函数的调用,是在编译期间就明确了的。简单地说,调用的时候将this指针传入函数,后面根据this指针指向的对象执行程序。但是在虚函数中却不一样。
2023-10-28 23:30:38
202
原创 C++编译与运行:其二、编译期和运行期的区别
编译器只知道这是通过一个Father类型的指针调用一个Father类的public成员函数,编译通过完全没问题。因为虚函数表的产生,所以运行时实际调用的是Son类的私有成员函数,但是运行时不关注访问类型,如此一来毫无问题。ptr是动态类型,因为存在虚函数和虚函数表,所以变成了动态绑定。但是动态绑定发生在运行的时候,在编译期间,首先,类是编译期间的概念,所谓访问权限控制只发生在编译期间。C++的运行,就是将可执行文件交给操作系统,按照机器码逐步执行,运行功能。C++的编译分为四步,最终生成一个可执行文件。
2023-10-28 20:31:37
235
原创 C++编译与运行:其一、静态类型和动态类型
先说结论:编译期间可以明确的类型是静态类型;运行期间才能明确的类型是动态类型。后半句可能有点不好理解,通俗地说,需要通过执行代码才能明确的类型是动态类型。假如我们有两个类,分别是基类A,和派生类B。A a;//静态类型//动态类型第一行,编译的时候编译器就知道了,a的类型是A,所以是静态绑定。第二行,难道编译的时候,编译器不知道p指向的是B类型的对象吗?抱歉,真的不知道。因为编译过程中,不会执行代码。
2023-10-28 20:10:19
367
原创 C++内存管理:其七、标准库中的allocator
首先明确一点,绝大多数情况下,是标准库中的容器使用allocator。因为容器需要频繁的申请和释放内存。
2023-10-26 21:03:14
225
原创 C++内存管理:其六、静态allocator的实现
前文我们介绍了简易版内存池的实现,但是那个代码看起来过于繁琐,不够清爽。我们可以把内存池的各个操作封装起来,作为一个独立的内存池使用。
2023-10-18 23:51:55
162
原创 C++内存管理:其五、指针类型转换与嵌入式指针
在C++里面,所有指针都是四字节,表示一个地址。举个例子,假如是一个int类型的指针,定位到这个地址之后,扫描后面的四个字节,去解析这32位二进制代表的int数字是多少。作者在上一版本里面介绍了链表实现内存池,其中有一个小缺陷:虽然较少了cookie的内存损耗,但是加入了一个额外的指针,仍然需要占用内存。说一个看起来违背常识的事情,指针之间转换,基本是不被编译器报错的!(1)Test类的字节数大于A类,将Test指针强转为A类型指针后,相当于使用前面的地址,后面的地址也不会被抛弃,只是当前不用。
2023-10-17 11:03:19
151
原创 C++内存管理:其四、使用链表实现简易版内存池
既然频繁的malloc不是一件好事情,那么我们可以一次malloc申请一大块内存。当需要new一个对象的时候,可以在已经申请好的内存里面执行构造函数。在我们delete这个对象之后,这个内存上还可以用来构造其它对象。这就是内存池设计的基本思想:一次性申请大块内存,用于多次new对象,避免频繁的malloc与delete。
2023-10-11 23:43:37
230
原创 C++内存管理:其二、数组内存管理
如果有指针了,那么需要在析构函数里面delete,不调用析构函数直接回收内存就会导致内存泄漏,如std::string。像上述我们的例子,Complex,成员变量全是基本数据类型,就可以不delete [],直接delete就可以。(1)根据cookie中记录的对象个数,依次执行析构函数。那么不影响这一个数组本身的内存回收,但是析构函数只会执行一次。(1)申请一块内存,足够放置三个Complex对象。都没有析构函数的概念,直接delete a就足够了。对于对象数组的delete,通常采用的是。
2023-10-07 23:46:37
171
原创 C++内存管理:其一、四种内存管理方式概述
参数size指定待分配的内存大小,函数内部调用malloc初始化内存,返回指向这个内存的指针。(1)调用::operator new函数分配内存。operator new的原型是。(2)在给定位置执行构造函数。(3)返回对象的指针。
2023-10-07 12:10:15
65
原创 C++智能指针系列:其三、shared_ptr
前面提到的两种智能指针,设计思想都是独占资源。shared_ptr的设计思想是共享资源。一个指针可以被多个shared_ptr管理。采用引用计数法控制析构函数的执行,当shared_ptr发生赋值和拷贝的时候,引用计数加一。当shared_ptr发生析构的时候,引用计数减一。这个看起来特别像jvm里面的引用计数法,实则不然。后文会对原理有详细介绍。
2023-10-04 23:22:45
139
原创 C++智能指针系列:其二、unique_ptr
前文提到了,auto_ptr由于资源转移机制设计的不合理,有可能导致程序崩溃。在C++11中,提出了unique_ptr用以取代auto_ptr。
2023-09-30 23:52:02
73
原创 C++智能指针系列:其一、auto_ptr
但是注意,在通常的拷贝构造函数中,参数类型为const引用,但是这里应该是非const的引用,因为要保证被拷贝的智能指针,解除对于堆对象指针的托管。比如我们new了一个对象,会占用堆上的一块内存,返回指针,如果不手动delete这个指针,那么这块内存就会一直被占用,这就是所谓的内存泄露。(2)这个模板类有一个成员变量,类型是:指向模板参数类型的指针,由这个成员变量存储被托管的指针。(1)智能指针本质上是一个模板类,模板参数表示实际上要托管的指针指向的对象的类型。于是,我们可以很好的总结智能指针的基本要素。
2023-09-29 23:32:36
115
原创 C++左值引用、右值引用、移动语义详解
为帮助您在CSDN创作的文章获得更多曝光和关注,我们为您提供了专属福利:已注册且未在CSDN平台发布过文章的用户,9月1日—9月30日期间发布首篇文章可享大额首篇流量券扶持,且发布首篇文章后30日内,享连续每日流量券扶持;已注册且未在CSDN平台发布过文章的用户,在8月1日—8月30日期间发布过首篇,可自9月1日起,享连续30天每日流量券扶持;更多福利介绍详见https://mp.csdn.net/mp_blog/manage/traffic如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇
2023-09-20 23:06:37
241
原创 STL随笔:遍历容器
C++为每一个STL模板容器提供了一个iterator(指针),用于遍历容器。二、使用C++11提供的遍历方法。一、使用iterator遍历。
2023-08-22 23:08:19
85
原创 C++ 命名空间
一个中大型软件往往由多名程序员共同开发,会使用大量的变量和函数,不可避免地会出现变量或函数的命名冲突。当所有人的代码都测试通过,没有问题时,将它们结合到一起就有可能会出现命名冲突。例如小李和小韩都参与了一个文件管理系统的开发,它们都定义了一个全局变量 fp,用来指明当前打开的文件,将他们的代码整合在一起编译时,很明显编译器会提示 fp 重复定义(Redefinition)错误。为了解决合作开发时的命名冲突问题,C++ 引入了命名空间(Namespace)的概念。
2023-08-05 14:44:47
137
原创 C++函数模板
因为“abc”底层是指针,我当成string用,是因为string有对应的转换构造函数。这是模板,编译器不知道应该转换为什么类型,所以不会调用转换构造函数,直接是两个地址比较大小。模板标识从template开始定义,后续跟着一对“”,中间是参数列表。模板参数表示等待绑定实际类型的模板类型列表,统一表示为“typename”类型,或者是“class”类型。T表示类型待定,我们在使用模板时需要明确类型。
2023-07-10 19:18:18
95
原创 C++转换函数
转换构造函数是将其他类型转化为当前class类型;反之,就是转换函数。operator 是 C++ 关键字,type 是要转换的目标类型,data 是要返回的 type 类型的数据。因为要转换的目标类型是 type,所以返回值 data 也必须是 type 类型。既然已经知道了要返回 type 类型的数据,所以没有必要再像普通函数一样明确地给出返回值类型。这样做导致的结果是:类型转换函数看起来没有返回值类型,其实是隐式地指明了返回值类型。
2023-07-02 22:07:33
79
原创 C++转换构造函数(隐式构造函数)
语法:explicit放在构造函数的前面。如果构造函数定义在类外,那么类外定义构造函数时不能再使用explicit关键字。转换构造函数特点:只有一个参数的构造函数,参数类型可以是基本数据类型,也可以是复杂class。注意:explicit构造函数只能用于直接初始化,而不能用于拷贝拷贝形式的初始化过程。功能:如果我们不想让类发现隐式类型转换,就可以使用该关键字来阻止。explicit关键字抑制构造函数定义隐式转换。可以给class定义转换构造函数;
2023-07-02 14:21:55
134
原创 静态vector容器成员变量的定义和初始化
想要定义一个静态容器成员变量,保存数据以便后面共享。2.在.cpp文件中,记住是 类外部进行初始化。1.要现在.h文件的类内先声明该成员。
2023-06-27 13:53:48
388
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人