C++高性能并行编程与优化 - 课件 - 08 CUDA 开启的 GPU 编程
到高效的共享内存中,然后在共享内存中跨步地读,连续地写到 out 指向的低效的全局内存中 。 • 这样跨步的开销就开在高效的共享内存上,而不是低效的全局内存上,因此会变快。 共享内存:什么是区块( bank ) • GPU 的共享内存,实际上是 32 块内存条通过并联组成的(有点类似 CPU 的双通道内存条)。 • 每个 bank 都可以独立地访问,他们每个时钟周期都可以读取一个 int 样交错存储,可以保证随机访问时,访存能够尽量分摊到 32 个区块,这样速度就提升了 32 倍。 • 比如: __shared__ int arr[1024]; • 那么 arr[0] 是存储在 bank 0 , arr[1] 是 bank 1……arr[32] 又是 bank 0 , arr[33] 又是 bank 1 。 区块冲突( bank conflict ) • 然而这种设计有一个问题,那就是如果多个线程同时访问到 的,如果两个线程同时访问了 arr[0] 和 arr[32] 就会出现 bank conflict 导致必须排队影响 性能! 没有冲突,并行访问↓ 出现冲突,串行访问↓ 回到矩阵转置的案例:如何解决区块冲突? • 而刚刚那个矩阵转置的例子,这里的 blockSize 是 32 。可以看到第一个对 tmp 的访问是没冲突的。 • 而分析一下第二个对 tmp 的访问: threadIdx=(0,0)0 码力 | 142 页 | 13.52 MB | 1 年前3《深入浅出MFC》2/e
类别的成员函数,你可以想象就是C 语言中的函数。它只是被编译器改过名称, 并增加一个参数(this 指针),因而可以处理调用者(C++ 对象)中的成员变量。所以, 你并没有在Class1 对象的内存区块中看到任何与成员函数有关的任何东西。 class Class2 : public Class1 { public : data3; memfunc(); virtual 以下列宏仿真C++ exception handling: MFC 所使用的语法与日渐浮现的标准稍微不同,不过其间差异微不足道。为了以MFC 捕捉exceptions,你应该建立一个TRY 区块,下面接着CATCH 区块: 第㆒篇 勿在浮砂築高台 98 printf("Caught a memory exception.\n"); } AND_CATCH_ALL (e) { Many Open Files"); #033 break; #034 } #035 } 让我简单地做一个说明。TRY 区块中的动作(本例为开档、写档、关档)如果在执行时 期有任何exception 发生,就会跳到CATCH 区块中执行。CATCH 的第一个参数是 exception type:如果是文件方面的exception,就是CFileException,如果是内存方面0 码力 | 1009 页 | 11.08 MB | 1 年前3Hello 算法 1.0.0b5 C++版
在 C、C++、Go 和 Rust 等支持指针的语言中,上述的“引用”应被替换为“指针”。 如以下代码所示,链表节点 ListNode 除了包含值,还需额外保存一个引用(指针)。因此在相同数据量下,链 表比数组占用更多的内存空间。 /* 链表节点结构体 */ struct ListNode { int val; // 节点值 ListNode *next; // 指向下一节点的指针 ListNode(int 节点的引用两项数据。我们将首 个节点称为头节点,将最后一个节点称为尾节点,尾节点指向空 None 。 ‧ 环形链表:如果我们令单向链表的尾节点指向头节点(即首尾相接),则得到一个环形链表。在环形链 表中,任意节点都可以视作头节点。 ‧ 双向链表:与单向链表相比,双向链表记录了两个方向的引用。双向链表的节点定义同时包含指向后继 节点(下一个节点)和前驱节点(上一个节点)的引用(指针)。相较于单向链表,双向链表更具灵活 ‧ 栈与队列:当插入和删除操作都在链表的一端进行时,它表现出先进后出的的特性,对应栈;当插入操 作在链表的一端进行,删除操作在链表的另一端进行,它表现出先进先出的特性,对应队列。 ‧ 哈希表:链地址法是解决哈希冲突的主流方案之一,在该方案中,所有冲突的元素都会被放到一个链表 中。 ‧ 图:邻接表是表示图的一种常用方式,在其中,图的每个顶点都与一个链表相关联,链表中的每个元素 都代表与该顶点相连的其他顶点。0 码力 | 377 页 | 30.69 MB | 1 年前3Hello 算法 1.0.0b1 C++版
相比于「线性探测」,「多次哈希」方法更不容易产生聚集,代价是多个哈希函数增加了额外计算量。 � 工业界方案 Java 采用「链式地址」。在 JDK 1.8 之后,HashMap 内数组长度大于 64 时,长度大于 8 的链 表会被转化为「红黑树」,以提升查找性能。 Python 采用「开放寻址」。字典 dict 使用伪随机数进行探测。 6.3. 小结 ‧ 向哈希表中输入一个键 key ,查询到值 value 的时间复杂度为 hello‑algo.com 96 Figure 7‑7. 平衡二叉树 7.1.4. 二叉树的退化 当二叉树的每层的结点都被填满时,达到「完美二叉树」;而当所有结点都偏向一边时,二叉树退化为「链 表」。 ‧ 完美二叉树是一个二叉树的“最佳状态”,可以完全发挥出二叉树“分治”的优势; ‧ 链表则是另一个极端,各项操作都变为线性操作,时间复杂度退化至 ?(?) ; Figure 7‑8. 二叉树的术语较多,包括根结点、叶结点、层、度、边、高度、深度等。 ‧ 二叉树的初始化、结点插入、结点删除操作与链表的操作方法类似。 ‧ 常见的二叉树类型包括完美二叉树、完全二叉树、完满二叉树、平衡二叉树。完美二叉树是理想状态,链 表则是退化后的最差状态。 ‧ 二叉树可以使用数组表示,具体做法是将结点值和空位按照层序遍历的顺序排列,并基于父结点和子结 点之间的索引映射公式实现指针。 二叉树遍历 ‧ 二叉树层序遍历是一0 码力 | 187 页 | 14.71 MB | 1 年前3Hello 算法 1.0.0b2 C++版
相比于「线性探测」,「多次哈希」方法更不容易产生聚集,代价是多个哈希函数增加了额外计算量。 � 工业界方案 Java 采用「链式地址」。在 JDK 1.8 之后,HashMap 内数组长度大于 64 时,长度大于 8 的链 表会被转化为「红黑树」,以提升查找性能。 Python 采用「开放寻址」。字典 dict 使用伪随机数进行探测。 Golang 采用「链式地址」。Go 规定每个桶最多存储 8 个键值对,超出容量则连接一个溢出桶; hello‑algo.com 96 Figure 7‑7. 平衡二叉树 7.1.4. 二叉树的退化 当二叉树的每层的结点都被填满时,达到「完美二叉树」;而当所有结点都偏向一边时,二叉树退化为「链 表」。 ‧ 完美二叉树是一个二叉树的“最佳状态”,可以完全发挥出二叉树“分治”的优势; ‧ 链表则是另一个极端,各项操作都变为线性操作,时间复杂度退化至 ?(?) ; Figure 7‑8. 二叉树的术语较多,包括根结点、叶结点、层、度、边、高度、深度等。 ‧ 二叉树的初始化、结点插入、结点删除操作与链表的操作方法类似。 ‧ 常见的二叉树类型包括完美二叉树、完全二叉树、完满二叉树、平衡二叉树。完美二叉树是理想状态,链 表则是退化后的最差状态。 ‧ 二叉树可以使用数组表示,具体做法是将结点值和空位按照层序遍历的顺序排列,并基于父结点和子结 点之间的索引映射公式实现指针。 二叉树遍历 ‧ 二叉树层序遍历是一0 码力 | 197 页 | 15.72 MB | 1 年前3Hello 算法 1.0.0b4 C++版
‧ 栈与队列:当插入和删除操作都在链表的一端进行时,它表现出先进后出的的特性,对应栈;当插入操 作在链表的一端进行,删除操作在链表的另一端进行,它表现出先进先出的特性,对应队列。 ‧ 散列表:链地址法是解决哈希冲突的主流方案之一,在该方案中,所有冲突的元素都会被放到一个链表 中。 ‧ 图:邻接表是表示图的一种常用方式,在其中,图的每个顶点都与一个链表相关联,链表中的每个元素 都代表与该顶点相连的其他顶点。 会执行一次特殊的等量扩容操作,以确保性能。 6.3. 哈希算法 在上两节中,我们了解了哈希表的工作原理和哈希冲突的处理方法。然而无论是开放寻址还是链地址法,它 们只能保证哈希表可以在发生冲突时正常工作,但无法减少哈希冲突的发生。 如果哈希冲突过于频繁,哈希表的性能则会急剧劣化。对于链地址哈希表,理想情况下键值对平均分布在各 个桶中,达到最佳查询效率;最差情况下所有键值对都被存储到同一个桶中,时间复杂度退化至 com 123 Figure 7‑11. 前序遍历的递归过程 7.3. 二叉树数组表示 在链表表示下,二叉树的存储单元为节点 TreeNode ,节点之间通过指针相连接。在上节中,我们学习了在链 表表示下的二叉树的各项基本操作。 那么,能否用「数组」来表示二叉树呢?答案是肯定的。 7.3.1. 表示完美二叉树 先分析一个简单案例。给定一个完美二叉树,我们将所有节点按照层序遍历的顺序存储在一个数组中,则每0 码力 | 343 页 | 27.39 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 04 从汇编角度看编译器优化
懂!总之非常高效就对了! 第 5 章:循环 循环中的矢量化:还在伺候指针别名 我们可怜的编译器啊!他还在担心 a 和 b 指向的数组是否有重合。 考虑 func(a, a + 1) 的情况,那样会产生数据依赖链,没法 SIMD 化 。 为了优化而不失正确性,他索性生成两份代码: 一份是 SIMD 的,一份是传统标量的 他在运行时检测 a, b 指针的差是否超过 1024 来判断是否有重叠现 象。 1. 如果没有重叠,则跳转到0 码力 | 108 页 | 9.47 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 11 现代 CMake 进阶指南
系统,需要设置全部 6 个属性,是不是非常繁琐? 不懂就问,为什么说 Linux 系统是永远滴神? • 而 Linux 系统支持 RPATH , CMake 会让生成出来可执行文件的 RPATH 字段指向他链 接了的 .so 文件所在目录,运行时会优先从 RPATH 里找链接库,所以即使不在同目录也 能找到。 • 所以还有第三种解决方案:微软,我卸卸你全家(指卸载)。然后安装 Arch Linux 系统0 码力 | 166 页 | 6.54 MB | 1 年前3Hello 算法 1.1.0 C++ 版
在 C、C++、Go 和 Rust 等支持指针的语言中,上述“引用”应被替换为“指针”。 如以下代码所示,链表节点 ListNode 除了包含值,还需额外保存一个引用(指针)。因此在相同数据量下,链 表比数组占用更多的内存空间。 /* 链表节点结构体 */ struct ListNode { int val; // 节点值 ListNode *next; // 指向下一节点的指针 ListNode(int (1) 。 ‧ 划分阶段:可以使用“迭代”替代“递归”来实现链表划分工作,从而省去递归使用的栈帧空间。 ‧ 合并阶段:在链表中,节点增删操作仅需改变引用(指针)即可实现,因此合并阶段(将两个短有序链 表合并为一个长有序链表)无须创建额外链表。 具体实现细节比较复杂,有兴趣的读者可以查阅相关资料进行学习。 11.7 堆排序 Tip 阅读本节前,请确保已学完“堆“章节。 堆排序(heap0 码力 | 379 页 | 18.47 MB | 1 年前3Hello 算法 1.0.0 C++版
在 C、C++、Go 和 Rust 等支持指针的语言中,上述“引用”应被替换为“指针”。 如以下代码所示,链表节点 ListNode 除了包含值,还需额外保存一个引用(指针)。因此在相同数据量下,链 表比数组占用更多的内存空间。 /* 链表节点结构体 */ struct ListNode { int val; // 节点值 ListNode *next; // 指向下一节点的指针 ListNode(int (1) 。 ‧ 划分阶段:可以使用“迭代”替代“递归”来实现链表划分工作,从而省去递归使用的栈帧空间。 ‧ 合并阶段:在链表中,节点增删操作仅需改变引用(指针)即可实现,因此合并阶段(将两个短有序链 表合并为一个长有序链表)无须创建额外链表。 具体实现细节比较复杂,有兴趣的读者可以查阅相关资料进行学习。 11.7 堆排序 � 阅读本节前,请确保已学完“堆“章节。 「堆排序 heap s0 码力 | 378 页 | 17.59 MB | 1 年前3
共 11 条
- 1
- 2