C++高性能并行编程与优化 - 课件 - 02 现代 C++ 入门:RAII 内存管理deduction / 编 译期参数推断 当代: C++17 引入常用数值算法 未来: C++20 引入区间( ranges ) https://zhuanlan.zhihu.com/p/350068132 未来: C++20 引入模块( module ) https://zhuanlan.zhihu.com/p/350136757 未来: C++20 允许函数参数为自动推断( auto ) BV1h64y197Fd 自定义构造函数:无参数 自定义构造函数:无参数(使用初始化表达式) 为什么需要初始化表达式? 1. 假如类成员为 const 和引用 2. 假如类成员没有无参构造函数 3. 避免重复初始化,更高效 自定义构造函数:多个参数 自定义构造函数:单个参数 自定义构造函数:单个参数(陷阱) 自定义构造函数:单个参数(避免陷阱) 避免陷阱体现在哪里? • 加了 explicit 否则 show(80) 也能编译通过! • 所以,如果你不希望这种隐式转换, • 请给单参数的构造函数加上 explicit 。 • 比如 std::vector 的构造函数 vector(size_t n) 也是 explicit 的。 explicit 对多个参数也起作用! • 多个参数时, explicit 的作用体现在禁止 从一个 {} 表达式初始化。 • 如果你希望在一个返回0 码力 | 96 页 | 16.28 MB | 1 年前3
现代C++ 教程:高速上手C++11/14/17/20类型别名模板 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 变长参数模板 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 折叠表达式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 非类型模板参数推导 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.6 面向对象 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 第 5 章智能指针与内存管理 52 5.1 RAII 与引用计数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .0 码力 | 83 页 | 2.42 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 08 CUDA 开启的 GPU 编程__global__ 用于定义核函数,他在 GPU 上执行,从 CPU 端通过三重尖括号语法调 用,可以有参数,不可以有返回值。 • 而 __device__ 则用于定义设备函数,他在 GPU 上执行,但是从 GPU 上调用的,而 且不需要三重尖括号,和普通函数用起来一 样,可以有参数,有返回值。 • 即: host 可以调用 global ; global 可以调 用 device ; 多遍,每次针对不同的架构,所以编译会 变得非常慢,生成的可执行文件也会变大 。 • 通常在自己的电脑上用时,同学们只要根 据自己显卡的指定一个版本号即可。 如果 CMakeLists.txt 里没有指定,也可以从命令行参数指定 : 版本号和商品名对照表 • 版本 52 : Quadro M6000 , GeForce 900, GTX-970, GTX-980, GTX Titan X • 版本 53 : Tegra 这种奇怪的语法,这里面 的数字代表什么意思呢? • 不妨把 <<<1, 1>>> 改成 <<<1, 3>>> 试试 看。你会看到 Hello, world! 打印了三遍! • 原来,三重尖括号里的第二个参数决定着启动 kernel 时所用 GPU 的线程数量。 • GPU 是为并行而生的,可以开启很大数量的 线程,用于处理大吞吐量的数据。 获取线程编号 • 可以通过 threadIdx.x0 码力 | 142 页 | 13.52 MB | 1 年前3
面向亿行 C/C++ 代码的静态分析系统设计及实践-肖枭有经验的程序员也会犯错 对代码提要求很难监督落实 测试更多是验证功能,很难检测编码缺陷 代码的快速变化使质量更难管 生产质量是责任 靠运维和事后复盘善后够吗? 静态分析工具:半智能的代码分析机器人 静态分析辅助代码评审 自动化工具+流程才是未来 Bug! Thx! Bug! 投入大 KPI不痛不痒 使用主体和责任主体不一致 一步登天想要终极AI 大多数开发人员眼中的静态分析工具 检查逻辑问题好,但耗时长 还挺多误报,想用而不敢用 编译器里的Errors and warnings 自带静态分析的语言如Typescript, Rust IDE里的智能提示 代码混淆和美化 代码交叉索引 Eclipse等IDE中的一键重构 App市场的审核 成功静态分析应用 代码评审中的静态分析 针对该提交 代码片段自 动触发分析 发现问题,拒绝代码合并 用尽量少机器完成一天几千次分析 每次分析10分钟要能结束 控制误报并建立反馈和改进机制 挑战:超大规模代码仓库 项目平均40分钟单机编译时间 项目平均编译代码量超百万行 编译的价值 C/C++代码逻辑受编 译参数深度控制 源代码索引和统计 提升开源静态分析工 具分析质量 提高分析质量 测试项目 glog (谷歌的日志项目), 17个源文件。 cppcheck不带编译分析: 111个分析报告0 码力 | 39 页 | 6.88 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 05 C++11 开始的多线程编程CMake 3.12 及以上(跨平台作业) Git 2.x (作业上传到 GitHub ) CUDA Toolkit 10.0 以上( GPU 专题) 温馨提示: 1. 会用到第二讲( RAII 与智能指针)里的知识 2. 课件中一部分代码是基于 C++17 的 个人认为, C++11 中很多特性, 其实可以看做是为了支持多线程而 顺带引入的……如 chrono 、移动 、 lambda 、 RAII…… C++ 中的多线程: std::thread • C++11 开始,为多线程提供了语言级别的 支持。他用 std::thread 这个类来表示线 程。 • std::thread 构造函数的参数可以是任意 lambda 表达式。 • 当那个线程启动时,就会执行这个 lambda 里的内容。 • 这样就可以一边和用户交互,一边在另一 个线程里慢吞吞下载文件了。 错误:找不到符号 如果线程在指定的时间内执行完毕,则认为等 待成功,返回 future_status::ready 。 • 同理还有 wait_until() 其参数是一个时间点。 另一种用法: std::launch::deferred 做参数 • std::async 的第一个参数可以设为 std::launch::deferred ,这时不会创建一个 线程来执行,他只会把 lambda 函数体内 的运算推迟到0 码力 | 79 页 | 14.11 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 15 C++ 系列课:字符与字符串必须告诉他是字符串( %s )还是整数( %d )还是 字符( %c ),必须和右边的参数一致,初学者容易搞错 。 • 而且即使搞错了也能正常编译通过(一些高级的编译器会 给出警告),但是运行结果不对,或者还有可能崩溃。 泛型的 iostream 应运而生 • 得益于 C++ 的重载技术, cout 不用你手动指定类型,他 会自动识别参数的类型,帮你调用相应的格式化函数。 c_str 和 data 的区别 data() 只保证返回长度为 s.size() 的连续内存的首地址指针,不保证 0 结 尾。 • 把 C++ 的 string 作为参数传入像 printf 这种 C 语言函数时,需要用 s.c_str() 。 • 如果只是在 C++ 函数之间传参数,直接用 string 或 string const & 即可。 • void legacy_c(const char *name); stoi(“42”) 等价,都会返回 42 。后面的 “ yuan” 会被 stoi 略去。 • 那如何才能知道哪些字符被 stoi 略去了呢?或者说,数字部分从哪里结束? • 这就要用到 stoi 的第二参数,他是一个 size_t 的指针,默认为 nullptr 。 • 若该指针不为 nullptr ,则会往他指向的变量写入一个整数,表示数字部分结 束的那个字符所在的位置,很绕口?来看个例子就懂了。0 码力 | 162 页 | 40.20 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 06 TBB 开启的并行编程之旅保证结果是准确的,同时不浪费太多时间 。 运行结果 刚才的 BENCHMARK_MAIN 自动生成了一个 main 函数 ,从而生成一个可执行文件供你运行。运行后会得到测试 的结果打印在终端上。 命令行参数 他还接受一些命令行参数来控制测试的输出格式为 csv 等等,你可以调用 --help 查看更多用法。 CMake 中使用: find_package CMake 中使用:作为子模块 这个什么“勾勾”公司非要默认开启 推荐通过迭代器顺序访问 • 最好的方式是用 begin() 和 end() 的迭代 器区间,按顺序访问。 parallel_for 也支持迭代器 • 冷知识: tbb::blocked_range 的参数不一 定是 size_t ,也可以是迭代器表示的区间 。 • 这样 lambda 体内 r 的 begin 和 end 也会返回 tbb::concurrent_vector 的迭代 器类型。 的数据就是在二级缓存里,比调度到让 t2 算需要进入三级缓存更高效。 而当 t2 的队列比较空时,又会让 t1 继续算 d2 的 s2 ,这样可以避免 t2 闲置浪费时间。总之就是会自动负载均衡非常智能,完全无需操心内部 细节。 流水线并行: filter 参数 • serial_in_order 表示当前步骤只允许串行执行,且 执行的顺序必须一致。 • serial_out_of_order 表示只允许串行执行,但是顺0 码力 | 116 页 | 15.85 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 07 深入浅出访存优化字节分块的效果拔群,但还是比顺 序访问慢一些,为什么?明明没有浪费带宽了? 缓存行预取技术:吃着一碗饭的同时,先喊妈妈烧下一碗饭 • 其实,当程序顺序访问 a[0], a[1] 时, CPU 会智能地预测到你接下来可 能会读取 a[2] ,于是会提前给缓存发送一个读取指令,让他读取 a[2] 、 a[3] 。缓存在后台默默读取数据的同时, CPU 自己在继续处理 a[0] 的数据。这样等 现象。 手动预取: _mm_prefetch • 对于不得不随机访问很小一块的情况,还可以通过 _mm_prefetch 指令手动预取一个缓存行。 • 这里第一个参数是要预取的地址(最好对齐到缓存 行),第二个参数 _MM_HINT_T0 代表预取数据 到一级缓存, _MM_HINT_T1 代表只取到二级缓 存, _MM_HINT_T2 代表三级缓存; _MM_HINT_NTA 则是预取到非临时缓冲结构中, 写入,他能够绕开缓存,将一个 4 字节的写入操 作,挂起到临时队列,等凑满 64 字节后,直接写 入内存,从而完全避免读的带宽。 • 可惜这货只支持 int 做参数,要用 float 还得转换 一下指针类型, bitcast 一下参数。 stream 的特点:不会读到缓存里 • 因为 _mm_stream_si32 会绕开缓存,直 接把数据写到内存,之后读取的话,反而 需要等待 stream0 码力 | 147 页 | 18.88 MB | 1 年前3
Hello 算法 1.1.0 C++ 版10 倍!” ——李沐,亚马逊资深首席科学家 计算机的出现给世界带来了巨大变革,它凭借高速的计算能力和出色的可编程性,成为了执行算法与处理数 据的理想媒介。无论是电子游戏的逼真画面、自动驾驶的智能决策,还是 AlphaGo 的精彩棋局、ChatGPT 的自然交互,这些应用都是算法在计算机上的精妙演绎。 事实上,在计算机问世之前,算法和数据结构就已经存在于世界的各个角落。早期的算法相对简单,例如古 1. 递:程序不断深入地调用自身,通常传入更小或更简化的参数,直到达到“终止条件”。 2. 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。 而从实现的角度看,递归代码主要包含三个要素。 1. 终止条件:用于决定什么时候由“递”转“归”。 2. 递归调用:对应“递”,函数调用自身,通常输入更小或更简化的参数。 3. 返回结果:对应“归”,将当前递归层级的结果返回至上一层。 尾递归:递归调用是函数返回前的最后一个操作,这意味着函数返回到上一层级后,无须继续执行其他 操作,因此系统无须保存上一层函数的上下文。 以计算 1 + 2 + ⋯ + ? 为例,我们可以将结果变量 res 设为函数参数,从而实现尾递归: // === File: recursion.cpp === /* 尾递归 */ int tailRecur(int n, int res) { // 终止条件 if (n0 码力 | 379 页 | 18.47 MB | 1 年前3
Hello 算法 1.2.0 简体中文 C++ 版10 倍!” ——李沐,亚马逊资深首席科学家 计算机的出现给世界带来了巨大变革,它凭借高速的计算能力和出色的可编程性,成为了执行算法与处理数 据的理想媒介。无论是电子游戏的逼真画面、自动驾驶的智能决策,还是 AlphaGo 的精彩棋局、ChatGPT 的自然交互,这些应用都是算法在计算机上的精妙演绎。 事实上,在计算机问世之前,算法和数据结构就已经存在于世界的各个角落。早期的算法相对简单,例如古 1. 递:程序不断深入地调用自身,通常传入更小或更简化的参数,直到达到“终止条件”。 2. 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。 而从实现的角度看,递归代码主要包含三个要素。 1. 终止条件:用于决定什么时候由“递”转“归”。 2. 递归调用:对应“递”,函数调用自身,通常输入更小或更简化的参数。 3. 返回结果:对应“归”,将当前递归层级的结果返回至上一层。 尾递归:递归调用是函数返回前的最后一个操作,这意味着函数返回到上一层级后,无须继续执行其他 操作,因此系统无须保存上一层函数的上下文。 以计算 1 + 2 + ⋯ + ? 为例,我们可以将结果变量 res 设为函数参数,从而实现尾递归: // === File: recursion.cpp === /* 尾递归 */ int tailRecur(int n, int res) { // 终止条件 if (n0 码力 | 379 页 | 18.48 MB | 10 月前3
共 28 条
- 1
- 2
- 3













