C++高性能并行编程与优化 - 课件 - 08 CUDA 开启的 GPU 编程
生成两份源码级不同的 代码。 __CUDA_ARCH__ 是个版本号 • 其实 __CUDA_ARCH__ 是一个整数,表 示当前编译所针对的 GPU 的架构版本号 是多少。这里是 520 表示版本号是 5.2.0 ,最后一位始终是 0 不用管,我们 通常简称他的版本号为 52 就行了。 • 这个版本号是编译时指定的版本,不是运 行时检测到的版本。编译器默认就是最老 的 52 ,能兼容所有 GTX900 CMake 设置架构版本号 • 可以用 CMAKE_CUDA_ARCHITECTURES 这个变量 ,设置要针对哪个架构生成 GPU 指令码。 • 小彭老师的显卡是 RTX2080 ,他的版本号是 75 ,因 此最适合他用的指令码版本是 75 。 • 如果不指定,编译器默认的版本号是 52 ,他是针对 GTX900 系列显卡的。 • 不过英伟达的架构版本都是向前兼容的,即版本号为 75 的 RTX2080 也可以运行版本号为 52 的指令码,虽然 不够优化,但是至少能用。也就是要求:编译期指定的 版本 ≤ 运行时显卡的版本。 CMAKE_CUDA_ARCHITECTURES 会自动转换成 --gpu-code 等编 译 flag 版本号不要太新了 • 比如这里设置了 RTX3000 系列的架构版 本号 86 ,在 RTX2080 上就运行不出结 果。 • 最坑的是他不会报错!也不输出任何东西0 码力 | 142 页 | 13.52 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 11 现代 CMake 进阶指南
Fortran :老年人的编程语言 • CUDA :英伟达的 CUDA ( 3.8 版本新增) • OBJC :苹果的 Objective-C ( 3.16 版本新增) • OBJCXX :苹果的 Objective-C++ ( 3.16 版本新增) • ISPC :一种因特尔的自动 SIMD 编程语言( 3.18 版本新增) • 如果不指定 LANGUAGES ,默认为 C 和 CXX 。 https://cmake VERSION x.y.z) 可以把当前项目的版本号设定为 x.y.z 。 • 之后可以通过 PROJECT_VERSION 来获取当前项目的版本号。 • PROJECT_VERSION_MAJOR 获取 x (主版本号)。 • PROJECT_VERSION_MINOR 获取 y (次版本号)。 • PROJECT_VERSION_PATCH 获取 z (补丁版本号)。 一些没什么用,但 CMake 指定最低所需的 CMake 版本 假如你写的 CMakeLists.txt 包含了 3.15 版本才有的特性, 如果用户在老版本上使用,就会出现各种奇怪的错误。 因此最好在第一行加个 cmake_minimum_required(VERSION 3.15) 表示本 CMakeLists.txt 至少需要 CMake 版本 3.15 以上才能运行。 如果用户的 CMake 版本小于 3.15 ,会出现“0 码力 | 166 页 | 6.54 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 16 现代 CMake 模块化项目管理指南
分别在各自的目录下有自己的 CMakeLists.txt 。 二、根项目的 CMakeLists.txt 配置 • 在根项目的 CMakeLists.txt 中,设置了默 认的构建模式,设置了统一的 C++ 版本 等各种选项。然后通过 project 命令初始 化了根项目。 • 随后通过 add_subdirectory 把两个子项 目 pybmain 和 biology 添加进来(顺序 无关紧要),这会调用 Qt5.12.1 ,你设置了环 境变量 Qt5_DIR=/opt/Qt5.12.1 ,后来又搞了个 B 项目依赖 Qt5.10.3 ,但是你忘了你设置过全 局的环境变量指向 5.12.1 了,导致版本冲突。 • 单项目有效(写死在 CMakeLists.txt )虽然方便了你,但是你的 CMakeLists.txt 拿到别人电脑 上(例如你通过 GitHub 开源的),可能你 set(Qt5_DIR D:/Qt5.12.1 。 • 则你会看到他下面有几个子目录: • D:/Qt5.12.1/msvc2017_64 (由 VS2017 编译 64 位版本) • D:/Qt5.12.1/mingw_64 (由 MinGW 编译 64 位版本) • 这几个目录里又分别包含: • D:/Qt5.12.1/msvc2017_64/include/qt/QtCore/qstring.h (实际的头文件,属于0 码力 | 56 页 | 6.87 MB | 1 年前3《深入浅出MFC》2/e
则停留在4.2,程序设计 的主轴没有什么大改变。对于新读者,本书乃全新产品自不待言,您可以从目录中细细琢磨 所有的主题。对于老读者,本书所带给您的,是更精致的制作,以及数章新增的内容(请看 第0章「与前版本之差异」)。 6 最后,我要说,我知道,这本书真的带给许多人很扎实的东西。而我所以愿意不计代价去做 些不求近利的深耕工作,除了这是身为专业作家的责任,以及个人的兴趣之外,是的,我自 己是工程师,我最清楚工程师在学习MFC 新竹1997.04.15 jjhou@ccca.nctu.edu.tw FAX 886-3-5733976 7 第一版序 有一种软件名曰version control,用来记录程序开发过程中的各种版本,以应不时之需,可以 随时反省、检查、回复过去努力的轨迹。 遗憾的是人的大脑没有version control 的能力。学习过程的彷徨犹豫、挫折困顿、在日积月 累的渐悟或x那之间的顿悟之后,彷 讓我們使用同㆒種語言 / 30 本書符號習慣 / 34 磁片內容與安裝 / 34 範例程式說明 / 34 與前版本之差異 / 39 如何聯絡作者 / 40 第㆒篇 勿在浮砂築高臺 - 本書技術前提 / 001 第1章 Win32 程式基本觀念/ 003 Win32 程式開發流程/ 0050 码力 | 1009 页 | 11.08 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 15 C++ 系列课:字符与字符串
find_first_not_of 寻找不在集合内的字符 举一反三: find_last_of 、 find_last_not_of • find 的反向版本是 rfind 。 • find_first_of 的反向版本是 find_last_of 。 • find_first_not_of 的反向版本是 find_last_not_of 。 replace 替换一段子字符串 • replace(pos, len, “str”) &append(const char *s, size_t len); // 只保留前 len 个字符 append 追加一段字符串 • 前面两个是最常用的版本,和 += 也是等价的。 • 后面两个带 len 的版本很奇怪,他们居然是反过来的: • 对于 str 是 string 类型时,会变成保留后半部分。 • 对于 str 是 const char * 类型时,会保留前半部分。 size() - len 个字符 • string &insert(size_t pos, const char *s, size_t len); // 只保留前 len 个字符 • 后两个版本和 append 的情况一样诡异……通常我们只用前两个就行。 • 又是一个就地修改字符串,返回指向自身引用的函数…… insert 插入一段字符串 • 当然,更直观的做法,还是 substr 配合0 码力 | 162 页 | 40.20 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 性能优化之无分支编程 Branchless Programming
• 对于传统分支的做法,为什么排序了的更 高效?既然无分支更高效,我要怎样优化 才能让我的程序变成无分支的呢?那就来 看本期性能优化专题课吧! 分支预测成败对性能的影响 排序为什么对有分支的版本影响那么大 为什么需要流水线 • 为了高效, CPU 的内部其实是一个流水 线 (pipeline) 。流水线的目的是能把原本 串行的一系列指令并行化。为了理解为什 么需要流水线,我们先反过来,假设没有 10 = 42 。 • 对于 x 小于等于 0 的情况, (x > 0) 变成 0 ,相当于 32 + 0 * 10 = 32 + 10 = 32 。 • 这样一来就和原来带 if-else 的版本的效果完全一样,但是取缔了分支,更高效。 • 我称之为“妙用加减乘”优化法。 “ 妙用加减乘”进行无分支优化的通用公式 • 因此我们总结规律得出: • if (cond) return a; ? a : b) // 方法 3 • 三目运算符通常会变成和 if-else 一样的分 支,同样会生成条件跳转指令,理应一样 低效。但是有时候编译器会检测到,可以 帮你自动优化成无分支版本的。 “ 妙用加减乘”进行无分支优化的通用公式 • 我比较喜欢方法 2 ,因为他可以很直观地同样适用于多个分支的情况,例如: • if (x < 0) return 0; • else if0 码力 | 47 页 | 8.45 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 12 从计算机组成原理看 C 语言指针
之外的其他类型则没有区别,可以放心使用。 无符号整数: unsigned 修饰 有符号版本 无符号版本 char unsigned char short unsigned short int unsigned int long unsigned long long long unsigned long long 无符号版本的类型不能表示负数,但是他在正数的表达范围更大。 此外,有的教材采用不同的写法,比如: long int 和 unsigned long 等价 unsigned long long int 和 unsigned long long 等价 有符号整数: signed 修饰 有符号版本 无符号版本 signed char unsigned char signed short unsigned short signed int unsigned int signed long unsigned 14f 才是 float 类 型的常量。 std::abs 函数:自动根据参数类型判断要使用的重载 • 在 C++ 中可以用 std::abs 替代 abs ,这个 在 std 命名空间中的版本是带有多种重载的。 • 建议别用全局的任何函数( C 语言原始的), 始终带上 std:: 前缀( C++ 改良后的)。 • C++ 甚至还有 std::printf , std::memcpy0 码力 | 128 页 | 2.95 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 04 从汇编角度看编译器优化
器处理寄存器翻车( register spill )的压力。 • 因此 64 位比 32 位机器相比,除了内存突破 4GB 限制外,也有一定性能优势。 8 位, 16 位, 32 位, 64 位版本 al, ax, eax, rax r15b, r15w, r15d, r15 AT&T 汇编语言 GCC 编译器所生成的汇编语言就属于这种 返回值:通过 eax 传出 movl $42, %eax 有所谓的“老师”就不肯动动手敲几行命令(写 doc 文件倒挺勤的),在那里传播假知识。 • 在线做编译器实验推荐这个网站: https://godbolt.org/ • 可以实时看源代码编译的结果,还能选不同的编译器版本和 flag 。 • 不要脑内模拟!你误以为某更改对性能有帮助,然而实际测一下时间有一定可能反而变慢 。 第 3 章:指针 编译器傻了吗? 为什么编译器不优化掉 *c = *a ? 指针别名现象( 指针的差是否超过 1024 来判断是否有重叠现 象。 1. 如果没有重叠,则跳转到 SIMD 版本高效运行。 2. 如果重叠,则跳转到标量版本低效运行,但至少不会错。 SIMD 版 标量版 循环中的矢量化:解决指针别名 所以,让我们加上 __restrict 关键字,打消编译器的顾虑! 这下只需要生成一个 SIMD 版本了,没有了运行时判断重叠的焦虑。 SIMD 版 循环中的矢量化: OpenMP0 码力 | 108 页 | 9.47 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 14 C++ 标准库系列课 - 你所不知道的 set 容器
个 size() 函数查询其中元素 个数。 • size_t size() const noexcept; set 的不去重版本: multiset • set 具有自动排序,自动去 重,能高效地查询的特点。 其中去重和数学的集合很像 。 • 还有一种不会去重的版本, 那就是 multiset ,他允许重 复的元素,但仍保留自动排 序,能高效地查询的特点。 • 特点:因为 multiset • pairequal_range(int const &val) const; 删除 multiset 中的等值区间 • erase 只有一个参数的版本 ,会把所有等于 2 的元素删 除。 • 例如: b.erase(2) 等价于 b.erase(b.lower_bound(2), b.upper_bound(2)); • iterator 数导致了随机的顺序。 不同版本的 set 容器比较 类型 去重 有序 查找 插入 vector × × O(n) O(1) ~ O(n) set √ √ O(logn) O(logn) multiset × √ O(logn) O(logn) unordered_set √ × O(1) O(1) unordered_multiset × × O(1) O(1) 不同版本的 set 容器比较 0 码力 | 83 页 | 10.23 MB | 1 年前3现代C++ 教程:高速上手C++11/14/17/20
len_foo() 在运行期实际上是返 回一个常数,这也就导致了非法的产生。 注意,现在大部分编译器其实都带有自身编译优化,很多非法行为在编译器优化的加持下会 变得合法,若需重现编译报错的现象需要使用老版本的编译器。 C++11 提供了 constexpr 让用户显式的声明函数或对象构造函数在编译期会成为常量表达式,这 个关键字明确的告诉编译器应该去验证 len_foo 在编译期就应该是一个常量表达式。 == 1) return 1; if(n == 2) return 1; return fibonacci(n-1) + fibonacci(n-2); } 为此,我们可以写出下面这类简化的版本来使得函数从 C++11 开始即可用: constexpr int fibonacci(const int n) { return n == 1 || n == 2 ? 1 : fibonacci(n-1) std::atomiccounter; 并为整数或浮点数的原子类型提供了基本的数值成员函数,举例来说,包括 fetch_add, fetch_sub 等,同时通过重载方便的提供了对应的 +,- 版本。比如下面的例子: #include #include #include std::atomic count = {0}; 0 码力 | 83 页 | 2.42 MB | 1 年前3
共 23 条
- 1
- 2
- 3