C++高性能并行编程与优化 - 课件 - 15 C++ 系列课:字符与字符串\x1b’ ) • 127 表示 DEL 键(‘ \x7f’ )等 • 0~31 和 127 这些整数,就构成了 ASCII 码中控制字符的部分。 关于控制字符的一个冷知识 • 在 Linux 命令行中启动 cat 。 • 试试按 Ctrl+R , Ctrl+E , Ctrl+C 等一系列 组合键,看到出现了什么? • 可以看到显示的字符变成了 ^R ^E ^C 等… … • 这是 Unix 除此之外,因为 ^D 是“传输终止符”,还可以在控制 台输入 Ctrl+D 来关闭标准输入流,终止正在读取他 的程序。 • 小彭老师常用 Ctrl+D 来快速关闭一个 shell (和输入 exit 命令的效果一样)。 • 以及按 Ctrl+I 的效果其实和 Tab 键一样,按 Ctrl+J 的效果和 Enter 键一样,按 Ctrl+H 的效果和退格键 一样。 • 这是因为 ASCII 表中规定 。因为 ‘ y’ 是数字 部分结束的地方,而 ‘ y’ 是第三个字符,但是计算机数数从 0 开始,所以计 算机说这是第 2 个字符,没毛病。 • 为什么要指针?因为 stoi 的返回值已经是 int 了,要额外的返回值只能这样 。 stoi 的第二参数: &pos stoi 的 &pos 参数实战案例 stoi 抛出异常的情况 • 如果字符串的开头不是数字,则 stoi 会抛出 std::invalid_argument0 码力 | 162 页 | 40.20 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 08 CUDA 开启的 GPU 编程__global__ 用于定义核函数,他在 GPU 上执行,从 CPU 端通过三重尖括号语法调 用,可以有参数,不可以有返回值。 • 而 __device__ 则用于定义设备函数,他在 GPU 上执行,但是从 GPU 上调用的,而 且不需要三重尖括号,和普通函数用起来一 样,可以有参数,有返回值。 • 即: host 可以调用 global ; global 可以调 用 device ; device 多遍,每次针对不同的架构,所以编译会 变得非常慢,生成的可执行文件也会变大 。 • 通常在自己的电脑上用时,同学们只要根 据自己显卡的指定一个版本号即可。 如果 CMakeLists.txt 里没有指定,也可以从命令行参数指定 : 版本号和商品名对照表 • 版本 52 : Quadro M6000 , GeForce 900, GTX-970, GTX-980, GTX Titan X • 版本 53 : 的调用是异步的,返回的时候 ,并不会实际让 GPU 把核函数执行完毕,必须 cudaDeviceSynchronize() 等待他执行完毕(和 线程的 join 很像)。所以,不可能从 kernel 里 通过返回值获取 GPU 数据,因为 kernel 返回 时核函数并没有真正在 GPU 上执行。所以核函 数返回类型必须是 void 。 试图解决:通过指针传递 • 那你可能会想,既然不能返回,那作为指0 码力 | 142 页 | 13.52 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 12 从计算机组成原理看 C 语言指针位的内存地址( 64GB ) 。 • 64 位计算机反而是因为 16777216 TB 太大,内存地址被阉割到了 39 位( 512GB )。 • 64 位计算机:小丑竟是我自己 lscpu 命令查看处理器相关信息 C 语言中的整数类型 C 语言的基础整数类型 类型 Unix 32 位 Unix 64 位 Windows 32 位 Windows 64 位 char 8 位 8 位 8 abs 函数:取出绝对值 • 而刚刚的例子中我们的 abs 返回类型其实始 终是 int ,被送到 printf 里却以 “ %f” 的方式 去读出,所以出错了。 • 因此我们先把 abs 的返回值写入到一个 float 类型变量里试试看,果然有了变化。 • 但还是不对,按理说 abs(-3.14) 应该是 3.14 的,怎么会变成 3.00 呢? abs 函数:取出整数的绝对值 结果显示第一个房间里的字节是 0x78 。 • 可见我们的 x86 架构是小端字节序。 指针的用途举例:用于函数的多个返回值 • 我们知道函数只能有一个返回值,如果需 要返回多个变量,可以用指针作为参数。 • 第一个返回值照常通过 return 返回,第二 个返回值会写入到调用者提供的指针所指 向的变量。 • 这里,指针的作用就在于,通过获取调用 者变量的地址,让被调用函数也能访问到0 码力 | 128 页 | 2.95 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 01 学 C++ 从 CMake 学起编译器,是一个根据源代码生成机器码的程序。 • > g++ main.cpp -o a.out • 该命令会调用编译器程序 g++ ,让他读取 main.cpp 中的字符串(称为源码),并根据 C+ + 标准生成相应的机器指令码,输出到 a.out 这个文件中,(称为可执行文件)。 • > ./a.out • 之后执行该命令,操作系统会读取刚刚生成的可执行文件,从而执行其中编译成机器码, 调用系统提供的 printf 文件越来越多时,一个个调用 g++ 编译链接会变得很麻烦。 • 于是,发明了 make 这个程序,你只需写出不同文件之间的依赖关系,和生成各文件的规则。 • > make a.out • 敲下这个命令,就可以构建出 a.out 这个可执行文件了。 • 和直接用一个脚本写出完整的构建过程相比, make 指明依赖关系的好处: 1. 当更新了 hello.cpp 时只会重新编译 hello.o ,而不需要把 2. 能够自动并行地发起对 hello.cpp 和 main.cpp 的编译,加快编译速度( make -j )。 3. 用通配符批量生成构建规则,避免针对每个 .cpp 和 .o 重复写 g++ 命令( %.o: %.cpp )。 • 但坏处也很明显: 1. make 在 Unix 类系统上是通用的,但在 Windows 则不然。 2. 需要准确地指明每个项目之间的依赖关系,有头文件时特别头疼。0 码力 | 32 页 | 11.40 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 04 从汇编角度看编译器优化架构中的通用寄存器有: • eax, ecx, edx, ebx, esi, edi, esp, ebp • 其中 esp 是堆栈指针寄存器,和函数的调用与返回相关。 • 其中 eax 是用于保存返回值的寄存器。 通用寄存器: 64 位时代 • 64 位 x86 架构中的通用寄存器有: • rax, rcx, rdx, rbx, rsi, rdi, rsp, rbp, r8, r9, r10 8 位, 16 位, 32 位, 64 位版本 al, ax, eax, rax r15b, r15w, r15d, r15 AT&T 汇编语言 GCC 编译器所生成的汇编语言就属于这种 返回值:通过 eax 传出 movl $42, %eax 相当于: eax = 42; 前 6 个参数:分别通过 edi , esi , edx , ecx , r8d , r9d 传入 movl %edi INT_MAX 的情况,推荐始终用 size_t 表示数组大小和索引。 浮点作为参数和返回: xmm 系列寄存器 xmm0 = xmm0 + xmm1 参数分别通过 xmm0 , xmm1 传入。 返回值通过 xmm0 传出。 什么是 xmm 系列寄存器? • xmm 寄存器有 128 位宽。 • 可以容纳 4 个 float ,或 2 个 double 。 • 刚才的案例中只用到了 xmm0 码力 | 108 页 | 9.47 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 11 现代 CMake 进阶指南CMake 。 • 现代 CMake 和古代 CMake 相比,使用 更方便,功能更强大。 为什么要学习现代 CMake ? 现代 CMake : 古代 CMake : 第 0 章:命令行小技巧 传统的 CMake 软件构建 / 安装方式 • mkdir build • cd build • cmake .. • make -j4 • sudo make install --build build 统一了不同平台( Linux 上会调用 make , Windows 上调用 devenv.exe ) • 结论:从现在开始,如果在命令行操作 cmake ,请使用更方便的 -B 和 --build 命令。 // 在源码目录用 -B 直接创建 build 目录并生成 build/Makefile // 自动调用本地的构建系统在 build 里构建,即: make CMAKE_PROJECT_NAME :根项目的项目名 • 详见: https://cmake.org/cmake/help/latest/command/project.html 子模块里也可以用 project 命令,将当前目录作为一个独立的子项目 这样一来 PROJECT_SOURCE_DIR 就会是子模块的源码目录而不是外层了。 这时候 CMake 会认为这个子模块是个独立的项目,会额外做一些初始化。 他的构建目录0 码力 | 166 页 | 6.54 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 性能优化之无分支编程 Branchless Programming无分支优化:从汇编角度分析 • 发生了什么?让我们把源码和汇编逐个对应。 • x 是第一个参数(通过 edi 传入,被存入 rbp 指向的堆 栈) • 比较 x 和 0 的大小( cmp 命令把刚存入堆栈的 x 和 0 比较) • 这里 x > 0 返回的是一个 bool 类型(通过指令 setg al 求出) • bool 类型和 char 一样只占据 1 字节( al 寄存器就 int 类型( movzx 把 1 字节的 al 转换成 4 字节的 eax ,零扩展:高 3 字节 填充零) • 返回类型 int 占据 4 字节( eax 寄存器就是 4 字节的) • 返回值都放 eax 寄存器(刚刚算得的就在 eax ,直接返 回) 无分支优化:从语法角度分析 • 刚刚其实是利用了 C 语言把 bool 类型的 true 当做 1 , false 当做 0 的特性。 总之,对于这种有副作用的函数,或是有可能返回 NaN 的函数,无法“妙用加减乘”优化 。 冷静分析,学会变通 • return x >= 0 ? sqrt(x) : 0; • 但是我们可以变通一下,既然从返回值优化不行,从参数入手怎样?首先已知 sqrt(0) 等 于 0 ,所以我们其实可以转换成: • return x >= 0 ? sqrt(x) : sqrt(0); • 然后再把 sqrt 提取出来得到:0 码力 | 47 页 | 8.45 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 03 现代 C++ 进阶:模板元编程auto 也并非万能,他也有很多限制。 • 因为需要等号右边的类型信息,所以没有 = 单独声明一个 auto 变量是不行的: • 而且,类成员也不可以定义为 auto : 自动类型推导:函数返回值 • 除了可以用于定义变量,还可以用作函数的返回类型: • 使用 auto 以后,会自动被推导为 return 右边的类型。 • 不过也有三点注意事项: 1. 当函数有多条 return 语句时,所有语句的返回类型必须一致,否则 = func(); • 会自动推导为 func() 的返回类型。 • 和下面这种方式等价: • decltype(func()) p = func(); • 在代理模式中,用于完美转发函数返回值。比如: • decltype(auto) at(size_t i) const { • return m_internal_class.at(i); } using :创建类型别名 • 16 字节?提示:一个指针大 小为 8 字节,捕获了 2 个变量。 lambda 表达式:作为返回值 • 既然函数可以作为参数,当然也可以作为 返回值! • 由于 lambda 表达式永远是个匿名类型, 我们需要将 make_twice 的返回类型声明 为 auto 让他自动推导。 作为返回值:出问题了 • 然而当我们试图用 [&] 捕获参数 fac 时, 却出了问题: • fac0 码力 | 82 页 | 12.15 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 14 C++ 标准库系列课 - 你所不知道的 set 容器会返回自增后迭代器 。 • 还有 std::advance 会就地自增作为引 用传入的迭代器,他同样会判断是否支 持 += 来决定要采用哪一种实现。 • 区别: advance 就地修改迭代器,没 有返回值; next 修改迭代器后返回, 不会改变原迭代器。 • advance 相当于 += , next 相当于 + 。 next 和 advance 同样支持负数 • next 的第二个参数 则什么也不会发生,因为 4 已经在集合中了。 • pairinsert(int val); insert 的第二个返回值:表示插入是否成功 • insert 函数的返回值是一个 pair 类型,也就是说他同时 返回了两个值。其中第二个 返回值是 bool 类型,指示 了插入是否成功。 • 若元素在 set 容器中已存有 相同的元素,则插入失败, 这个 bool 值为 false 中不存在,则 插入成功,这个 bool 值为 true 。 • pair insert(int val); insert 的第一个返回值:指向插入 / 现有元素的迭代 器 • 其中第一个返回值是一个迭 代器,分两种情况讨论。 • 当向 set 容器添加元素成功 时,该迭代器指向 set 容器 新添加的元素, bool 类型的 值为 true ; • 如果添加失败,即证明原 0 码力 | 83 页 | 10.23 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 05 C++11 开始的多线程编程运行。虽然 zeno 也用了 opengl ,但他用多进程成功在 opengl 的百般拖后腿下实现了 并发。 第 2 章:异步 异步好帮手: std::async • std::async 接受一个带返回值的 lambda ,自身返回一个 std::future 对象 。 • lambda 的函数体将在另一个线程里执行 。 • 接下来你可以在 main 里面做一些别的事 情, download download 会持续在后台悄悄运行。 • 最后调用 future 的 get() 方法,如果此时 download 还没完成,会等待 download 完成,并获取 download 的返回值。 显示地等待: wait() • 除了 get() 会等待线程执行完毕 外, wait() 也可以等待他执行完,但是不 会返回其值。 等待一段时间: wait_for() • 只要线程没有执行完, set_value() 设置返回值。在主线程里,用 get_future() 获取其 std::future 对象,进一步 get() 可 以等待并获取线程返回值。 std::future 小贴士 • future 为了三五法则,删除了拷贝构造 / 赋 值函数。如果需要浅拷贝,实现共享同一个 future 对象,可以用 std::shared_future 。 • 如果不需要返回值, std::async0 码力 | 79 页 | 14.11 MB | 1 年前3
共 17 条
- 1
- 2













