C++高性能并行编程与优化 -  课件 - 16 现代 CMake 模块化项目管理指南biology 这个静态库对象,并通过 GLOB_RECRUSE 为他批量添加 了所有位于 src 和 include 下源码和头文件。 • 根项目的 CMakeLists.txt 负责处理全局有效的设定。而子 项目的 CMakeLists.txt 则仅考虑该子项目自身的设定,比 如他的头文件目录,要链接的库等等。 四、子项目的头文件 • 这里我们给 biology 设置了头文件搜索路径 namsepace ),避免符号冲突 • 在声明和定义外面都套一层名字空间,例如此处我的子项目名是 biology ,那 我就 biology::Animal 。避免暴露全局的 Animal 。这是因为万一有个“不拘一 格”的第三方库也暴露个全局的 Animal ,两个符号就会发生冲突,由于类符号 都具有 weak 属性,链接器会随机选择一个覆盖掉,非常危险! • (关于符号的 weak 属性,以后单独开一门 Qt5Config.cmake 的文件。 • 这些形如 包名 + Config.cmake 的文件,我称之为包配置文件。 • Qt5Config.cmake 是你安装 Qt5 时,随 libQt5Core.so 等实际的库文件,一起装到你的 系统中去的。以我的 Arch Linux 系统为例: • 包配置文件位于 /usr/lib/cmake/Qt5/Qt5Config.cmake 。 • 实际的动态库文件位于0 码力 | 56 页 | 6.87 MB | 1 年前3
 C++高性能并行编程与优化 - 课件 - Zeno 中的现代 C++ 最佳实践 的实现。这里我们用 std::decay_t快速获取了 this 指针所指向的类型,也就是当前所在类的类型 。 • 宏的缺点是他不遵守命名空间的规则,宏的名 字是全局可见的,不符合 C++ 的高大尚封装思 想。 • 宏: IOBJECT_DEFINE_CLONE • 高大尚 C++ 封装: zeno::IObject::clone() 如何批量定义 clone 函数是 C/C++ 程序中 第一个执行的函数,是程序的入口点。 • 但,他真的是第一个执行的吗? 全局变量初始化的妙用 • 我们可以定义一个 int 类型全局变量 helper ,然后他的右边其实是可以写一个表达 式的,这个表达式实际上会在 main 函数之 前执行! • 全局变量的初始化会在 main 之前执行,这实 际上是 C++ 标准的一部分,我们完全可以放 心利用这一点来执行任意表达式。 Helper ),然后一个声明该类 的全局变量( helper ),就可以保证: • 1. 该类的构造函数一定在 main 之前执行 • 2. 该类的解构函数一定在 main 之后执行 • 该技巧可用于在程序退出时删除某些文件之类 。 • 这就是小彭老师的静态初始化 (static-init) 大法 。 静态初始化用于批量注册函数 • 我们可以定义一个全局的函数表(右图中的 functab 0 码力 | 54 页 | 3.94 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 08 CUDA 开启的 GPU 编程__device__ 函数放在 同一个文件,这样方便编译器自动内联优化(第四课讲 过)。 两种开启方式:全局有效 or 仅针对单个程序 只对 main 这个程序启用: 对下方所有的程序启用(推荐): 顺便一提, CXX_STANDARD 和 CUDA_ARCHITECTURES 也有 这两种方式,我一般推荐直接设置全局的 CMAKE_CXX_STANDARD 即可应用到全部 add_executable/add_library atomicAdd :会返回旧值(划重点!) • old = atomicAdd(dst, src) 其实相当于: • old = *dst; *dst += src; • 利用这一点可以实现往一个全局的数组 res 里追加数据的效果( push_back ), 其中 sum 起到了记录当前数组大小的作 用。 • 因为返回的旧值就相当于在数组里“分配”到 了一个位置一样,不会被别人占据。 该卡的不行了,却还是非常快的样子? • 那是因为 CUDA 编译器比较聪明,自动优化 了……稍后会解释他优化的原理。 解决:线程局部变量 • 解决方法之一就是:先累加到局部变量 local_sum ,最后一次性累加到全局的 sum 。 • 这样每个线程就只有一次原子操作,而不 是网格跨步循环的那么多次原子操作了。 当然,我们需要调小 gridDim * blockDim 使其远小于 n ,这样才能够减少原子操作0 码力 | 142 页 | 13.52 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 13 C++ STL 容器全解之 vector指针来访问数据。 vector 容器:延续生命周期 • 也可以移动到一个全局变量的 vector 对象。 • 这样数组就会一直等到 main 退出了才释放。 • 小彭老师曾经在 taichi 中就是用了一个全局 变量伺候了 unique_ptr 脱离作用域会释放的 麻烦,让 lambda 中仍可访问对象。 • 至于那个全局变量本身有没有被使用则无所谓 (我们是通过首地址指针间接访问)。他的存 (我们是通过首地址指针间接访问)。他的存 在只是为了延续生命周期,告知 C++ 编译器 什么时候能 delete 而已。 (注: C++ 规定全局变量都会在进入 main 函数之前构造, main 函数返回之后解构) vector 容器: resize 到更大尺寸会导致 data 失效 • 当 resize 的目标长度大于原有的容量时, 就需要重新分配一段更大的连续内存,并 把原数组长度的部分移动过去,多出来的 部分则用0 码力 | 90 页 | 4.93 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 12 从计算机组成原理看 C 语言指针命名空间中的版本是带有多种重载的。 • 建议别用全局的任何函数( C 语言原始的), 始终带上 std:: 前缀( C++ 改良后的)。 • C++ 甚至还有 std::printf , std::memcpy , std::size_t 虽然 这些其实没有任何区别…… using namespace std 大法的确也可以 • 顺便一提,用了 using namespace std 以后全局 的 abs 就有重载了,可以不加 常见错误:以为是值比较,其实是指针比较 • 但是你不能依赖这种巧合,如果两个字符 串是在栈上分配的数组,那么首地址就不 一样,从而始终会返回 false 了。 • 刚才编译器分配的空间实际上是在全局空 间,和可执行文件放在一块(相当于全局 变量)。现在这样是在栈空间,不一样了 。 常见错误:以为是值比较,其实是指针比较 • 正确的做法是用 strcmp ,他会在字符串 相等时返回 0 ,不相等时返回非 00 码力 | 128 页 | 2.95 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 03 现代 C++ 进阶:模板元编程定义好几个重载吗?定义为 multiply(Numeric *) 的话 依然会违背你们的开 - 闭原则:比如 3.14f * 3 ,两 端是不同的类型,怎么处理所有可能类型的排列组合 ? 不如放弃类和方法的概念,欣然接受全局函数和重载 。 模板函数:定义 • 使用 template• 其中 T 可以变成任意类型。 • 调用时 twice 即可将 T 替换为 int 。 • 注意有的教材上写做: 引入的 lambda 表达式允许我们 在函数体内创建一个函数,大大地方便了 函数式编程。 • 语法就是先一个空的 [] ,然后是参数列表 ,然后是 {} 包裹的函数体。 • 再也不用被迫添加一个全局函数了! lambda 表达式:返回类型 • lambda 表达式的返回类型写在参数列表 后面,用一个箭头 -> 表示。 lambda 表达式:自动推导返回类型 • 如果 lambda 表达式不通过 std::function ,直接用函数指针的 类型 int(int) 或者 int(*)(int) 即可。 • 函数指针效率更高一些,但是 [] 就没办法 捕获局部变量了(全局变量还是可以的) 。 • 最大的好处是可以伺候一些只接受函数指 针的 C 语言的 API 比如 pthread 和 atexit 。 lambda + 模板:双倍快乐 • 可以将 lambda 0 码力 | 82 页 | 12.15 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 05 C++11 开始的多线程编程std::thread 对象管理,而是在线程退 出以后自动销毁自己。 • 不过这样还是会在进程退出时候自动退出 。 解构函数不再销毁线程:移动到全局线程池 • 但是 detach 的问题是进程退出时候不会 等待所有子线程执行完毕。所以另一种解 法是把 t1 对象移动到一个全局变量去, 从而延长其生命周期到 myfunc 函数体外 。 • 这样就可以等下载完再退出了。 main 函数退出后自动 join join 全部线程 • 但是需要在 main 里面手动 join 全部线 程还是有点麻烦,我们可以自定义一个类 ThreadPool ,并用他创建一个全局变量, 其解构函数会在 main 退出后自动调用。 std::jthread :符合 RAII 思想,解构时自动 join() • C++20 引入了 std::jthread 类,和 std::thread 不同在于:他的解构函数里会0 码力 | 79 页 | 14.11 MB | 1 年前3
 现代C++ 教程:高速上手C++11/14/17/20个特定的时间获得线程 B 的结果。 在 C++11 的 std::future 被引入之前,通常的做法是:创建一个线程 A,在线程 A 里启动任务 B,当准备完毕后发送一个事件,并将结果保存在全局变量中。而主函数线程 A 里正在做其他的事情,当 需要结果的时候,调用一个线程等待函数来获得执行的结果。 而 C++11 提供的 std::future 简化了这个流程,可以用来获取异步任务的结果。自然地,我们很 信。 削弱进程间的同步条件,通常我们会考虑四种不同的一致性模型: 1. 线性一致性:又称强一致性或原子一致性。它要求任何一次读操作都能读到某个数据的最近一次写 的数据,并且所有线程的操作顺序与全局时钟下的顺序是一致的。 x.store(1) x.load() T1 ---------+----------------+------> T2 -------------------+-------------> store(2) 严格的发生在 x.load() 之前。值得一提的是,线性一致性对全局时钟的要求 是难以实现的,这也是人们不断研究比这个一致性更弱条件下其他一致性的算法的原因。 70 7.5 原子操作与内存模型 第 7 章并行与并发 2. 顺序一致性:同样要求任何一次读操作都能读到数据最近一次写入的数据,但未要求与全局时钟的 顺序一致。 x.store(1) x.store(3) x.load()0 码力 | 83 页 | 2.42 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 15 C++ 系列课:字符与字符串” + std::to_string(42) + “ yuan” std::to_string 数字转字符串 • std::to_string 是标准库定义的全局函数,他具有 9 个重载: • 为什么把 to_string 作为全局函数,而不是 string 类的构造函数? • 因为 cpp 之父喜欢解耦,他不想让数字转字符串这个特定的需求 ,和字符串本身的实现有太多耦合。 https://en 数字转宽字符串 • 同理还有 to_wstring ,用于把数字转换为 wstring 类型字符串。 std::sto* 字符串转数字 • std::stoi/stof/stod 是标准库定义的一系列全局函数: • 相当于 js 的 parseInt/parseFloat/parseDouble 。 https://en.cppreference.com/w/cpp/string/basic_string/stol0 码力 | 162 页 | 40.20 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 11 现代 CMake 进阶指南4 章:对象的属性 除了 POSITION_INDEPENDENT_CODE 还有哪些这样的属性? 另一种方式: set_target_properties 批量设置多个属性 另一种方式:通过全局的变量,让之后创建的所有对象都享有同样的属性 相当于改变了各个属性的初始默认值。 要注意此时 set(CMAKE_xxx) 必须在 add_executable 之前才有效。 如果你从百度学的 CMake CXX_STANDARD 这种 CMake 本就提供了变量来设置的东西,就不 要自己去设置 -std=c++17 选项,会和 CMake 自己设置好的冲突,导致出 错。 请始终用 CXX_STANDARD 或是全局变量 CMAKE_CXX_STANDARD 来 设置 -std=c++17 这个 flag , CMake 会在配置阶段检测编译器是否支持 C++17 。 CUDA 的 -arch=sm_75 也是同理,请使用0 码力 | 166 页 | 6.54 MB | 1 年前3
共 19 条
- 1
 - 2
 













