C++高性能并行编程与优化 - 课件 - Zeno 中的现代 C++ 最佳实践 0 码力 | 54 页 | 3.94 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 性能优化之无分支编程 Branchless Programmingchar 一样只占据 1 字节( al 寄存器就 1 字节) • 而 C 语言可以自动把 bool 转换成 int 类型( movzx 把 1 字节的 al 转换成 4 字节的 eax ,零扩展:高 3 字节 填充零) • 返回类型 int 占据 4 字节( eax 寄存器就是 4 字节的) • 返回值都放 eax 寄存器(刚刚算得的就在 eax ,直接返 回) 无分支优化:从语法角度分析0 码力 | 47 页 | 8.45 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 07 深入浅出访存优化可见数据量较小时,实际带宽甚至超过了 理论带宽极限 42672 MB/s ! • 而数据量足够大时, 才回落到正常的带宽 。 • 这是为什么? CPU 内部的高速缓存 • 原来 CPU 的厂商早就意识到了内存延迟高,读写效率低 下的问题。因此他们在 CPU 内部引入了一片极小的存储 器——虽然小,但是读写速度却特别快。这片小而快的 存储器称为缓存( cache )。 • 当 CPU 访问某个地址时,会先查找缓存中是否有对应的 a[w][z][y][x]; • 等价于: • float a[nw * nz * ny * nx]; • a[((w * nz + z) * ny + y) * nx + x]; 因为行列仅限于二维数组(矩阵),对高维数组 可以直接按照他们的 xyz 下标名这样称呼: ZYX 序: (z * ny + y) * nx + x XYZ 序: z + nz * (y + x * ny) 简单来说:哪个索引最连续,就在后面,最不连 普通的循环分块,需要一级缓存大于 blockSize^2 才能 享受一级缓存的带宽,否则就会回落到二级缓存的带宽。 这意味着我不能把 blockSize 调太大,否则在低配的电 脑上效率无法最大化;也不能调太小,否则高配的电脑 明明有更大的缓存,却无法全部发挥作用。 • 而且这样只利用到了一级缓存,要利用二级缓存就需要 分块再分块,并且每个 blockSize 需要调的和二级缓存 大小一致,这样换一台电脑还需要重新调参数。0 码力 | 147 页 | 18.88 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 06 TBB 开启的并行编程之旅(张心欣犯过的错) 利用多线程安全的 concurrent_vector 动态追加数据 基本没有加速,我猜想 concurrent_vector 内部可能 用了简单粗暴的互斥量,只保证了安全,并不保证高 效 加速比: 1.32 倍 并行筛选 2 先推到线程局部( thread-local )的 vector 最后一次性推入到 concurrent_vector 可以避免频繁在 concurrent_vector0 码力 | 116 页 | 15.85 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 05 C++11 开始的多线程编程0 码力 | 79 页 | 14.11 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 04 从汇编角度看编译器优化0 码力 | 108 页 | 9.47 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 03 现代 C++ 进阶:模板元编程0 码力 | 82 页 | 12.15 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 02 现代 C++ 入门:RAII 内存管理的生命周期,否则会出现危险的空悬指 针。比如右边这样: 更智能的指针: shared_ptr • 使用起来很困难的原因,在于 unique_ptr 解决重复释放 的方式是禁止拷贝,这样虽然有效率高的优势,但导致使 用困难,容易犯错等。 • 相比之下, 牺牲效率换来自由度的 shared_ptr 则允许 拷贝,他解决重复释放的方式是通过引用计数: 1. 当一个 shared_ptr 初始化时,将计数器设为0 码力 | 96 页 | 16.28 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 01 学 C++ 从 CMake 学起g-truc/glm - 模仿 GLSL 语法的数学矢量 / 矩阵库(附带一些常用函数,随机数生成等) 4. Tencent/rapidjson - 单纯的 JSON 库,甚至没依赖 STL (可定制性高,工程美学经典) 5. ericniebler/range-v3 - C++20 ranges 库就是受到他启发(完全是头文件组成) 6. fmtlib/fmt - 格式化库,提供 std::format0 码力 | 32 页 | 11.40 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 17 由浅入深学习 map 容器自动默默创建的特性反而是个优点了,如果用了 at() 反而会在插入新键值时莫名 其妙报错。此外 [] 默默创建以后把值初始化为 0 的特性,由于调用者是 = val 赋值,所 以初始化也没用了,反正马上会写入 val 。 浅谈这种精分设计的原因 • 总结,要符合你熟悉的 Python 的 [] 行为,在 C++ 中要根据不同情况选择不同的方法访 问: • 读取用 at() 写入用 [] • 很多同学会困惑,为什么要设计两套, 很多同学会困惑,为什么要设计两套, C++ 他爸是精神分裂症吗? • 恰恰相反, C++ 是中两个函数不论读写都一视同仁: at 总是抛出异常, [] 总是默默创建 。 • 这么看 Python 才是精分:同一个 [] 函数在读取的时候抛出异常,写入的时候又默默创建 。 • 例如:一个同学问小彭老师在干嘛? • 小彭老师说“我在吃答辩。”那么同学认为这个答辩指的是三体动画,小彭老师在看三体动画。 而不会认为小彭老师真的在吃答辩。 而不会认为小彭老师真的在吃答辩。 • 小彭老师说“我在拉答辩。”那么同学认为这个答辩指的是答辩(物理),小彭老师在上厕所。 而不会认为小彭老师在制作三体动画。 • 所以这位同学是人类思维,相当于 Python 的精分 API 。而如果另一个同学是硬核的计算 机思维,相当于 C++ 的一视同仁 API ,他会以为小彭老师真的在吃答辩。 • 这是通常来说,不过万一小彭老师真的这么重口味在吃答辩呢?要怎么传达这个信息?0 码力 | 90 页 | 8.76 MB | 1 年前3
共 28 条
- 1
- 2
- 3













