C++高性能并行编程与优化 - 课件 - 06 TBB 开启的并行编程之旅,其中 n 是元素个数 改进的并行缩并( GPU ) • 刚才那种方式对 c 比较大的情况不友好, 最后一个串行的 for 还是会消耗很多时间 。 • 因此可以用递归的模式,每次只使数据缩 小一半,这样基本每次都可以看做并行的 for ,只需 log2(n) 次并行 for 即可完成 缩并。 • 这种常用于核心数量很多,比如 GPU 上 的缩并。 结论:改进后的并行缩并的时间复杂度为 ,工作复杂度为 O(n+c) ,其中 n 是元素个数 改进的并行扫描( GPU ) 第一步、 4 个线程,每个处理 2 个元素的扫描,花了 1 秒 第而步、 4 个线程,每个处理 2 个元素的扫描,花了 1 秒 第三步、 4 个线程,每个处理 2 个元素的扫描,花了 1 秒 用电量: 3*4=12 度电 总用时: 1*3=3 秒 结论:改进后的并行扫描的时间复杂度为 O(logn) ,工作复杂度为 完毕后才去认领下一个任务,从而即使每个任务 工作量不一也能自动适应。 • 这种技术又称为线程池( thread pool ),避免了 线程需要保存上下文的开销。但是需要我们管理 一个任务队列,而且要是线程安全的队列。 struct Task { int x0, y0; int nx, ny; }; std::queueq; 1 2 3 4 解决 3 :每个线程一个任务队 0 码力 | 116 页 | 15.85 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 07 深入浅出访存优化当读和写创建的新条目过多,缓存快要塞不下时,他会把 最不常用的那个条目移除,这个现象称为失效( invalid )。 如果那个条目是被标记为脏的,则说明是当时打算写入的 数据,那就需要向主内存发送写入请求,等他写入成功, 才能安全移除这个条目。 • 如有多级缓存,则一级缓存失效后会丢给二级缓存。 连续访问与跨步访问 • 如果访问数组时,按一定的间距跨步访问,则效率如何? • 从 1 到 16 都是一样快的, 32 开始才按 ?原来现在操作系统管理内存是用分页 ( page ),程序的内存是一页一页贴在地址空间中的, 有些地方可能不可访问,或者还没有分配,则把这个页设 为不可用状态,访问他就会出错,进入内核模式。 • 因此硬件出于安全,预取不能跨越页边界,否则可能会触 发不必要的 page fault 。所以我们选用页的大小,因为本 来就不能跨页顺序预取,所以被我们切断掉也无所谓。 • 另外,我们可以用 _mm_alloc nx*nblur 需要缓存: blockSize*nx*nblur 改进:只对 X 循环做分块 BM_y_blur_tiled BM_y_blur_tiled_only_x 因此,可以只对 X 循环分块,并且把外 层改成 XY 序,形成 XYx 序。 需要缓存: blockSize*nx*nblur 需要缓存: blockSize*nblur 改进:只对 X 循环做分块 • 反而变慢了,是怎么回事?0 码力 | 147 页 | 18.88 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 12 从计算机组成原理看 C 语言指针long 的大小应该和系统架构位数一样, 32 位系统上就 32 位, 64 位系统上就 64 位。 Windows 认为 long 不论 32 位系统还是 64 位系统都一样应该为 32 位,认为这样安全。 因此我们在编写 C 语言程序时,应该避免使用 long 类型,他会导致你的程序难以跨平台。 除了 long 之外的其他类型则没有区别,可以放心使用。 无符号整数: unsigned 修饰 有符号版本 用途举例:假如一个参数类 Params 非常复杂 ,然后函数是 func(Params *pars) ,这样只要 给 pars 传一个空指针,就表示“用户不想指定 这个参数”的意思。 C++ 可以用更安全的 func(std::optionalpars) 来替代。 NULL 的定义为什么是这样的? • 如果你看过标准库的头文件内容,会看到 NULL 的 本质无非是一个宏。那为什么要这样定义呢? 意类型的指针。 • 这样总算可以区分 func(int*) 和 func(int) 的重载了。 • 在现代 C++ 中,建议始终用 nullptr ,不 要再用 NULL 和 0 表示空指针了,不安全 。 来看看 GLIBC 的源码 数组的本质究竟是什么? 数组就是一堆在内存中紧密排列在一起的数 • 例如一个由 4 个字节组成的 char 数组,在内存中就是: 实验: char 类型数组 0 码力 | 128 页 | 2.95 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - Zeno 中的现代 C++ 最佳实践 是一个模板类,则 CRTP 的那个参数应包含派生类的模板参数,例 如: • template• struct Derived : Base > {}; CRTP 的改进:如果基类还想基于另一个类 • 现在我们的需求有变,需要新增一个“超狗 (superdog)” 类,他继承自普通狗 (dog) 。 • 这时我们可以给 IObjectClone 新增一个模板参数 的对象,如果类型不是 T ,则出错。 NumericObject 的定义 • NumericObject 是基于 std::variant 的。 • 注意他的 get 成员函数,这和 std::get 相 比更安全,例如 value 是 int 类型,但用 户却调用了 get 。则这里 is_constructible 是 true ,不会出错,而是 会自动把 float 转换成 int 0 码力 | 54 页 | 3.94 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 04 从汇编角度看编译器优化ivdep C/C++ 的缺点:指针的自由度过高,允许多个 immutable reference 指向同一个对象,而 Rust 从语法层面禁止,从而让编译器放心大胆 优化。 为什么标准委员会不改进一下?因为一旦放弃 兼容,就等于抛弃所有历史遗产的全新语言, 就和 Rust 无异,从而没有任何理由再学习 C++ 。 std::vector :也能实现 SOA ! 优化前 (AOS) 优化后 总之,请勿用全局的数学函数,他们是 C 语言 的遗产。始终用 std::sin, std::pow 等。 abs(1.4f) = 1 ,因为 abs 是个参数类型为 int 的函数。 为了避免这种麻烦,请用安全的 std::abs(1.4f) = 1.4f 开启前: sqrt 矢量化失败 开启后: sqrt 矢量化成功 嵌套循环:直接累加,有指针别名问题 编译器担心 c 和 a 可能会指向同一个 地址,而连续判断三个指针是否有重合0 码力 | 108 页 | 9.47 MB | 1 年前3
Hello 算法 1.0.0b4 C++版return -1; } � 实际应用中我们很少使用「最佳时间复杂度」,因为通常只有在很小概率下才能达到,可能会 带来一定的误导性。相反,「最差时间复杂度」更为实用,因为它给出了一个“效率安全值”, 让我们可以放心地使用算法。 从上述示例可以看出,最差或最佳时间复杂度只出现在“特殊分布的数据”中,这些情况的出现概率可能很 小,因此并不能最真实地反映算法运行效率。相较之下,「平均时间复杂度」可以体现算法在随机输入数据下 均匀分布:哈希算法应使得键值对平均分布在哈希表中。分布越平均,哈希冲突的概率就越低。 实际上,哈希算法除了可以用于实现哈希表,还广泛应用于其他领域中。举两个例子: ‧ 密码存储:为了保护用户密码的安全,系统通常不会直接存储用户的明文密码,而是存储密码的哈希 值。当用户输入密码时,系统会对输入的密码计算哈希值,然后与存储的哈希值进行比较。如果两者匹 配,那么密码就被视为正确。 ‧ 数据完整性 数据完整性检查:数据发送方可以计算数据的哈希值并将其一同发送;接收方可以重新计算接收到的 数据的哈希值,并与接收到的哈希值进行比较。如果两者匹配,那么数据就被视为完整的。 对于密码学的相关应用,哈希算法需要满足更高的安全标准,以防止从哈希值推导出原始密码等逆向工程, 包括: ‧ 抗碰撞性:应当极其困难找到两个不同的输入,使得它们的哈希值相同。 ‧ 雪崩效应:输入的微小变化应当导致输出的显著且不可预测的变化。0 码力 | 343 页 | 27.39 MB | 1 年前3
Hello 算法 1.1.0 C++ 版i; } return -1; } 值得说明的是,我们在实际中很少使用最佳时间复杂度,因为通常只有在很小概率下才能达到,可能会带来 一定的误导性。而最差时间复杂度更为实用,因为它给出了一个效率安全值,让我们可以放心地使用算法。 从上述示例可以看出,最差时间复杂度和最佳时间复杂度只出现于“特殊的数据分布”,这些情况的出现概率 可能很小,并不能真实地反映算法运行效率。相比之下,平均时间复杂度可以体现算法在随机输入数据下的 { if (nums[i] == target) return i; } return -1; } 7. 扩容数组 在复杂的系统环境中,程序难以保证数组之后的内存空间是可用的,从而无法安全地扩展数组容量。因此在 大多数编程语言中,数组的长度是不可变的。 如果我们希望扩容数组,则需重新建立一个更大的数组,然后把原数组元素依次复制到新数组。这是一个 ?(?) 的操作,在数组很大的情况下非常耗时。代码如下所示: 了。这意味着节点 P 已经 从链表中删除了,此时节点 P 指向哪里都不会对该链表产生影响。 从数据结构与算法(做题)的角度看,不断开没有关系,只要保证程序的逻辑是正确的就行。从标准库的角 度看,断开更加安全、逻辑更加清晰。如果不断开,假设被删除节点未被正常回收,那么它会影响后继节点 的内存回收。 Q:在链表中插入和删除操作的时间复杂度是 ?(1) 。但是增删之前都需要 ?(?) 的时间查找元素,那为什0 码力 | 379 页 | 18.47 MB | 1 年前3
Hello 算法 1.0.0 C++版i; } return -1; } 值得说明的是,我们在实际中很少使用最佳时间复杂度,因为通常只有在很小概率下才能达到,可能会带来 一定的误导性。而最差时间复杂度更为实用,因为它给出了一个效率安全值,让我们可以放心地使用算法。 从上述示例可以看出,最差时间复杂度和最佳时间复杂度只出现于“特殊的数据分布”,这些情况的出现概率 可能很小,并不能真实地反映算法运行效率。相比之下,平均时间复杂度可以体现算法在随机输入数据下的 { if (nums[i] == target) return i; } return -1; } 7. 扩容数组 在复杂的系统环境中,程序难以保证数组之后的内存空间是可用的,从而无法安全地扩展数组容量。因此在 大多数编程语言中,数组的长度是不可变的。 如果我们希望扩容数组,则需重新建立一个更大的数组,然后把原数组元素依次复制到新数组。这是一个 ?(?) 的操作,在数组很大的情况下非常耗时。代码如下所示: 列表 [0] 。 Q:在删除节点中,需要断开该节点与其后继节点之间的引用指向吗? 从数据结构与算法(做题)的角度看,不断开没有关系,只要保证程序的逻辑是正确的就行。从标准库的角 度看,断开更加安全、逻辑更加清晰。如果不断开,假设被删除节点未被正常回收,那么它会影响后继节点 的内存回收。 89 第 5 章 栈与队列 � 栈如同叠猫猫,而队列就像猫猫排队。 两者分别代表先入后出和先入先出的逻辑关系。0 码力 | 378 页 | 17.59 MB | 1 年前3
Hello 算法 1.0.0b5 C++版i; } return -1; } 值得说明的是,我们在实际中很少使用最佳时间复杂度,因为通常只有在很小概率下才能达到,可能会带来 一定的误导性。而最差时间复杂度更为实用,因为它给出了一个效率安全值,让我们可以放心地使用算法。 从上述示例可以看出,最差或最佳时间复杂度只出现于“特殊的数据分布”,这些情况的出现概率可能很小, 并不能真实地反映算法运行效率。相比之下,平均时间复杂度可以体现算法在随机输入数据下的运行效率, { if (nums[i] == target) return i; } return -1; } 7. 扩容数组 在复杂的系统环境中,程序难以保证数组之后的内存空间是可用的,从而无法安全地扩展数组容量。因此在 大多数编程语言中,数组的长度是不可变的。 如果我们希望扩容数组,则需重新建立一个更大的数组,然后把原数组元素依次拷贝到新数组。这是一个 ?(?) 的操作,在数组很大的情况下是非常耗时的。 。 ‧ 均匀分布:哈希算法应使得键值对平均分布在哈希表中。分布越平均,哈希冲突的概率就越低。 实际上,哈希算法除了可以用于实现哈希表,还广泛应用于其他领域中。 ‧ 密码存储:为了保护用户密码的安全,系统通常不会直接存储用户的明文密码,而是存储密码的哈希 值。当用户输入密码时,系统会对输入的密码计算哈希值,然后与存储的哈希值进行比较。如果两者匹 配,那么密码就被视为正确。 ‧ 数据完整性0 码力 | 377 页 | 30.69 MB | 1 年前3
Hello 算法 1.2.0 简体中文 C++ 版i; } return -1; } 值得说明的是,我们在实际中很少使用最佳时间复杂度,因为通常只有在很小概率下才能达到,可能会带来 一定的误导性。而最差时间复杂度更为实用,因为它给出了一个效率安全值,让我们可以放心地使用算法。 从上述示例可以看出,最差时间复杂度和最佳时间复杂度只出现于“特殊的数据分布”,这些情况的出现概率 可能很小,并不能真实地反映算法运行效率。相比之下,平均时间复杂度可以体现算法在随机输入数据下的 { if (nums[i] == target) return i; } return -1; } 7. 扩容数组 在复杂的系统环境中,程序难以保证数组之后的内存空间是可用的,从而无法安全地扩展数组容量。因此在 大多数编程语言中,数组的长度是不可变的。 如果我们希望扩容数组,则需重新建立一个更大的数组,然后把原数组元素依次复制到新数组。这是一个 ?(?) 的操作,在数组很大的情况下非常耗时。代码如下所示: 了。这意味着节点 P 已经 从链表中删除了,此时节点 P 指向哪里都不会对该链表产生影响。 从数据结构与算法(做题)的角度看,不断开没有关系,只要保证程序的逻辑是正确的就行。从标准库的角 度看,断开更加安全、逻辑更加清晰。如果不断开,假设被删除节点未被正常回收,那么它会影响后继节点 的内存回收。 Q:在链表中插入和删除操作的时间复杂度是 ?(1) 。但是增删之前都需要 ?(?) 的时间查找元素,那为什0 码力 | 379 页 | 18.48 MB | 10 月前3
共 25 条
- 1
- 2
- 3













