C++高性能并行编程与优化 - 课件 - 01 学 C++ 从 CMake 学起make -j )。 3. 用通配符批量生成构建规则,避免针对每个 .cpp 和 .o 重复写 g++ 命令( %.o: %.cpp )。 • 但坏处也很明显: 1. make 在 Unix 类系统上是通用的,但在 Windows 则不然。 2. 需要准确地指明每个项目之间的依赖关系,有头文件时特别头疼。 3. make 的语法非常简单,不像 shell 或 python 可以做很多判断等。 规则,为 g++ 准备的参数可能对 MSVC 不适用。 构建系统的构建系统( CMake ) • 为了解决 make 的以上问题,跨平台的 CMake 应运而生! • make 在 Unix 类系统上是通用的,但在 Windows 则不然。 • 只需要写一份 CMakeLists.txt ,他就能够在调用时生成当前系统所支持的构建系统。 • 需要准确地指明每个项目之间的依赖关系,有头文件时特别头疼。 这个名字是一个函数,不是一个变量或者类的名字:这样当我写下 hello() 的时候,他知道我是想调用 hello 这个函数,而不是创建一个叫 hello 的类的对 象。 • 其实, C++ 是一种强烈依赖上下文信息的编程语言,举个例子: • vector < MyClass > a; // 声明一个由 MyClass 组成的数组 • 如果编译器不知道 vector 是个模板类,那他完全可以把 vector0 码力 | 32 页 | 11.40 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 10 从稀疏数据结构到量化数据类型unordered_map 、空间稀 疏网格、位运算、浮点的二进制格式、内存带宽优 化 面向人群:图形学、 CFD 仿真、深度学习编程人 员 第 0 章:稀疏矩阵 稠密数组存储矩阵 用 foreach 包装一下枚举的过程 改用 map 来存储 分离 read/write/create 三种访问模式 foreach 直接给出当前坐标指向的值 改用 unordered_map 来存储 unordered_map 量,目的是为了防止用户犯错。 • 因此,想要追求稀疏的我们,最好想办法绕 开 glibc ,直接和操作系统打交道。 解决:绕开 glibc ,直接调用操作系统的 mmap 分配 • 那么就要用到 Unix 类操作系统的 mmap 函数,他位于 sys/mman.h 头文件里。 • Windows 可以用 VirtualAllocateEx 之类。 • mmap 出来的起始地址保证是对齐到 4KB 的,读写访问其0 码力 | 102 页 | 9.50 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - Zeno 中的现代 C++ 最佳实践 o2 • Zeno 1.0 所在的分支: https://github.com/zenustech/zeno/ Zeno 中的基本类型 • IObject 一切对象的公共基类。 • INode 一切节点的公共基类。 多态的经典案例 • IObject 具有一个 eatFood 纯虚函数,而 CatObject 和 DogObject 继承自 IObject ,他 们实现了 eatFood m_catFood 。所以 这里的解构函数也是多态的,他根据类型的不同 调用不同派生类的解构函数。 多态用于设计模式之“模板模式” • 这样之后如果有一个任务是要基于 eatFood 做文章,比如要重复 eatFood 两遍。 • 就可以封装到一个函数 eatTwice 里,这个函数只需接受他们共同的基类 IObject 作为参数,然后调 用 eatFood 这个虚函数来做事(而不是直接操作具体的猫和狗本身)。 指针所指向的类型,也就是当前所在类的类型 。 • 宏的缺点是他不遵守命名空间的规则,宏的名 字是全局可见的,不符合 C++ 的高大尚封装思 想。 • 宏: IOBJECT_DEFINE_CLONE • 高大尚 C++ 封装: zeno::IObject::clone() 如何批量定义 clone 函数? • 另一种方法是定义一个 IObjectClone 模板 类。其模板参数是他的派生类 Derived0 码力 | 54 页 | 3.94 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 02 现代 C++ 入门:RAII 内存管理C++17 标准,有时会谈到 C++20 作为扩展阅读。 C++ 有哪些面向对象思想? C++ 思想:封装 比如要表达一个数组,需要:起始地址指针 v ,数组大小 nv 将多个逻辑上相关的变量包装成一个类 因此 C++ 的 vector 将他俩打包起来,避免程序员犯错 封装:不变性 比如当我要设置数组大小为 4 时,不能只 nv = 4 还要重新分配数组内存,从而修改数组起始地址 v 常遇到: 为成员函数 不变性:请勿滥用封装 • 仅当出现“修改一个成员时,其他也成员要 被修改,否则出错”的现象时,才需要 getter/setter 封装。 • 各个成员之间相互正交,比如数学矢量类 Vec3 ,就没必要去搞封装,只会让程序员 变得痛苦,同时还有一定性能损失:特别 是如果 getter/setter 函数分离了声明和定 义,实现在另一个文件时! C++ 思想: RAII :离不开构造函数 • 如题,那么如何定义构造函数呢? BV1h64y197Fd 自定义构造函数:无参数 自定义构造函数:无参数(使用初始化表达式) 为什么需要初始化表达式? 1. 假如类成员为 const 和引用 2. 假如类成员没有无参构造函数 3. 避免重复初始化,更高效 自定义构造函数:多个参数 自定义构造函数:单个参数 自定义构造函数:单个参数(陷阱) 自定义构造函数:单个参数(避免陷阱)0 码力 | 96 页 | 16.28 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 15 C++ 系列课:字符与字符串这些整数就用于是表示这些 可显示字符 (printable character) 的。 计算机如何表达字符 • 除了可显示字符 (printable character) 外, ASCII 还规定了一 类特殊的控制字符 (control character) : • 0 表示空字符(‘ \0’ ) • 9 表示 Tab 制表符(‘ \t’ ) • 10 表示换行(‘ \n’ ) • 13 表示回车(‘ 命令行中启动 cat 。 • 试试按 Ctrl+R , Ctrl+E , Ctrl+C 等一系列 组合键,看到出现了什么? • 可以看到显示的字符变成了 ^R ^E ^C 等… … • 这是 Unix 类系统显示控制字符的一种方式 。 • 众所周知,我们常用 Ctrl+C 来发送中断信号 ( SIGINT )强制终止程序,这时常常会看到 一个 ^C 的字样,就是这样出现的。这里我 们的 cat 程序收到 函数内部在运行时处理了 % 的下一个字符。 • % 就像你和同学随手“拉钩”定下的约定,这是 printf 约定俗成的。 • \ 就像正式合同,有法律效力的,这是 C 语言编译器规定好的。 C++ 字符串类 第 3 章 C 语言字符串操作繁琐 封装的 std::string 应运而生 封装的 std::string 应运而生 • string 可以从 const char * 隐式构造: • string0 码力 | 162 页 | 40.20 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 05 C++11 开始的多线程编程std::milli> 的别名 跨平台的 sleep : std::this_thread::sleep_for • 可以用 std::this_thread::sleep_for 替代 Unix 类操作系统专有的的 usleep 。他可 以让当前线程休眠一段时间,然后继续。 • 而且单位也可以自己指定,比如这里是 milliseconds 表示毫秒,也可以换成 microseconds 下载完成前,整个界面都会处于“未响应”状 态,用户想做别的事情就做不了。 现代 C++ 中的多线程: std::thread • C++11 开始,为多线程提供了语言级别的 支持。他用 std::thread 这个类来表示线 程。 • std::thread 构造函数的参数可以是任意 lambda 表达式。 • 当那个线程启动时,就会执行这个 lambda 里的内容。 • 这样就可以一边和用户交互,一边在另一 主线程等待子线程结束: t1.join() • 因此,我们想要让主线程不要急着退出, 等子线程也结束了再退出。 • 可以用 std::thread 类的成员函数 join() 来等待该进程结束。 std::thread 的解构函数会销毁线程 • 作为一个 C++ 类, std::thread 同样遵循 RAII 思想和三五法则:因为管理着资源, 他自定义了解构函数,删除了拷贝构造 / 赋 值函数,但是提供了移动构造0 码力 | 79 页 | 14.11 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 13 C++ STL 容器全解之 vectorallocator ) 侯捷 STL 侯捷 STL vector 容器 vector 容器:构造函数 • vector 的功能是长度可变的数组,他里面的数据 存储在堆上。 • vector 是一个模板类,第一个模板参数是数组里 元素的类型。 • 例如,声明一个元素是 int 类型的动态数组 a : • vectora; vector 容器:构造函数和 size • vector vector(initializer_list list); • explicit vector(size_t n); vector 容器:构造函数 • 这在对于只能用花括号初始化的类成员来说,就 有很大问题: • vector a{4}; • 会得到长度为 1 只有一个元素 4 的数组。 • 但还是可以用这种写法强制调用显式构造函数: • vector vector(initializer_list list); • explicit vector(size_t n); vector 容器:构造函数 • 这在对于只能用花括号初始化的类成员来说,就 有很大问题: • vector a{4}; • 会得到长度为 1 只有一个元素 4 的数组。 • 但还是可以用这种写法强制调用显式构造函数: • vector 0 码力 | 90 页 | 4.93 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 16 现代 CMake 模块化项目管理指南六、头文件和源文件的一一对应关系 • 通常每个头文件都有一个对应的源文件,两个文件名字应当相同 (方便我们理解,也方便 IDE 跳转),只有后缀名不一样。 • 如果是一个类,则文件名应和类名相同,方便查找 ( Animal.cpp )。 • 头文件中包含函数和类的声明,源文件则包含他们的实现。 七、只有头文件,没有源文件的情况 • 有时我们会直接把实现直接写在头文件里,这时可以没有与之对 应的源文件,只有一个头文件。 时,同时添加同名的源文件和头文 件。 • 头文件中的声明和源文件中的实现一一对应。 九、一个模块依赖其他模块,则应导入他的头文件 • 如果新模块( Carer )中用到了其他模块( Animal )的类或函数,则需要 在新模块( Carer )的头文件和源文件中都导入其他模块( Animal )的头 文件。 • 注意不论是项目自己的头文件还是外部的系统的头文件,请全部统一采用 < 项目名 / 这种相对路径的格式,避 免模块名和系统已有头文件名冲突。 十、依赖其他模块但不解引用,则可以只前向声明不导入头文件 • 如果模块 Carer 的头文件 Carer.h 虽然引用了其他模块中的 Animal 类,但 是他里面并没有解引用 Animal ,只有源文件 Carer.cpp 解引用了 Animal 。 • 那么这个头文件是不需要导入 Animal.h 的,只需要一个前置声明 struct Animal0 码力 | 56 页 | 6.87 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 03 现代 C++ 进阶:模板元编程2” 这个 功能,需要: 为什么面向对象在 HPC 不如函数式和元编程香了? 这个例子要是按传统的面向对象思想,可能是这样: 令 Int, Float, Double 继承 Numeric 接口类并实现 ,其中 multiply(int) 作为虚函数。然后定义: Numeric *twice(Numeric *t) { return t->multiply(2); } 且不说这样的性能问题,你忍得住寂寞去重复定义好 ,你也去 定义好几个重载吗?定义为 multiply(Numeric *) 的话 依然会违背你们的开 - 闭原则:比如 3.14f * 3 ,两 端是不同的类型,怎么处理所有可能类型的排列组合 ? 不如放弃类和方法的概念,欣然接受全局函数和重载 。 模板函数:定义 • 使用 template• 其中 T 可以变成任意类型。 • 调用时 twice 即可将 T 替换为 定义的变量,其类型会自动根据等号右边的值来确定 : 自动类型推导:一些局限性 • 不过 auto 也并非万能,他也有很多限制。 • 因为需要等号右边的类型信息,所以没有 = 单独声明一个 auto 变量是不行的: • 而且,类成员也不可以定义为 auto : 自动类型推导:函数返回值 • 除了可以用于定义变量,还可以用作函数的返回类型: • 使用 auto 以后,会自动被推导为 return 右边的类型。 • 不过也有三点注意事项: 0 码力 | 82 页 | 12.15 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 14 C++ 标准库系列课 - 你所不知道的 set 容器set 的排序: string 会按“字典序”来排 • set 会从小到大排序,对 int 来 说就是数值的大小比较。那么对 字符串类型 string 要怎么排序 呢? • 其实 string 类定义了运算符重 载 < ,他会按字典序比较两个 字符串。所谓字典序就是优先比 较两者第一个字符(按 ASCII 码比较),如果相等则继续比较 下一个,不相等则直接以这个比 较的结果返回。如果比到末尾都 set做字符串集合。 这样只会按字符串指针的地址去判断相等, 而不是所指向字符串的内容。 set 的排序:自定义排序函数 • set 作为模板类,其实有两 个模板参数: set • 第一个 T 是容器内元素的类 型,例如 int 或 string 等。 • 第二个 CompT 定义了你想 要的比较函子, set 内部会 调用这个函数来决定怎么排 序。 • python 里的元组,不过固定只能有两个元素,自从 C++11 引 入了能支持任意多元素的 tuple 以来,就没 pair 什么事了……但是为了兼 容 pair 还是继续存在着。 pair 是个模板类,根据尖括号里你给定的类型来 替换这里的 _T1 和 _T2 。例如 pair 就会变成: • struct pair { • iterator first; • 0 码力 | 83 页 | 10.23 MB | 1 年前3
共 21 条
- 1
- 2
- 3













