C++高性能并行编程与优化 - 课件 - 03 现代 C++ 进阶:模板元编程• 因此可以把 debug 改成模板参数,这样 就是编译期常量。编译器会生成两份函数 sumto和 sumto 。前者保 留了调试用的打印语句,后者则完全为性 能优化而可以去掉打印语句。 • 后者其实在编译器看来就是 • if (false) std::cout << ... • 这样显然是会被他自动优化掉的。 模板的应用:编译期分支 • 更进一步,可以用 除了可以用于定义变量,还可以用作函数的返回类型: • 使用 auto 以后,会自动被推导为 return 右边的类型。 • 不过也有三点注意事项: 1. 当函数有多条 return 语句时,所有语句的返回类型必须一致,否则 auto 会报错。 2. 当函数没有 return 语句时, auto 会被推导为 void 。 3. 如果声明和实现分离了,则不能声明为 auto 。比如: auto func(); // 错误 C++ 表示。 lambda 表达式:自动推导返回类型 • 如果 lambda 表达式不通过 -> 指定类型 ,则和 -> auto 等价,自动根据函数体内 的 return 语句决定返回类型,如果没有 return 语句则相当于 -> void 。 lambda 表达式:捕获 main 中的变量 • lambda 函数体中,还可以使用定义他的 main 函数中的变量,只需要把方括号 [] 0 码力 | 82 页 | 12.15 MB | 1 年前3
现代C++ 教程:高速上手C++11/14/17/200。而这依然会产生新的问题,将 NULL 定义 成 0 将导致 C++ 中重载特性发生混乱。考虑下面这两个 foo 函数: void foo(char*); void foo(int); 那么 foo(NULL); 这个语句将会去调用 foo(int),从而导致代码违反直觉。 为了解决这个问题,C++11 引入了 nullptr 关键字,专门用来区分空指针、0。而 nullptr 的类型 为 nullptr_t,能 fibonacci(n-1)+fibonacci(n-2); } 14 2.2 变量及其初始化 第 2 章语言可用性的强化 从 C++14 开始,constexpr 函数可以在内部使用局部变量、循环和分支等简单语句,例如下面的 代码在 C++11 的标准下是不能够通过编译的: constexpr int fibonacci(const int n) { if(n == 1) return 1; if(n fibonacci(n-2); } 2.2 变量及其初始化 if/switch 变量声明强化 在传统 C++ 中,变量的声明虽然能够位于任何位置,甚至于 for 语句内能够声明一个临时变量 int,但始终没有办法在 if 和 switch 语句中声明一个临时的变量。例如: #include#include #include int 0 码力 | 83 页 | 2.42 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 13 C++ STL 容器全解之 vector中哪个运算符是最强的?我觉得是 } • 因为 } 标志着一个语句块的结束,在这里,他 会调用所有身处其中的对象的解构函数。比如 这里的 vector ,他的解构函数会释放动态数组 的内存(即自动 delete )。 • vector 会在退出作用域时释放内存,这时候所 有指向其中元素的指针,包括 data() 都会失效。 因此如果你是在语句块内获取的 data() 指针, 语句块外就无法访问了。 • 可见 data() data() 指针是对 vector 的一种引用,实 际对象生命周期仍由 vector 类本身管理。 vector 容器:延续生命周期 • 如果需要在一个语句块外仍然保持 data() 对 数组的弱引用有效,可以把语句块内的 vector 对象移动到外面的一个 vector 对象 上。 vector 在移动时指针不会失效,例如: • a = move(b) • 则会把 b 变成空数组,0 码力 | 90 页 | 4.93 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 04 从汇编角度看编译器优化想一想,为什么会是这个结果,然后在作 业的 PR 描述中和老师分享你的思考 那改用 array 试试? 那改用手写的 reduce ? 那改小到 10 ?成功了! 结论:代码过于复杂,涉及的语句数量 过多时,编译器会放弃优化! 简单的代码,比什么优化手段都强。 constexpr :强迫编译器在编译期求值 结论:如果发现编译器放弃了自动优化,可以 用 constexpr 函数迫使编译器进行常量折叠! 指令不同,这里只 是拿 GCC 举例,其他编译器请自行查找资料 。 循环中的 if 语句:挪到外面来 乘法模式 加法模式 这个案例中,作者的用意很明显,在 is_mul 为真时 执行 a *= b ,否则执行 a += b 。 然而有 if 分支的循环体是难以 SIMD 矢量化的。 循环中的 if 语句:挪到外面来(续) 编译器看到 is_mul 是一个常量,于是把 if 分支判断挪到了0 码力 | 108 页 | 9.47 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 17 由浅入深学习 map 容器,他会以为小彭老师真的在吃答辩。 • 这是通常来说,不过万一小彭老师真的这么重口味在吃答辩呢?要怎么传达这个信息? C++ 一视同仁的接口就能处理这种罕见的情况,不过 Python 用一些 if 语句套一套一样可以。 深入理解 Python 中 [] 能自动区分是读是写的原理 • 写入要创建元素,而读取则要在元素不存在时出错,确实应该是两个不同的函数。 • 为什么 Python 不用区分读取和写入两个函数?只有统一的 map.end(); ++it) 就一目了然了。 • for 里面第一部分,也就是初始化语句: it = map.begin() 代表从最左节点开始出发。 • 第二部分,也就是判断是否退出的条件: it != map.end() 判断是否抵达最右节点的下一个 。 • 第三部分,也就是每次循环后执行的更新语句: ++it 会让迭代器往下一个节点移动。 • 所以人话就是:从根节点出发,不断向下一个移动,直到没有节点可遍历了。0 码力 | 90 页 | 8.76 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 11 现代 CMake 进阶指南LANGUAGES C CXX) 即可 也可以先设置 LANGUAGES NONE ,之后再调用 enable_language(CXX) 这样可以把 enable_language 放到 if 语句里,从而只有某些选项开启才启用某语言之类的 设置 C++ 标准: CMAKE_CXX_STANDARD 变量 • CMAKE_CXX_STANDARD 是一个整数,表示要用的 C++ 标准。 • cpp 里可以判断 WITH_TBB 宏,找不到 TBB 时退化到串行 for 循环 也可以用 TARGET 判断是否存在 TBB::tbb 这个伪对象,实现同样效果 也可以复合 if 的各种判断语句,例如 NOT TARGET TBB::tbb AND TARGET Eigen3::eigen 表示找得到 TBB 但是找不到 Eigen3 的情况。 第 6 章:输出与变量 在运行 cmake -Wno-dev 关闭 message(FATAL_ERROR “...”) 表示是错误信息,会终止 CMake 的 运行 message(SEND_ERROR “...”) 表示是错误信息,但之后的语句仍继续 执行 message 可以用于打印变量 如果 set 没加引号会怎样?会变成分号分割的列表 set(myvar hello world) 其实等价于: set(myvar “hello;world”)0 码力 | 166 页 | 6.54 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - Zeno 中的现代 C++ 最佳实践 • []{ xxx; yyy; return zzz; }() • 可以在表达式层面里插入一个语句块,本 质上是立即求值的 lambda 表达式(内部 是分号级别,外部是逗号级别)。 • 在函数体内也可以这样: • [&]{ xxx; yyy; return zzz; }() • 来在语句块内使用外部的局部变量。 带有构造函数和解构函数的类 • 实际上,只需定义一个带有构造函数和解构函0 码力 | 54 页 | 3.94 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 02 现代 C++ 入门:RAII 内存管理支都要手动释放所有之前的资源 : RAII :异常安全( exception-safe ) C++ 标准保证当异常发生时,会调用已创建对象的解构函数 。 因此 C++ 中没有(也不需要) finally 语句。 如果此处不关闭,则可等 待稍后垃圾回收时关闭。 虽然最后还是关了,但如 果对时序有要求或对性能 有要求就不能依靠 GC 。 比如 mutex 忘记 unlock 造成死锁等等…… RAII // 显式地拷贝 • v1 = v2 // 默认拷贝 • 注意,以下语句没有任何作用: • std::move(v2) // 不会清空 v2 ,需要清空可以用 v2 = {} 或 v20 码力 | 96 页 | 16.28 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 08 CUDA 开启的 GPU 编程什么是线程组分歧( warp divergence ) • GPU 线程组( warp )中 32 个线程实际是 绑在一起执行的,就像 CPU 的 SIMD 那样。 因此如果出现分支( if )语句时,如果 32 个 cond 中有的为真有的为假,则会导致两个分 支都被执行!不过在 cond 为假的那几个线程 在真分支会避免修改寄存器和访存,产生副作 用。而为了避免会产生额外的开销。因此建议 什么是线程组分歧( warp divergence ) • GPU 线程组( warp )中 32 个线程实际是 绑在一起执行的,就像 CPU 的 SIMD 那样。 因此如果出现分支( if )语句时,如果 32 个 cond 中有的为真有的为假,则会导致两个分 支都被执行!不过在 cond 为假的那几个线程 在真分支会避免修改寄存器和访存,产生副作 用。而为了避免会产生额外的开销。因此建议0 码力 | 142 页 | 13.52 MB | 1 年前3
Hello 算法 1.0.0b4 C++版的是选读章节,内容相对困难。如果你的时间有限,建议可以先跳过。 ‧ 文章中的重要名词会用「」 括号标注,例如「数组 Array」 。请务必记住这些名词,包括英文翻译,以 便后续阅读文献时使用。 ‧ 加粗的文字 表示重点内容或总结性语句,这类文字值得特别关注。 ‧ 专有名词和有特指含义的词句会使用“双引号” 标注,以避免歧义。 ‧ 涉及到编程语言之间不一致的名词,本书均以 Python 为准,例如使用 None 来表示“空”。 回退:撤销选择,恢复到之前的状态 undoChoice(state, choice); } } } 根据题意,当找到值为 7 的节点后应该继续搜索,因此我们需要将记录解之后的 return 语句删除。下图对比 了保留或删除 return 语句的搜索过程。 13. 回溯 hello‑algo.com 252 Figure 13‑4. 保留与删除 return 的搜索过程对比 相比基于前序遍历的代码实现,基于回0 码力 | 343 页 | 27.39 MB | 1 年前3
共 14 条
- 1
- 2













