Bringing Existing Code to CUDA Using constexpr and std::pmr|void add_cpu(int n, float* x, float* y) { for (int i = 0; i < n; i++) y[i] = x[i] + y[i]; } TEST_CASE("cppcon-0", "[CUDA]") { int N = 1 << 20; float* x = new float[N]; float* float* y = new float[N]; for (int i = 0; i < N; i++) { x[i] = 1.0f; y[i] = 2.0f; } add_cpu(N, x, y); delete[] x; delete[] y; } An Even Easier Introduction to CUDA |TEST_CASE("cppcon-1", "[CUDA]") { int N = 1 << 20; float* x; float* y; cudaMallocManaged(&x, N*sizeof(float)); cudaMallocManaged(&y, N*sizeof(float)); // … cudaFree(x);0 码力 | 51 页 | 3.68 MB | 6 月前3
C++高性能并行编程与优化 - 课件 - 13 C++ STL 容器全解之 vector类型的动态数组 a : • vectora; vector 容器:构造函数和 size • vector 可以在构造时指定初始长度。 • explicit vector(size_t n); • 例如,要创建一个长度为 4 的 int 型数组 : • vector a(4); • 之后可以通过 a.size() 获得数组的长度。 • 比如右边这段代码会得到 4 。 vector a(4); • 会得到长度为 4 元素全为 0 的数组。 • vector(initializer_list list); • explicit vector(size_t n); vector 容器:构造函数 • 这在对于只能用花括号初始化的类成员来说,就 有很大问题: • vector a{4}; • 会得到长度为 1 只有一个元素 4 的数组。 • vector (4); • 会得到长度为 4 元素全为 0 的数组。 • vector(initializer_list list); • explicit vector(size_t n); vector 容器:构造函数 • 这在对于只能用花括号初始化的类成员来说,就 有很大问题: • vector a{4}; • 会得到长度为 1 只有一个元素 4 的数组。 • 0 码力 | 90 页 | 4.93 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 07 深入浅出访存优化原始的代码第一个循环体执行 a[i] = a[i] * 2 ,等乘法全 部结束了以后,再来一个循环体执行 a[i] = a[i] + 1 。 • 因为第一遍循环过了 1GB 的数据,执行到 a[n-1] 时 ,原本 a[0] 处的缓存早已失效,因此第二遍循环开始 读取 a[0] 时必须重新从主内存读取,然后再次写回主 内存。 • 这种代码在主内存看来, CPU 做的事情相当于:读 + 案例:一维 jacobi 迭代 • 一些物理仿真中,常用到这种形式的迭代法: • for (i=0...n) b[i] = a[i + 1] + a[i - 1]; // 假装是 jacobi • swap(a, b); // 交换双缓冲 • for (i=0...n) b[i] = a[i + 1] + a[i - 1]; // 假装是 jacobi • swap(a, b); 第 5 章:内存分配与分页 vector :写入两次,时间都是一样的(理所当然) malloc :写入两次,第一次明显比第二次慢? new int[n] :和 malloc 一样,写入两次,第一次明显比第二次慢? new int[n]{} :后面加个花括号,就和 vector 一样,两次一样快了 结论 • 原理,当调用 malloc 时,操作系统并不会实际分配那一块内存,而是将这一段内存标记0 码力 | 147 页 | 18.88 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 06 TBB 开启的并行编程之旅time-efficiency )与工作量复杂度( work-efficiency ) • 在“小学二年级”算法课里,我们学过复杂度的概念,意思是算法执行所花费的时间取决于数据量 的大小 n ,比如 O(n²) 表示花费时间和数据量的平方成正比。 • 对于并行算法,复杂度的评估则要分为两种: • 时间复杂度:程序所用的总时间(重点) • 工作复杂度:程序所用的计算量(次要) • 这两个指 至有牺牲工作复杂度换取时间 复杂度的情形。 • 并行算法的复杂度取决于数据量 n ,还取决于线程数量 c ,比如 O(n/c) 。不过要注意如果线程 数量超过了 CPU 核心数量,通常就无法再加速了,这就是为什么要买更多核的电脑。 • 也有一种说法,认为要用 c 趋向于无穷时的时间复杂度来衡量,比如 O(n/c) 应该变成 O(1) 。 映射( map ) 1 个线程,独自处理 8 个元素的映射,花了 个元素的映射,花了 8 秒 用电量: 1*8=8 度电 结论:串行映射的时间复杂度为 O(n) ,工作复杂度为 O(n) ,其中 n 是元素个数 并行映射 4 个线程,每人处理 2 个元素的映射,花了 2 秒 用电量: 4*2=8 度电 结论:并行映射的时间复杂度为 O(n/c) ,工作复杂度为 O(n) ,其中 c 是线程数量 封装好了: parallel_for 面向初学者: parallel_for0 码力 | 116 页 | 15.85 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 14 C++ 标准库系列课 - 你所不知道的 set 容器那如果我确实需要让 set 迭 代器向前移动 3 格怎么办? • 可以调用三次 ++ 运算,实 现和 + 3 同样的效果。 • vector 迭代器的 + n 复杂度 是 O(1) 。而 set 迭代器模 拟出来的 + n 复杂度为 O(n) 。虽然低效,但至少可 以用了。 std::next 等价于 + • 但是这样手写三个 ++ 太麻烦了 ,而且是就地操作,会改变迭代 器本身。 • • 因此标准库提供了 std::next 函 数,他的内部实现相当于这样: • 没错,他会自动判断迭代器是否 支持 + 运算,如果不支持,会 改为比较低效的调用 n 次 ++ 。 std::advance 等价于 += • 刚刚的 std::next 会返回自增后迭代器 。 • 还有 std::advance 会就地自增作为引 用传入的迭代器,他同样会判断是否支 持 += 来决定要采用哪一种实现。 不会改变原迭代器。 • advance 相当于 += , next 相当于 + 。 next 和 advance 同样支持负数 • next 的第二个参数 n 通常是正 数,表示向前走的距离。 • 如果迭代器类型是双向迭代器。 next 的第二个参数 n 还可以是 负数,这时他会让迭代器往前走 一段距离,例如: • std::next(it, -3) 相当于 it - 3 。 • 还可以用另一个专门的函数0 码力 | 83 页 | 10.23 MB | 1 年前3
Lock-Free Atomic Shared Pointers Without a Split Reference Count? It Can Be Done!Load 24.8 n 16n 16n 31n 34n Store 29.8 n 19n 19n 34n 40n Store-delete 63.0 n 44n 44n 59n 14.9u Deamortized 1% 50% 99% 99.95% Load 30.0 n 20n 20n 34n 35n Store 30.2 n 20n 20n 34n 35n Store-delete Store-delete 62.4 n 47n 47n 88n 163n0 码力 | 45 页 | 5.12 MB | 6 月前3
C++高性能并行编程与优化 - 课件 - 15 C++ 系列课:字符与字符串ASCII 还规定了一 类特殊的控制字符 (control character) : • 0 表示空字符(‘ \0’ ) • 9 表示 Tab 制表符(‘ \t’ ) • 10 表示换行(‘ \n’ ) • 13 表示回车(‘ \r’ ) • 27 表示 ESC 键(‘ \x1b’ ) • 127 表示 DEL 键(‘ \x7f’ )等 • 0~31 和 127 这些整数,就构成了 ASCII 的效果其实和 Tab 键一样,按 Ctrl+J 的效果和 Enter 键一样,按 Ctrl+H 的效果和退格键 一样。 • 这是因为 ASCII 表中规定 ^I 就是 ‘ \t’ , ^J 就是 ‘ \ n’ , ^H 就是 ‘ \b’ ,所以以前原始的计算机键盘上其 实还没有 Enter 键,大家都是按 Ctrl+J 来换行的… … • 不过,如果直接在控制台输入 ‘ ^’ 和 ‘ C’ 两个字符并 判断是否为字母或数字(包括字母和数字)。 • isxdigit(c) 判断是否为十六进制数字( 0~9 或 a-f 或 A-F )。 • isspace(c) 判断是否为等价于空格的字符(‘ ’ 或 ‘ \t’ 或 ‘ \n’ 或 ‘ \v’ 或 ‘ \r’ ) 。 • iscntrl(c) 判断是否为控制字符( 0 <= c && c <= 31 或 c == 127 )。 • toupper(c) 把小写字母转换为大写字母,如果不是则原封不动返回。0 码力 | 162 页 | 40.20 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 12 从计算机组成原理看 C 语言指针课程 PPT 和代码: https://github.com/parallel101/course 请问下面这三段代码有什么错误? • float x = -3.14; • printf(“%f\n”, abs(x)); • char str[10]; • scanf(“%10s”, str); • int size = 1000; • int *arr = (int *)malloc(size); 俗称下标,表示第几个元素。 • 要注意数组的下标是从 0 开始数起的! • 虽然从 1 开始数数符合人类的习惯,但是从 0 开始数数符合计算机和程序员的习惯。 • 一个大小为 n 的数组,下标从 0 到 n-1 。 实验: char 类型数组 • 当后面有 {} 初始化时, [] 里的 4 可以省 略。 • 这时,编译器会自动推断出数组的大小。 实验: char 类型数组 • 如果没有初始化,而是稍后再赋值的话, 阿基米德说,给我一个支点,我可以撬动整个地球。 • 小彭老师说,给我一个首地址,我可以访问整个数组。 • 只要知道了首地址,就可以确定整个数组。这是因为数组的元素都是连续存储的,只要把 首地址加上 n ,就可以得到第 n 个元素的地址。从而任意一个元素的地址都可以随意访 问。 • 因此对于定长数组,只需要一个指针就可以了,稍后讨论变长数组的情况。 如果是多个字节组成的类型,例如 int 的指针呢? •0 码力 | 128 页 | 2.95 MB | 1 年前3
C++23: An Overview of Almost All New and Updated Featuresitself! With deducing this: auto fibonacci = [](this auto self, int n) { if (n < 2) { return n; } return self(n - 1) + self(n - 2); };12 Agenda C++23 Core Language Explicit Object Parameters auto b { u8"\u0100\u0300" }; C++23 auto a { U'\N{LATIN CAPITAL LETTER A WITH MACRON}' }; auto b { u8"\N{LATIN CAPITAL LETTER A WITH MACRON}\N{COMBINING GRAVE ACCENT}" }; Ā Ā ̀33 Agenda C++23 Old-style cout/format() pattern std::cout << std::format("Hello {} 2023!\n", name); // C++23 print() std::print("Hello {} 2023!\n", name); // C++23 println() std::println("Hello {} 2023!", name); String0 码力 | 105 页 | 759.96 KB | 6 月前3
C++高性能并行编程与优化 - 课件 - 10 从稀疏数据结构到量化数据类型则: a / pow(2,n) = a >> n • 也就是说 a / b = a >> log2(b) • 其中 log2 表示取 2 的对数。 • 比如 b=8 ,则 log2(b)=3 ,因为 2^3 = b 。 • 比如 a / 8 可以改成 a >> 3 。 >> 2 = 位运算 >> 对负数的处理 signed 类型的 >> n 会把最高位复制 n 次。 因为补码的特性,这导致负数 因为补码的特性,这导致负数 >> 的结果仍是负 数。 这样就实现了和 Python 一样的始终向下取整除 法。 >> 2 = unsigned 类型的位运算 >> 不一样 而 unsigned 类型的 >> n 会不会复制最高位, 只是单纯的位移,这会导致负数的符号位单独被位 移,补码失效,造成结果不对。 unsigned 类型的 >> 会生成 shr 指令, signed 类型的 >> 会生成 sar • pow(2,n) = 1 << n • pow(2,n+1) = 2 << n • 以后你们 CFDer 不要再让我看见 pow(2,n) 了…… • 或者至少用 powf(2,n) 也比这好点吧…… << 2 = 位运算总结 • a >> n 可以取出 a 的高 32-n 位 • a & ((1 << n) - 1) 可以取出 a 的低 n 位 • (a >> n) | (b &0 码力 | 102 页 | 9.50 MB | 1 年前3
共 42 条
- 1
- 2
- 3
- 4
- 5













