C++高性能并行编程与优化 - 课件 - 07 深入浅出访存优化应该达到 6 倍(物理核心数量)才算理想加速比。 加速曲线 • funcA 用了 2 核就饱和。 • funcB 用了 4 核才饱和。 • funcC 用了 6 核才饱和。 • 结论:要想利用全部 CPU 核心,避免 mem-bound ,需要 func 里有足够的计算 量。 • 当核心数量越多, CPU 计算能力越强,相 对之下来不及从内存读写数据,从而越容 易 mem-bound 字节的 跨步,则中间的缓存行没有被读取,从而变快了。 缓存行决定数据的粒度 • 结论:访问内存的用时,和访问的字节数 量无关,和访问的每个字节所在的缓存行 数量有关。 • 可见,能否很好的利用缓存,和程序访问 内存的空间局域性有关。 缓存行决定数据的粒度(续) • 所以我们设计数据结构时,应该把数据存 储的尽可能紧凑,不要松散排列。最好每 个缓存行里要么有数据,要么没数据,避 的读, x 的写。浪费了 50% 带宽。 • 而 SOA 把三个属性分开存,每个属性作为独立的数组,稠密存储。 这样当用不到 z 的时候, z 数组就完全不会被读取,不会占用内 存带宽,从而带宽利用率是 100% ,因此比 AOS 快了 2 倍。 AOSOA :两者得兼 • 还有一种办法就是让 MyClass 内部是 SOA ,而外部仍是一个 vector的 AOS—— 0 码力 | 147 页 | 18.88 MB | 1 年前3
《深入浅出MFC》2/eside Visual C++ 4.0(中文版)。在此 之前我买了你的另一本书深入浅出MFC。在读了深入浅出MFC 前面50~70 页之后,我 想我错买了一本很艰深的书籍。我需要的是一本教我如何利用MFC 来产生一个程序的书, 而不是一本教我如何设计一套MFC 的书。但是在我又读了30~40 页之后,我想这本书真是 棒,它告诉了我许多过去我不甚清楚的事情,像是virtual functio 对您的书总是捧读再三,即使翻烂了也值得。这本深入浅出MFC,不但具有学习价值, 亦极具参考价值。 我买您的第一本书,好象是「内存管理与多任务」。还记得当时热中突破640KB 内存, 发现该书如获至宝。数月前购买了深入浅出MFC,并利用闲暇时间翻阅学习(包括如厕 时间... )。 我的学习曲线比较不同,我比较倾向于了解事情的因,而不是该如何做事情。比方说,「应 该使用MFC 的哪个类别」或「要改写哪个虚拟函数」,对我而言还不如「CWinApp 您。去年我因为想写Windows 程序而买您的书,说老实话,我实在看不懂您所写的文字(我 真的懂C++ 语法,也用Visual Basic 写过Windows 程序),故放弃改买其它书籍来学习。 虽把那些书全部看完了,也利用MFC 来写简单程序,但心里仍搞不懂这些程序的How、 What、Why。后来整理书架,发现这本书,就停下来拿来看,结果越看越搞懂这些Windows 程序到底如何How、What、Why。正如您0 码力 | 1009 页 | 11.08 MB | 1 年前3
Hello 算法 1.1.0 C++ 版章 数组与链表 hello‑algo.com 72 return res; } 4.1.2 数组的优点与局限性 数组存储在连续的内存空间内,且元素类型相同。这种做法包含丰富的先验信息,系统可以利用这些信息来 优化数据结构的操作效率。 ‧ 空间效率高:数组为数据分配了连续的内存块,无须额外的结构开销。 ‧ 支持随机访问:数组允许在 ?(1) 时间内访问任何元素。 ‧ 缓存局部性:当访问 效率,减少 对较慢的内存的依赖。 图 4‑10 硬盘、内存和缓存之间的数据流通 4.4.2 数据结构的内存效率 在内存空间利用方面,数组和链表各自具有优势和局限性。 一方面,内存是有限的,且同一块内存不能被多个程序共享,因此我们希望数据结构能够尽可能高效地利用 空间。数组的元素紧密排列,不需要额外的空间来存储链表节点间的引用(指针),因此空间效率更高。然而, 数组需要一次性分配足够的 的时间和空间成本。 相比之下,链表以“节点”为单位进行动态内存分配和回收,提供了更大的灵活性。 另一方面,在程序运行时,随着反复申请与释放内存,空闲内存的碎片化程度会越来越高,从而导致内存的 利用效率降低。数组由于其连续的存储方式,相对不容易导致内存碎片化。相反,链表的元素是分散存储的, 在频繁的插入与删除操作中,更容易导致内存碎片化。 4.4.3 数据结构的缓存效率 缓存虽然在空间容0 码力 | 379 页 | 18.47 MB | 1 年前3
Hello 算法 1.0.0 C++版章 数组与链表 hello‑algo.com 72 return res; } 4.1.2 数组的优点与局限性 数组存储在连续的内存空间内,且元素类型相同。这种做法包含丰富的先验信息,系统可以利用这些信息来 优化数据结构的操作效率。 ‧ 空间效率高:数组为数据分配了连续的内存块,无须额外的结构开销。 ‧ 支持随机访问:数组允许在 ?(1) 时间内访问任何元素。 ‧ 缓存局部性:当访问 效率,减少 对较慢的内存的依赖。 图 4‑10 硬盘、内存和缓存之间的数据流通 4.4.2 数据结构的内存效率 在内存空间利用方面,数组和链表各自具有优势和局限性。 一方面,内存是有限的,且同一块内存不能被多个程序共享,因此我们希望数据结构能够尽可能高效地利用 空间。数组的元素紧密排列,不需要额外的空间来存储链表节点间的引用(指针),因此空间效率更高。然而, 数组需要一次性分配足够的 的时间和空间成本。 相比之下,链表以“节点”为单位进行动态内存分配和回收,提供了更大的灵活性。 另一方面,在程序运行时,随着反复申请与释放内存,空闲内存的碎片化程度会越来越高,从而导致内存的 利用效率降低。数组由于其连续的存储方式,相对不容易导致内存碎片化。相反,链表的元素是分散存储的, 在频繁的插入与删除操作中,更容易导致内存碎片化。 4.4.3 数据结构的缓存效率 缓存虽然在空间容0 码力 | 378 页 | 17.59 MB | 1 年前3
Hello 算法 1.2.0 简体中文 C++ 版数组与链表 www.hello‑algo.com 72 return res; } 4.1.2 数组的优点与局限性 数组存储在连续的内存空间内,且元素类型相同。这种做法包含丰富的先验信息,系统可以利用这些信息来 优化数据结构的操作效率。 ‧ 空间效率高:数组为数据分配了连续的内存块,无须额外的结构开销。 ‧ 支持随机访问:数组允许在 ?(1) 时间内访问任何元素。 ‧ 缓存局部性:当访问 效率,减少 对较慢的内存的依赖。 图 4‑10 硬盘、内存和缓存之间的数据流通 4.4.2 数据结构的内存效率 在内存空间利用方面,数组和链表各自具有优势和局限性。 一方面,内存是有限的,且同一块内存不能被多个程序共享,因此我们希望数据结构能够尽可能高效地利用 空间。数组的元素紧密排列,不需要额外的空间来存储链表节点间的引用(指针),因此空间效率更高。然而, 数组需要一次性分配足够的 的时间和空间成本。 相比之下,链表以“节点”为单位进行动态内存分配和回收,提供了更大的灵活性。 另一方面,在程序运行时,随着反复申请与释放内存,空闲内存的碎片化程度会越来越高,从而导致内存的 利用效率降低。数组由于其连续的存储方式,相对不容易导致内存碎片化。相反,链表的元素是分散存储的, 在频繁的插入与删除操作中,更容易导致内存碎片化。 4.4.3 数据结构的缓存效率 缓存虽然在空间容0 码力 | 379 页 | 18.48 MB | 10 月前3
Hello 算法 1.2.0 繁体中文 C++ 版陣列與鏈結串列 www.hello‑algo.com 72 return res; } 4.1.2 陣列的優點與侷限性 陣列儲存在連續的記憶體空間內,且元素型別相同。這種做法包含豐富的先驗資訊,系統可以利用這些資訊 來最佳化資料結構的操作效率。 ‧ 空間效率高:陣列為資料分配了連續的記憶體塊,無須額外的結構開銷。 ‧ 支持隨機訪問:陣列允許在 ?(1) 時間內訪問任何元素。 ‧ 快取區域性:當 減少對較慢的記憶體的依賴。 圖 4‑10 硬碟、記憶體和快取之間的資料流通 4.4.2 資料結構的記憶體效率 在記憶體空間利用方面,陣列和鏈結串列各自具有優勢和侷限性。 一方面,記憶體是有限的,且同一塊記憶體不能被多個程式共享,因此我們希望資料結構能夠儘可能高效地 利用空間。陣列的元素緊密排列,不需要額外的空間來儲存鏈結串列節點間的引用(指標),因此空間效率更 高。然而,陣列需要一次性分配 間和空間成本。相比之下,鏈結串列以“節點”為單位進行動態記憶體分配和回收,提供了更大的靈活性。 另一方面,在程式執行時,隨著反覆申請與釋放記憶體,空閒記憶體的碎片化程度會越來越高,從而導致記 憶體的利用效率降低。陣列由於其連續的儲存方式,相對不容易導致記憶體碎片化。相反,鏈結串列的元素 是分散儲存的,在頻繁的插入與刪除操作中,更容易導致記憶體碎片化。 4.4.3 資料結構的快取效率 快取雖然0 码力 | 379 页 | 18.79 MB | 10 月前3
Hello 算法 1.0.0b5 C++版章 数组与链表 hello‑algo.com 69 return res; } 4.1.2 数组优点与局限性 数组存储在连续的内存空间内,且元素类型相同。这种做法包含丰富的先验信息,系统可以利用这些信息来 优化数据结构的操作效率。 ‧ 空间效率高: 数组为数据分配了连续的内存块,无须额外的结构开销。 ‧ 支持随机访问: 数组允许在 ?(1) 时间内访问任何元素。 ‧ 缓存局部性: 空间开销:由于每个元素需要两个额外的指针(一个用于前一个元素,一个用于后一个 元素),所以 std::list 通常比 std::vector 更占用空间。 ‧ 缓存不友好:由于数据不是连续存放的,std::list 对缓存的利用率较低。一般情况下, std::vector 的性能会更好。 另一方面,必要使用链表的情况主要是二叉树和图。栈和队列往往会使用编程语言提供的 stack 和 queue ,而非链表。 84 计算得到哈希值。 2. 将哈希值对桶数量(数组长度)capacity 取模,从而获取该 key 对应的数组索引 index 。 index = hash(key) % capacity 随后,我们就可以利用 index 在哈希表中访问对应的桶,从而获取 value 。 设数组长度 capacity = 100、哈希算法 hash(key) = key ,易得哈希函数为 key % 100 。图 6‑20 码力 | 377 页 | 30.69 MB | 1 年前3
Hello 算法 1.0.0b4 C++版计算得到哈希值。 2. 将哈希值对桶数量(数组长度)capacity 取模,从而获取该 key 对应的数组索引 index 。 index = hash(key) % capacity 随后,我们就可以利用 index 在哈希表中访问对应的桶,从而获取 value 。 设数组长度 capacity = 100 、哈希算法 hash(key) = key ,易得哈希函数为 key % 100 。下图以 哈希算法的设计是一个复杂且需要考虑许多因素的问题。然而对于简单场景,我们也能设计一些简单的哈希 算法。以字符串哈希为例: ‧ 加法哈希:对输入的每个字符的 ASCII 码进行相加,将得到的总和作为哈希值。 ‧ 乘法哈希:利用了乘法的不相关性,每轮乘以一个常数,将各个字符的 ASCII 码累积到哈希值中。 ‧ 异或哈希:将输入数据的每个元素通过异或操作累积到一个哈希值中。 ‧ 旋转哈希:将每个字符的 ASCII 码累 然而,数组表示也具有一些局限性: ‧ 数组存储需要连续内存空间,因此不适合存储数据量过大的树。 ‧ 增删节点需要通过数组插入与删除操作实现,效率较低。 ‧ 当二叉树中存在大量 None 时,数组中包含的节点数据比重较低,空间利用率较低。 7.4. 二叉搜索树 「二叉搜索树 Binary Search Tree」满足以下条件: 1. 对于根节点,左子树中所有节点的值 < 根节点的值 < 右子树中所有节点的值。 2.0 码力 | 343 页 | 27.39 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 15 C++ 系列课:字符与字符串道数组在什么地方结束,规定用 ASCII 码中的“空字符”也 就是 0 来表示数组的结尾。这样只需要一个首地址指针就 能表示一个动态长度的数组,高,实在是高。 “0 结尾字符串”知识点应用举例 • 利用 C 语言字符串“以 0 结尾”这个特点,我们可以在一个 本来非 0 的字符处写入 0 ,来提前结束字符串。例如在第 n 个字符写入 0 ,就会只保留前 n 个字符作为一个子字 符串,删除后半部分。 ,不能自己定义了。 • 所以 cpp 之父曾经说,他设计 cpp11 的时候,是考虑“如何在对语言本身改动最小的情况下 ,尽量只在标准库里做手脚,尽可能只利用现有的语言特性,实现 cpp 的现代化。” • 例如 shared_ptr 可以通过利用语言本身的“拷贝构造函数”实现引用计数,没必要在编译器里 开洞。但“移动语义”这个概念在旧 cpp 里没有,所以这个是真正必要的语言本身的改动。 • 而 做个加法运算,得到新的指针并解引用。如果你给的 i 超过了字符 串大小 i ≥ s.size() ,那程序的行为是未定义的,因为这个地方可能 有其他的对象,程序可能会奔溃,也可能行为异常。如果是富连网 程序,还可能会被黑客利用,窃取或篡改服务器上的数据。 • 那为什么还要 [] ?性能! at 做越界检测需要额外的开销, [] 不需 要。 • 所以 [] 更高效, at 更安全。遇到诡异 bug 时,试试把 [] 都改0 码力 | 162 页 | 40.20 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 06 TBB 开启的并行编程之旅这里卖个关子,欲知后事如何,请待下集揭晓! 更专业的性能测试框架: Google benchmark • 手动计算时间差有点太硬核了,而且只运 行一次的结果可能不准确,最好是多次运 行取平均值才行。 • 因此可以利用谷歌提供的这个框架。 • 只需将你要测试的代码放在他的 • for (auto _: bm) • 里面即可。他会自动决定要重复多少次, 保证结果是准确的,同时不浪费太多时间 。 运行结果 (auto r) ,这里写具体类型仅为 教学目的。 TBB 中其他并发容器 第 7 章:并行筛选 筛选( filter ) 利用 vector 的 push_back 动态追加数据 筛选出所有大于 0 的 sin(i) 值 并行筛选 1 (张心欣犯过的错) 利用多线程安全的 concurrent_vector 动态追加数据 基本没有加速,我猜想 concurrent_vector 内部可能 concurrent_vector 上产生锁竞争 加速比: 5.55 倍 并行筛选 3 线程局部的 vector 调用 reserve 预先分配一定内存 避免 push_back 反复扩容时的分段式增长 同时利用标准库的 std::copy 模板简化了代码 加速比: 5.94 倍 并行筛选 4 如果需要筛选后的数据是连续的,即 a 是个 std::vector ,这时就需要用 mutex 锁定,避免数据竞争0 码力 | 116 页 | 15.85 MB | 1 年前3
共 28 条
- 1
- 2
- 3













