C++高性能并行编程与优化 - 课件 - 07 深入浅出访存优化)。 • 并行能减轻计算瓶颈,但不减轻内存瓶颈,故后者是优化的重点 。 浮点加法的计算量 • 冷知识:并行地给浮点数组每个元素做一次加法反而更慢。 • 因为一次浮点加法的计算量和访存的超高延迟相比实在太少了。 • 计算太简单,数据量又大,并行只带来了多线程调度的额外开销 。 • 小彭老师经验公式: 1 次浮点读写 ≈ 8 次浮点加法 • 如果矢量化成功( SSE ): 1 次浮点读写 cycle , 符合小彭老师的经验公式。 • “right” 和“ wrong” 指的是分支预测是否成功。 多少计算量才算多? • 看右边的 func ,够复杂了吧?也只是勉勉强强超过一 点内存的延迟了,但在 6 个物理核心上并行加速后, 还是变成 mem-bound 了。 • 加速比: 1.36 倍 • 应该达到 6 倍(物理核心数量)才算理想加速比。 加速曲线 • funcA 用了 可见数据量较小时,实际带宽甚至超过了 理论带宽极限 42672 MB/s ! • 而数据量足够大时, 才回落到正常的带宽 。 • 这是为什么? CPU 内部的高速缓存 • 原来 CPU 的厂商早就意识到了内存延迟高,读写效率低 下的问题。因此他们在 CPU 内部引入了一片极小的存储 器——虽然小,但是读写速度却特别快。这片小而快的 存储器称为缓存( cache )。 • 当 CPU 访问某个地址时,会先查找缓存中是否有对应的0 码力 | 147 页 | 18.88 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 08 CUDA 开启的 GPU 编程板块中的线程数量过少:延迟隐藏( latency hiding )失效 • 我们说过,每个 SM 一次只能执行板块中的一个线程组( warp ),也就是 32 个线程。 • 而当线程组陷入内存等待时,可以切换到另一个线程,继续计算,这样一个 warp 的内存 延迟就被另一个 warp 的计算延迟给隐藏起来了。因此,如果线程数量太少的话,就无法 通过在多个 warp 之间调度来隐藏内存等待的延迟,从而低效。 共享内存区块冲突( bank conflict ) GPU 优化手法总结 • 线程组分歧( wrap divergence ):尽量保证 32 个线程都进同样的分支,否则两个分支都会执行 。 • 延迟隐藏( latency hiding ):需要有足够的 blockDim 供 SM 在陷入内存等待时调度到其他线程 组。 • 寄存器打翻( register spill ):如果核函数用到很多局部变量(寄存器),则0 码力 | 142 页 | 13.52 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 03 现代 C++ 进阶:模板元编程列出所有模板参数的排列组合 ,违背了开 - 闭原则。 模板的惰性:延迟编译 • 要证明模板的惰性,只需看这个例子: • 要是编译器哪怕细看了一眼:字符串怎么可能被写入呢?肯定是会出错的。 • 但是却没有出错,这是因为模板没有被调用,所以不会被实际编译! • 而只有当 main 调用了这个函数,才会被编译,才会报错! • 用一个假模板实现延迟编译的技术,可以加快编译的速度,用于代理模式等。 模板函数:一个例子0 码力 | 82 页 | 12.15 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 10 从稀疏数据结构到量化数据类型)。 • 右边就是一个很好的例子。 使用 int64_t :每个占据 8 字节 • 如果用更大的数据类型,用时会直接提升两倍! • 这是因为 i % 2 的计算时间,完全隐藏在内存 的超高延迟里了。 • 可见,当数据量足够大,计算量却不多时,读写 数据量的大小唯一决定着你的性能。 • 特别是并行以后,计算量可以被并行加速,而访 存却不行。 使用 int8_t :每个占据 1 字节0 码力 | 102 页 | 9.50 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 06 TBB 开启的并行编程之旅域按照常规的两层循环访问以便矢量化,块外 部大区域则以类似 Z 字型的曲线遍历,这样 能保证每次访问的数据在地址上比较靠近,并 且都是最近访问过的,从而已经在缓存里可以 直接读写,避免了从主内存读写的超高延迟。 • 下次课会进一步深入探讨访存优化,详细剖析 这个案例,那么下周六 14 点敬请期待。 第 6 章:并发容器 std::vector 扩容时会移动元素 • std::vector 内部存储了一个指针,指向一段容量0 码力 | 116 页 | 15.85 MB | 1 年前3
《深入浅出MFC》2/ewMinute, st.wSecond); SetDlgItemText (_hWndDlg, IDE_TIMER, str); Sleep (1000); // 延迟一秒。 } } 46 以_beginthreadex 取代CreateThread 别忘了Windows 程序除了调用Win32 API,通常也很难避免调用任何一个C runtime 函 *(ThreadArg), rect.bottom-(dwThreadHits/10), *(ThreadArg)+0x40, rect.bottom); // 延迟. . . if (_uDelayType == SLEEPDELAY) Sleep(10); else if (_uDelayType == FORLOOPDELAY) leep(10),意思是先睡10 个 毫秒,之后再醒来;这段期间,CPU 可以给别人使用。第二种方式是以空循环30000 次 做延迟;空循环期间CPU 不能给别人使用(事实上CPU 正忙碌于那30000 次空转)。 52 图1-9 是执行画面。注意,先选择延迟方式("for loop delay" 或"sleep delay"),再按下 【Resume Thread】。如果你选择¡ §for0 码力 | 1009 页 | 11.08 MB | 1 年前3
现代C++ 教程:高速上手C++11/14/17/20在上面的情况中,如果我们假设 x 的初始值为 0,则 T2 中四次 x.read() 结果可能但不限于以下 情况: 3 4 4 4 // x 的写操作被很快观察到 0 3 3 4 // x 的写操作被观察到的时间存在一定延迟 0 0 0 4 // 最后一次读操作读到了 x 的最终值,但此前的变化并未观察到 0 0 0 0 // 在当前时间段内 x 的写操作均未被观察到, // 但未来某个时间点上一定能观察到 x 为0 码力 | 83 页 | 2.42 MB | 1 年前3
Hello 算法 1.1.0 C++ 版的成本是硬盘的几十倍,这使得它难以在消费者市场普及。 ‧ 缓存的大容量和高速度难以兼得。随着 L1、L2、L3 缓存的容量逐步增大,其物理尺寸会变大,与 CPU 核心之间的物理距离会变远,从而导致数据传输时间增加,元素访问延迟变高。在当前技术下,多层级 的缓存结构是容量、速度和成本之间的最佳平衡点。 图 4‑9 计算机存储系统 Note 计算机的存储层次结构体现了速度、容量和成本三者之间的精妙平衡。实际上,这种权衡普遍存在于0 码力 | 379 页 | 18.47 MB | 1 年前3
Hello 算法 1.0.0 C++版的成本是硬盘的几十倍,这使得它难以在消费者市场普及。 ‧ 缓存的大容量和高速度难以兼得。随着 L1、L2、L3 缓存的容量逐步增大,其物理尺寸会变大,与 CPU 核心之间的物理距离会变远,从而导致数据传输时间增加,元素访问延迟变高。在当前技术下,多层级 的缓存结构是容量、速度和成本之间的最佳平衡点。 图 4‑9 计算机存储系统 � 计算机的存储层次结构体现了速度、容量和成本三者之间的精妙平衡。实际上,这种权衡普遍0 码力 | 378 页 | 17.59 MB | 1 年前3
Hello 算法 1.2.0 简体中文 C++ 版的成本是硬盘的几十倍,这使得它难以在消费者市场普及。 ‧ 缓存的大容量和高速度难以兼得。随着 L1、L2、L3 缓存的容量逐步增大,其物理尺寸会变大,与 CPU 核心之间的物理距离会变远,从而导致数据传输时间增加,元素访问延迟变高。在当前技术下,多层级 的缓存结构是容量、速度和成本之间的最佳平衡点。 图 4‑9 计算机存储系统 Tip 计算机的存储层次结构体现了速度、容量和成本三者之间的精妙平衡。实际上,这种权衡普遍存在于0 码力 | 379 页 | 18.48 MB | 10 月前3
共 10 条
- 1













