C++高性能并行编程与优化 - 课件 - 07 深入浅出访存优化CPU 的厂商早就意识到了内存延迟高,读写效率低 下的问题。因此他们在 CPU 内部引入了一片极小的存储 器——虽然小,但是读写速度却特别快。这片小而快的 存储器称为缓存( cache )。 • 当 CPU 访问某个地址时,会先查找缓存中是否有对应的 数据。如果没有,则从内存中读取,并存储到缓存中; 如果有,则直接使用缓存中的数据。 • 这样一来,访问的数据量比较小时,就可以自动预先加 宽。三级缓存也装不下,那就取决于主内存 的带宽了。 • 结论:要避免 mem-bound ,数据量尽量足 够小,如果能装的进缓存就高效了。 L2: 256 KB L3: 12 MB 缓存的工作机制:读 • 缓存中存储的数据结构: • struct CacheEntry { • bool valid; • uint64_t address; • char data[64]; • 架构中每个条目的存储 64 字节的数据,这个条目 又称之为缓存行( cacheline )。 • 当访问 0x0048~0x0050 这 4 个字节时,实际会导致 0x0040~0x0080 的 64 字节数据整个被读取到缓存中。 • 这就是为什么我们喜欢把数据结构的起始地址和大小对齐到 64 字节,为的是不要浪费缓存行的存储空间。 缓存的工作机制:写 • 缓存中存储的数据结构:0 码力 | 147 页 | 18.88 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 11 现代 CMake 进阶指南1 章:添加源文件 一个 .cpp 源文件用于测试 CMake 中添加一个可执行文件作为构建目标 另一种方式:先创建目标,稍后再添加源文件 如果有多个源文件呢? 逐个添加即可 使用变量来存储 建议把头文件也加上,这样在 VS 里可以出现在“ Header Files” 一栏 使用 GLOB 自动查找当前目录下指定扩展名的文件,实现批量添加源文件 启用 CONFIGURE_DEPENDS find_package(spdlog REQUIRED) 时却 变成预编译链接库的版本。(嗯,其实不是 PUBLIC 而是 INTERFACE ,因为伪对象没有实体) 和古代 CMake 做对比:为什么 PUBLIC 属性的传播机制如此便利 现代 CMake : 古代 CMake : 和 find_package(TBB CONFIG REQUIRED) 有什么区别? 其实更好的是通过 find_package(TBB 第 7 章:变量与缓存 重复执行 cmake -B build 会有什么区别? 可以看到第二次的输出少了很多,这是因为 CMake 第一遍需要检测编译器 和 C++ 特性等比较耗时,检测完会把结果存储到缓存中,这样第二遍运行 cmake -B build 时就可以直接用缓存的值,就不需要再检测一遍了。 如何清除缓存?删 build 大法了解一下 然而有时候外部的情况有所更新,这时候 CMake0 码力 | 166 页 | 6.54 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 06 TBB 开启的并行编程之旅mutex 到无锁并行 6.并行编程常用框架: OpenMP 与 Intel TBB 7.被忽视的访存优化:内存带宽与 cpu 缓存机制 8.GPU 专题: wrap 调度,共享内存, barrier 9.并行算法实战: reduce , scan ,矩阵乘法等 10.存储大规模三维数据的关键:稀疏数据结构 11.物理仿真实战:邻居搜索表实现 pbf 流体求解 12.C++ 在 ZENO 中的工程实践:从 :每个线程一个任务队列,做完本职工作后可以认领其他线程的任务 工作窃取法( work-stealing ) 原始的单一任务队列 解决 4 :随机分配法(通过哈希函数或线性函数) • 然而队列的实现较复杂且需要同步机制,还是有一 定的 overhead ,因此另一种神奇的解法是: • 我们仍是分配 4 个线程,但还是把图像切分为 16 份。然后规定每一份按照 xy 轴坐标位置编号,比 如 (1,3) 等。 直接读写,避免了从主内存读写的超高延迟。 • 下次课会进一步深入探讨访存优化,详细剖析 这个案例,那么下周六 14 点敬请期待。 第 6 章:并发容器 std::vector 扩容时会移动元素 • std::vector 内部存储了一个指针,指向一段容量 capacity 大于等于其 size 的内存。 • 众所周知, push_back 会导致 size 加 1 ,但 当他看到容量 capacity 等于当前 size0 码力 | 116 页 | 15.85 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 04 从汇编角度看编译器优化mutex 到无锁并行 6.并行编程常用框架: OpenMP 与 Intel TBB 7.被忽视的访存优化:内存带宽与 cpu 缓存机制 8.GPU 专题: wrap 调度,共享内存, barrier 9.并行算法实战: reduce , scan ,矩阵乘法等 10.存储大规模三维数据的关键:稀疏数据结构 11.物理仿真实战:邻居搜索表实现 pbf 流体求解 12.C++ 在 ZENO 中的工程实践:从 什么是 xmm 系列寄存器? • xmm 寄存器有 128 位宽。 • 可以容纳 4 个 float ,或 2 个 double 。 • 刚才的案例中只用到了 xmm 的低 32 位 用于存储 1 个 float 。 addss 是什么意思? • 可以拆分成三个部分: add , s , s 1. add 表示执行加法操作。 2. 第一个 s 表示标量 (scalar) ,只对 xmm 的容器:我是说,内存分配在堆上的容器 • 存储在堆上(妨碍优化): • vector, map, set, string, function, any • unique_ptr, shared_ptr, weak_ptr • 存储在栈上(利于优化): • array, bitset, glm::vec, string_view • pair, tuple, optional, variant 存储在栈上无法动态扩充大小,这就是0 码力 | 108 页 | 9.47 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 03 现代 C++ 进阶:模板元编程mutex 到无锁并行 6.并行编程常用框架: OpenMP 与 Intel TBB 7.被忽视的访存优化:内存带宽与 cpu 缓存机制 8.GPU 专题: wrap 调度,共享内存, barrier 9.并行算法实战: reduce , scan ,矩阵乘法等 10.存储大规模三维数据的关键:稀疏数据结构 11.物理仿真实战:邻居搜索表实现 pbf 流体求解 12.C++ 在 ZENO 中的工程实践:从 然后 Func const & 做类型。 2. lambda 作为返回值:用 auto 做类型。 3. 牺牲性能但存储方便: std::function 容器。 4. lambda 作为参数:通常用 [&] 存储引用。 5. lambda 作为返回值:总是用 [=] 存储值。 • 其实 lambda 还有更多语法,比如 mutable , [p = std::move(p)] 等…… 常用容器: 时会自动释放内部的对象。 • 利用这一点可以实现 RAII 容器的提前 释放。和 unique_ptr 的区别在于他的 对象存储在栈上,效率更高。 variant :安全的 union ,存储多个不同类型的值 • 有时候需要一个类型“要么存储 int ,要么存储 float” ,这时候就可以用 std::variant。 • 和 union 相比, variant 0 码力 | 82 页 | 12.15 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 12 从计算机组成原理看 C 语言指针如果你没看出来(哪怕是其中一个),那就要好好上小彭老师的课哦! 字节( byte ) 和位( bit )有什么区别 • 众所周知,计算机是二进制的,存储的实际上是一个个 0 和 1 。 • 每个存储 0 或 1 的空间称为一个位( bit ),一位可以存储 0 或 1 两个可能的值。 • 现在的计算机都会把 8 个位打包成一个字节( byte ),也就是说: 1 字节 = 8 位。 • 一字节可以表示 断,才能支持有符号整数的加减法,因此如今的计算机都采用了一种更聪明的表示法: • 他们让 11111111 表示 -1 , 10000000 表示 -128 ,也就是大名鼎鼎的补码表示法。 • 这样做的目的是,利用加法器的“溢出”机制,例如 -1 + 2 = 1 ,在计算机看来就是: • 11111111 + 00000010 = 100000001 • 正好和普通的二进制加法一样,只需要丢弃最前面的那一位进位就可以了。 • = 1024 KB • 1 GB = 1024 MB • 1 TB = 1024 GB • 也有人说 1 KiB 才是 1024 B 的,但是很少有人采用这种写法…… • 在买硬盘和 u 盘等存储设备的时候,往往会出现容量减少的情况,这是因为生产厂家按照 的是 1000 倍的换算的,而我们的系统中一般都是按照 1024 倍去计算的。 字还被用于表示内存地址 • 字的长度除了决定一次处理0 码力 | 128 页 | 2.95 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 02 现代 C++ 入门:RAII 内存管理mutex 到无锁并行 6.并行编程常用框架: OpenMP 与 Intel TBB 7.被忽视的访存优化:内存带宽与 cpu 缓存机制 8.GPU 专题: wrap 调度,共享内存, barrier 9.并行算法实战: reduce , scan ,矩阵乘法等 10.存储大规模三维数据的关键:稀疏数据结构 11.物理仿真实战:邻居搜索表实现 pbf 流体求解 12.C++ 在 ZENO 中的工程实践:从 管理的对象生命周期长度,取决于他所属的唯一一个引用的寿命 。 那是不是只要 shared_ptr 就行,不用 unique_ptr 了? • 可以适当使用减轻初学者的压力,因为他的行为和 Python 等 GC 语言的引用计数机制很像。但从长远 来看是不行的,因为: 1. shared_ptr 需要维护一个 atomic 的引用计数器, 效率低,需要额外的一块管理内存,访问实际对象 需要二级指针,而且 deleter 除拷贝函数的那一类,解 决这种需求,几乎总是在用 shared_ptr的模式,于是 Java 和 Python 干 脆简化:一切非基础类型的对象都是浅拷贝,引用计数由垃圾回收机制自动管理。 • 因此,以系统级编程、算法数据结构、高性能计算为主要业务的 C++ ,才发展出了这些思 想,并将拷贝 / 移动 / 指针 / 可变性 / 多线程等概念作为语言基本元素存在。这些在我们的 0 码力 | 96 页 | 16.28 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 05 C++11 开始的多线程编程mutex 到无锁并行 6.并行编程常用框架: OpenMP 与 Intel TBB 7.被忽视的访存优化:内存带宽与 cpu 缓存机制 8.GPU 专题: wrap 调度,共享内存, barrier 9.并行算法实战: reduce , scan ,矩阵乘法等 10.存储大规模三维数据的关键:稀疏数据结构 11.物理仿真实战:邻居搜索表实现 pbf 流体求解 12.C++ 在 ZENO 中的工程实践:从 思想,但自由度更高 • std::lock_guard 严格在解构时 unlock() ,但是 有时候我们会希望提前 unlock() 。这时可以用 std::unique_lock ,他额外存储了一个 flag 表 示是否已经被释放。他会在解构检测这个 flag ,如果没有释放,则调用 unlock() ,否则 不调用。 • 然后可以直接调用 unique_lock 的 unlock() 用于读取其中的 int 值 fetch_add :会返回其旧值 • int old = atm.fetch_add(val) • 除了会导致 atm 的值增加 val 外,还会 返回 atm 增加前的值,存储到 old 。 • 这个特点使得他可以用于并行地往一个列 表里追加数据:追加写入的索引就是 fetch_add 返回的旧值。 • 当然这里也可以 counter++ ,不过要追加 多个的话还是得用到0 码力 | 79 页 | 14.11 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 15 C++ 系列课:字符与字符串计算机如何表达字符 • 众所周知,计算机只能处理二进制 整数,字符要怎么办呢? • 于是就有了 ASCII 码表,他规定, 每个英文字符(包括大小写字母、 数字、特殊符号)都对应着一个整 数。在计算机里只要存储这个的整 数,就能代表这个字符了。 • 例如 32 代表空格, 48 代表 ‘ 0’ , 65 代表 ‘ A’ , 97 代表 ‘ a’…… • 32~126 这些整数就用于是表示这些 可显示字符 和 string 其实都是胖指针。 • string 和 vector 内部都有三个成员变量: ptr, len, capacity 。 • 前两个 [ptr, len] 其实就是表示实际有效范围(存储了字符的)的胖指针。 • 而 [ptr, capacity] 就是表示实际已分配内存(操作系统认为的)的胖指针。 • struct vector { • char *ptr; • size_t string 克服了 C 语言 0 结尾字符串的缺点: • 字符串本身可以含有 ‘ \0’ 了,这下任何字符都众生平等。 • 末尾没有 ‘ \0’ 额外浪费的空间(除非调用 c_str 时)。 • 长度已经存储在 string 的成员里, size() 是 O(1) 的。 • 在尾部切片可以用 resize() 修改长度,无需写入字符串本身。 • string_view 和 span 无非是个弱引用版本,额外增加了在头部切片的能力而0 码力 | 162 页 | 40.20 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 01 学 C++ 从 CMake 学起mutex 到无锁并行 6.并行编程常用框架: OpenMP 与 Intel TBB 7.被忽视的访存优化:内存带宽与 cpu 缓存机制 8.GPU 专题: wrap 调度,共享内存, barrier 9.并行算法实战: reduce , scan ,矩阵乘法等 10.存储大规模三维数据的关键:稀疏数据结构 11.物理仿真实战:邻居搜索表实现 pbf 流体求解 12.C++ 在 ZENO 中的工程实践:从0 码力 | 32 页 | 11.40 MB | 1 年前3
共 29 条
- 1
- 2
- 3













