C++高性能并行编程与优化 - 课件 - 15 C++ 系列课:字符与字符串没什么好神秘的,他就是一个普通的字符。 • 仅仅只是 printf 和 scanf 这些特定的函数会对 % 特殊处理而已。 • 而 \ 比较厉害,他是编译器内部专门为他“开了个后门”。 • 编译器检测到字符串中出现 \ 就会把下一个字符特殊处理。 • 而 % ,编译器并不会特殊处理 % ,是 printf 函数内部在运行时处理了 % 的下一个字符。 • % 就像你和同学随手“拉钩”定下的约定,这是 的返回值已经是 int 了,要额外的返回值只能这样 。 stoi 的第二参数: &pos stoi 的 &pos 参数实战案例 stoi 抛出异常的情况 • 如果字符串的开头不是数字,则 stoi 会抛出 std::invalid_argument 异常,可以用 catch 捕 获。 • 但是开头可以有空格,例如 stoi(“ 42yuan”) 可以正常得到 42 ,但 stoi(“my42yuan”) 个字符。 • 区别在于 at 如果遇到 i 越界的情况,也就是检测到 i ≥ s.size() 时,会抛出 std::out_of_range 异常终止程序。使用 gdb 等调试 器就可以在出这个异常的时候暂停,帮你调试错误 ( BV1kP4y1K7Eo )。也可以从外部函数 catch 住这个异常(以 后再讲)。 • 而 [] 则不会抛出异常,他只是简单地给字符串的首地址指针和 i 做个加法运算,得到新的指针并解引用。如果你给的0 码力 | 162 页 | 40.20 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 03 现代 C++ 进阶:模板元编程value() : 3 optional : value() 会检测是否为空,空则抛出异常 • 当 ret 没有值时(即 nullopt ), ret.value() 会抛出一个异 常,类型为 std::bad_optional_access 。 optional : operator*() 不检测是否为空,不会抛出异常 • 除了 ret.value() 之外还可以用 *ret 获 获 取 optional 容器中的值,不过他不会 去检测是否 has_value() ,也不会抛出 异常,更加高效,但是要注意安全。 • 请确保在 has_value() 的分支内使用 *ret ,否则就是不安全的。 • 如果 optional 里的类型是结构体,则 也可以用 ret->xxx 来访问该结构体的 属性。 optional : operator bool() 和 has_value() 。 variant :获取容器中的数据用 std::get • 要获取某个类型的值,比如要获取 int 用 std::get。如果当前 variant 里不是 这个类型,就会抛出异常: std::bad_variant_access 。 • 此外,还可以通过 std::get<0> 获取 variant 列表中第 0 个类型,这个例子中 和 std::get 0 码力 | 82 页 | 12.15 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 13 C++ STL 容器全解之 vector会覆盖掉别的变量导致程序行为异常,或是访 问到操作系统未映射的区域导致奔溃。 • int &operator[](size_t i) noexcept; • int const &operator[](size_t i) const noexcept; vector 容器: at • 为了防止不小心越界,可以用 a.at(i) 替代 a[i] , at 函数会检测索引 i 是否越界,如果他 是否越界,如果他 发现索引 i >= a.size() 则会抛出异常 std::out_of_range 让程序提前终止(或者被 try-catch 捕获),配合任意一款调试器,就可 以很快速地定位到出错点。 • 不过 at 需要额外检测下标是否越界,虽然更安 全方便调试,但和 [] 相比有一定性能损失。 • int &at(size_t i); • int const &at(size_t0 码力 | 90 页 | 4.93 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 02 现代 C++ 入门:RAII 内存管理解构函数是显式的,离开作用域自动销毁,毫不含 糊(有好处也有坏处,对高性能计算而言利大于 弊) 如果没有解构函数,则每个带有返回的分 支都要手动释放所有之前的资源 : RAII :异常安全( exception-safe ) C++ 标准保证当异常发生时,会调用已创建对象的解构函数 。 因此 C++ 中没有(也不需要) finally 语句。 如果此处不关闭,则可等 待稍后垃圾回收时关闭。 虽然最后还是关了,但如 如果需要允许用户拷贝你的 Vector 类对象 ,我们还是需要实现一下的。 • 发现了吗?其实不管是 size/resize 这样的 get/set 模式也好;自定义的拷贝构造函数 也好; RAII 保证异常安全也好;都是在为 面向对象思想的“封装:不变性”服务。 • 即:保证任何单个操作前后,对象都是处于 正确的状态,从而避免程序读到错误数据 (如空悬指针)的情况。 三五法则:拷贝赋值函数 lock() 也会返回 nullptr 。 可以把 C * 理解为 unique_ptr 的弱引用 。 weak_ptr 理解为 shared_ptr 的弱引用。 但 weak_ptr 能提供失效检测,更安全。 智能指针:作为类的成员变量 • 可以在类中使用智能指针作为成员变量。需要根据实际情况 (主要是看所有权),判断要用哪一种智能指针: 1. unique_ptr :当该对象仅仅属于我时。比如:父窗口中指向子0 码力 | 96 页 | 16.28 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 08 CUDA 开启的 GPU 编程GPU 的架构版本号 是多少。这里是 520 表示版本号是 5.2.0 ,最后一位始终是 0 不用管,我们 通常简称他的版本号为 52 就行了。 • 这个版本号是编译时指定的版本,不是运 行时检测到的版本。编译器默认就是最老 的 52 ,能兼容所有 GTX900 以上显卡。 https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index ret 的值并没有被改写成 功。 分析返回的错误代码 • CUDA 的函数,如 cudaDeviceSynchronize() 。 • 他们出错时,并不会直接终止程序,也不会抛出 C++ 的异常,而是返回一个错误代码,告诉你出的具体什么 错误,这是出于通用性考虑。 • 这个错误代码的类型是 cudaError_t ,其实就是个 enum 类型,相当于 int 。 • 可以通过 cudaGetErrorName0 码力 | 142 页 | 13.52 MB | 1 年前3
《深入浅出MFC》2/emultithreading, console programming。 ■ C++ 重要技术:类别与对象、this 指针与继承、静态成员、虚拟函数与多态、 深入淺出 MFC 28 模板(template)类别、异常处理(exception handling)。 ■ MFC 六大技术之简化仿真(Console 程序) 第二篇【欲善工事先利其器】提供给对Visual C++ 整合环境全然陌生的朋友一个导引。 pSales = &aSales; pWager = &aSales; // 以「基础类别之指针」指向「衍生类别之对象」 pWager->setSales(800.0); // 错误(编译器会检测出来), // 因为CWage 并没有定义setSales 函数。 pSales->setSales(800.0); // 正确,调用CSales::setSales 函数。 虽然pSales 是如果发 生异常情况(exception),而程序设计了异常情况处理程序(exception handling), 控制权就会截弯取直地「直接跳」到你所设定的处理例程去,这时候堆栈中的C++ 对象有没有机会被析构?这得视编译器而定。如果编译器有支持unwinding 功能,就 会在一个异常情况发生时,将堆栈中的所有对象都析构掉。 关于异常情况(exception)及异常处理(exception0 码力 | 1009 页 | 11.08 MB | 1 年前3
现代C++ 教程:高速上手C++11/14/17/20const char * 或者 auto。 char *str = "hello world!"; // 将出现弃用警告 8 1.2 与 C 的兼容性 第 1 章迈向现代 C++ • C++98 异常说明、unexpected_handler、set_unexpected() 等相关特性被弃用,应该使用 noexcept。 • auto_ptr 被弃用,应使用 unique_ptr。 • register 名一个函数的情况下去使用 的。这样的场景其实有很多很多,所以匿名函数几乎是现代编程语言的标配。 基础 Lambda 表达式的基本语法如下: [捕获列表](参数列表) mutable(可选) 异常属性 -> 返回类型 { // 函数体 } 上面的语法规则除了 [捕获列表] 内的东西外,其他部分都很好理解,只是一般函数的函数名被略 去,返回值使用了一个 -> 的形式进行(我们在上一节前面的尾返回类型已经提到过这种写法了)。 可以进行解锁。但是在实际编写代码的过程中,最好不去直 接调用成员函数,因为调用成员函数就需要在每个临界区的出口处调用 unlock(),当然,还包括异常。 这时候 C++11 还为互斥量提供了一个 RAII 语法的模板类 std::lock_guard。RAII 在不失代码简洁性 的同时,很好的保证了代码的异常安全性。 在 RAII 用法下,对于临界区的互斥量的创建只需要在作用域的开始部分,例如: #include0 码力 | 83 页 | 2.42 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 17 由浅入深学习 map 容器通过实战案例来学习 STL 算法库 7. C++ 标准输入输出流 & 字符串格式化 8. traits 技术,用户自定义迭代器与算法 9. allocator ,内存管理与对象生命周期 10. C++ 异常处理机制的前世今生 我们都要认真鞋习哦 我们都要认真鞋习哦 第一章:读取与写入 我负责监督你鞋习 ! 我负责监督你鞋习 ! map 查找元素的两个接口 • map 提供了两个查找元素的接口,一曰 key” 元素 • val = m.at(“key”); // 读取键值为 “ key” 的元素,如果不存在,抛出异常 • 所以 [] 和 at() 唯一的区别,在于键值不存在这一特殊情况的处理方式。 • [] 默默创建。 • at() 抛出异常。 读取 map 元素 • mapm; • val = m[“key”]; • 读取键值为 “ key” it->second; 读取 map 元素 • map m; • val = m.at(“key”); • 读取键值为 “ key” 的元素,如果不存在,那就抛出异常,导致程序异常退出。等价于: • it = m.find(“key”); • if (it == m.end()) { • throw std::out_of_range(“ 找不到键值” ); 0 码力 | 90 页 | 8.76 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 11 现代 CMake 进阶指南这个目标,即安 装 -D 选项:指定配置变量(又称缓存变量) • 可见 CMake 项目的构建分为两步: • 第一步是 cmake -B build ,称为配置阶段( configure ),这时只检测环境并生成构建规则 • 会在 build 目录下生成本地构建系统能识别的项目文件( Makefile 或是 .sln ) • 第二步是 cmake --build build ,称为构建阶段( build 则是专为性能优化的构建系统,他和 CMake 结合都是行业标准了。 Ninja 和 Makefile 简单的对比 性能上: Ninja > Makefile > MSBuild Makefile 启动时会把每个文件都检测一遍, 浪费很多时间。特别是有很多文件,但是实 际需要构建的只有一小部分,从而是 I/O Bound 的时候, Ninja 的速度提升就很明 显。 然而某些专利公司的 CUDA toolkit 在 CMAKE_CXX_STANDARD_REQUIRED 是 BOOL 类型,可以为 ON 或 OFF ,默认 OFF 。 • 他表示是否一定要支持你指定的 C++ 标准:如果为 OFF 则 CMake 检测到编译器不支 持 C++17 时不报错,而是默默调低到 C++14 给你用;为 ON 则发现不支持报错,更安 全。 https://crascit.com/2015/03/28/enabling-cxx11-in-cmake/0 码力 | 166 页 | 6.54 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 性能优化之无分支编程 Branchless Programmingset 系列指令有 setle , setge , setl 等等。 • 冷知识: 32 位时代 cmov 系列曾经是 x86 的一个拓展特性(像 sse 一样),使用前需 要先用 cpuid 指令检测是否支持,如果在不支持 cmov 的 CPU 上使用会产生 SIGILL 错误。不过现在 64 位的 x86 CPU 都保证自带了 cmov 和 sse 拓展,所以不需要手动 开启什么开关编译器就会自动生成利用 还有一种“摆烂”的做法: • (cond ? a : b) // 方法 3 • 三目运算符通常会变成和 if-else 一样的分 支,同样会生成条件跳转指令,理应一样 低效。但是有时候编译器会检测到,可以 帮你自动优化成无分支版本的。 “ 妙用加减乘”进行无分支优化的通用公式 • 我比较喜欢方法 2 ,因为他可以很直观地同样适用于多个分支的情况,例如: • if (x < 0) return if-else ,编译器往往会自动检测到可以优化,帮你 应用“妙用加减乘”了,无法体现手动优化的意义。 不同写法的性能测试 • 我们照常编写了测试用例,禁止内联优化,同样生成 10^7 个随机数( -512 到 512 区间)。 • 至于为什么采用需要三个分支的 clamp 做测试? • 优化级别在 -O1 以上时,对于只有两个分支的 if- else ,编译器往往会自动检测到可以优化,帮你应用0 码力 | 47 页 | 8.45 MB | 1 年前3
共 25 条
- 1
- 2
- 3













