 Zadig 面向开发者的云原生 DevOps 平台产品发展历程 高频极速迭代: Zadig 开源 29 个月共迭代 21 个版本 “ ” 开发者常处于 今天发版、明早升级 嗷嗷待哺状态 Zadig 优势、使用场景、解决问题域 Zadig 解决问题域 Zadig 云原生开放性:极简、 0 负担接入 Zadig 业务架构 Zadig 系统架构 1 Zadig 行业方案 对比分析 职能 传统 DevOps 方案 ZadigX 30% 统一治理内部规范,开发 自助上线;解放运维,工 作重心向业务稳定性保 障,建设平台工程体系 研发 研发时间被大量占用: • 本地开发环境难模拟 • 多业务联调艰难,诊断耗时多 • 出现问题诊断耗时多 • 流程割裂协作痛苦,响应慢 调试自测免打扰:本地 / 子环境免打扰,独立完成验证工作 自助验证更高效:自动化工作流 + 云上环境,高效验证调试 安全发布有信心: 一个平台完成日常 价值被团队感知:自动化测试从开发到发布被全团队感知 部署频率升高 1-5 倍 验证有效性提升 100% 解放测试,全面自动化 提升效率,建设质量体系 安全 安全建设被动: • 安全建设缺乏时机和抓手 • 出现问题,影响业务进度 前置安全服务:全流程嵌入安全检测,避免流入业务环节。 全流程安全门禁:关键环节设置安全门禁,快速反馈研发改进 故障拦截率提升 1-3 倍 业务响应效率提升 3-5 倍0 码力 | 59 页 | 81.43 MB | 1 年前3 Zadig 面向开发者的云原生 DevOps 平台产品发展历程 高频极速迭代: Zadig 开源 29 个月共迭代 21 个版本 “ ” 开发者常处于 今天发版、明早升级 嗷嗷待哺状态 Zadig 优势、使用场景、解决问题域 Zadig 解决问题域 Zadig 云原生开放性:极简、 0 负担接入 Zadig 业务架构 Zadig 系统架构 1 Zadig 行业方案 对比分析 职能 传统 DevOps 方案 ZadigX 30% 统一治理内部规范,开发 自助上线;解放运维,工 作重心向业务稳定性保 障,建设平台工程体系 研发 研发时间被大量占用: • 本地开发环境难模拟 • 多业务联调艰难,诊断耗时多 • 出现问题诊断耗时多 • 流程割裂协作痛苦,响应慢 调试自测免打扰:本地 / 子环境免打扰,独立完成验证工作 自助验证更高效:自动化工作流 + 云上环境,高效验证调试 安全发布有信心: 一个平台完成日常 价值被团队感知:自动化测试从开发到发布被全团队感知 部署频率升高 1-5 倍 验证有效性提升 100% 解放测试,全面自动化 提升效率,建设质量体系 安全 安全建设被动: • 安全建设缺乏时机和抓手 • 出现问题,影响业务进度 前置安全服务:全流程嵌入安全检测,避免流入业务环节。 全流程安全门禁:关键环节设置安全门禁,快速反馈研发改进 故障拦截率提升 1-3 倍 业务响应效率提升 3-5 倍0 码力 | 59 页 | 81.43 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 02 现代 C++ 入门:RAII 内存管理的函数。 构造函数! 编写我们自己的 vector 类! 看来 vector 也不过如此!让我们自己实现一个 Vector 类试试看 吧 It works! 这个 Vector 类有哪些问题? 三五法则:规则类怪谈 1. 如果一个类定义了解构函数,那么您必须同时定义 或删除拷贝构造函数和拷贝赋值函数,否则出错。 2. 如果一个类定义了拷贝构造函数,那么您必须同时 定义或删除拷贝赋值函数,否则出错,删除可导致 com/isocpp/ CppCoreGuidelines 三五法则:拷贝构造函数 • 在 = 时,默认是会拷贝的。比如右边这样: • 但是这样对我们当前 Vector 的实现造成一个很大 的问题。其 m_data 指针是按地址值浅拷贝的, 而不深拷贝其指向的数组! • 这就是说,在退出 main 函数作用域的时 候, v1.m_data 会被释放两次!更危险的则是 v1 被解构而 会导致内存泄露等情况,更可能被黑客利 用空悬指针篡改系统内存从而盗取重要数 据等。 RAII 解决内存管理的问题: unique_ptr • 似曾相识的情形……是的,和我们刚刚提 到的 RAII 思想不谋而合! • 因此, C++11 引入了 unique_ptr 容器, 他的解构函数中会调用 delete p ,因此不 会有马虎犯错的问题。 • 这里 make_unique C++高性能并行编程与优化 -  课件 - 02 现代 C++ 入门:RAII 内存管理的函数。 构造函数! 编写我们自己的 vector 类! 看来 vector 也不过如此!让我们自己实现一个 Vector 类试试看 吧 It works! 这个 Vector 类有哪些问题? 三五法则:规则类怪谈 1. 如果一个类定义了解构函数,那么您必须同时定义 或删除拷贝构造函数和拷贝赋值函数,否则出错。 2. 如果一个类定义了拷贝构造函数,那么您必须同时 定义或删除拷贝赋值函数,否则出错,删除可导致 com/isocpp/ CppCoreGuidelines 三五法则:拷贝构造函数 • 在 = 时,默认是会拷贝的。比如右边这样: • 但是这样对我们当前 Vector 的实现造成一个很大 的问题。其 m_data 指针是按地址值浅拷贝的, 而不深拷贝其指向的数组! • 这就是说,在退出 main 函数作用域的时 候, v1.m_data 会被释放两次!更危险的则是 v1 被解构而 会导致内存泄露等情况,更可能被黑客利 用空悬指针篡改系统内存从而盗取重要数 据等。 RAII 解决内存管理的问题: unique_ptr • 似曾相识的情形……是的,和我们刚刚提 到的 RAII 思想不谋而合! • 因此, C++11 引入了 unique_ptr 容器, 他的解构函数中会调用 delete p ,因此不 会有马虎犯错的问题。 • 这里 make_unique- (...) 可以理解为和 之前的 0 码力 | 96 页 | 16.28 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 11 现代 CMake 进阶指南大可不必!用 aux_source_directory ,自动搜集需要的文件后缀名 进一步: GLOB_RECURSE 了解一下!能自动包含所有子文件夹下的文件 GLOB_RECURSE 的问题:会把 build 目录里生成的临时 .cpp 文件也 加进来 解决方案:要么把源码统一放到 src 目录下,要么要求使用者不要把 build 放到和源码同一个目录里,我个人的建议是把源码放到 src 版本新增) • 如果不指定 LANGUAGES ,默认为 C 和 CXX 。 https://cmake.org/cmake/help/latest/command/project.html 常见问题: LANGUAGES 中没有启用 C 语言,但是却用到了 C 语言 解决:改成 project( 项目名 LANGUAGES C CXX) 即可 也可以先设置 LANGUAGES NONE ,之后再调用 GCC 编译器的选项,无法跨平台用于 MSVC 编 译器。 假如你一定要用动态链接库( Windows 对动态链接很不友好) 假如你一定要用动态链接库( Windows 对动态链接很不友好) 常见问题:老师,我链接了自己的 dll ,但是为什么运行时会找不到? • 这是因为你的 dll 和 exe 不在同一目录。 Windows 比较蠢,他只会找当前 exe 所在目 录,然后查找 PATH ,找不到就报错。而你的0 码力 | 166 页 | 6.54 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 11 现代 CMake 进阶指南大可不必!用 aux_source_directory ,自动搜集需要的文件后缀名 进一步: GLOB_RECURSE 了解一下!能自动包含所有子文件夹下的文件 GLOB_RECURSE 的问题:会把 build 目录里生成的临时 .cpp 文件也 加进来 解决方案:要么把源码统一放到 src 目录下,要么要求使用者不要把 build 放到和源码同一个目录里,我个人的建议是把源码放到 src 版本新增) • 如果不指定 LANGUAGES ,默认为 C 和 CXX 。 https://cmake.org/cmake/help/latest/command/project.html 常见问题: LANGUAGES 中没有启用 C 语言,但是却用到了 C 语言 解决:改成 project( 项目名 LANGUAGES C CXX) 即可 也可以先设置 LANGUAGES NONE ,之后再调用 GCC 编译器的选项,无法跨平台用于 MSVC 编 译器。 假如你一定要用动态链接库( Windows 对动态链接很不友好) 假如你一定要用动态链接库( Windows 对动态链接很不友好) 常见问题:老师,我链接了自己的 dll ,但是为什么运行时会找不到? • 这是因为你的 dll 和 exe 不在同一目录。 Windows 比较蠢,他只会找当前 exe 所在目 录,然后查找 PATH ,找不到就报错。而你的0 码力 | 166 页 | 6.54 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 15 C++ 系列课:字符与字符串选项。 • 但是他的输出会保存到一个字符串里。 • 调用成员函数 .str() 就能取出这个字符串了。 • 之后这个字符串就可以用作其他用途,比如 printf 打印,或者用于查询数据库,都没问题。 • 这里比较无聊,最后还是直接输出到了 cout 。 stringstream 也可以取代 stoi • 刚刚展示了 stringstream 模仿 cout 的方法。 • stringstream 和 += • 刚刚说了 + 和 += 比 append 更直观,而且只要配合 string_view ,性能上就没有区别了,为什么不用呢? • 先把程序写出来,结果跑对了,再来考虑什么优化的问题。 • 直观的程序更容易调试,而 + 是非常直观的。——沃兹基硕德 • 小彭老师锐评:跑之前先学会走,反对心理作用优化、性能强迫症 。 • 围棋术语说:本手,妙手,俗手。 • 可能你以为自己这一步是妙手,其实是擦粑粑的手。 首先映入眼帘的是 _Alloc_hider 这个奇怪的类,包装了一 下首地址指针 _M_p 。为什么要套这一层壳?这其实是为 了防止 allocator 对象浪费空间的优化手段。俗称空基类 优化,问题来了,为什么需要空基类优化? string 的空基类优化 • 如果不优化的话,是会把 allocator 直接作为成员变量放 在 basic_string 里的,但是因为 C++ 规定任何对象都要0 码力 | 162 页 | 40.20 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 15 C++ 系列课:字符与字符串选项。 • 但是他的输出会保存到一个字符串里。 • 调用成员函数 .str() 就能取出这个字符串了。 • 之后这个字符串就可以用作其他用途,比如 printf 打印,或者用于查询数据库,都没问题。 • 这里比较无聊,最后还是直接输出到了 cout 。 stringstream 也可以取代 stoi • 刚刚展示了 stringstream 模仿 cout 的方法。 • stringstream 和 += • 刚刚说了 + 和 += 比 append 更直观,而且只要配合 string_view ,性能上就没有区别了,为什么不用呢? • 先把程序写出来,结果跑对了,再来考虑什么优化的问题。 • 直观的程序更容易调试,而 + 是非常直观的。——沃兹基硕德 • 小彭老师锐评:跑之前先学会走,反对心理作用优化、性能强迫症 。 • 围棋术语说:本手,妙手,俗手。 • 可能你以为自己这一步是妙手,其实是擦粑粑的手。 首先映入眼帘的是 _Alloc_hider 这个奇怪的类,包装了一 下首地址指针 _M_p 。为什么要套这一层壳?这其实是为 了防止 allocator 对象浪费空间的优化手段。俗称空基类 优化,问题来了,为什么需要空基类优化? string 的空基类优化 • 如果不优化的话,是会把 allocator 直接作为成员变量放 在 basic_string 里的,但是因为 C++ 规定任何对象都要0 码力 | 162 页 | 40.20 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 07 深入浅出访存优化理论带宽极限 42672 MB/s ! • 而数据量足够大时, 才回落到正常的带宽 。 • 这是为什么? CPU 内部的高速缓存 • 原来 CPU 的厂商早就意识到了内存延迟高,读写效率低 下的问题。因此他们在 CPU 内部引入了一片极小的存储 器——虽然小,但是读写速度却特别快。这片小而快的 存储器称为缓存( cache )。 • 当 CPU 访问某个地址时,会先查找缓存中是否有对应的 分量用 AOS” 这个结论,是单从内存访问效率来看的,需 要 SIMD 矢量化的话可能还是要 SOA 或 AOSOA ,比如 hw04 那种的。而 “ pos 和 vel 应该用 SOA 分开存”是没问题的。 • 而且 SOA 在遇到存储不是 vector ,而是稀疏的哈希网格之类索引有一定 开销的数据结构,可能就不适合了。这就是为什么王鑫磊最喜欢 AOSOA :在高层保持 AOS 的统一索引,底层又享受 不过其实标准库的 new 和 malloc 已经 可以保证 16 字节对齐了。如果你只需要 用 _mm_load_ps 而不用 _mm256_load_ps 的话,那直接用标准库 的内存分配也没问题。 标准库的 new 和 malloc :只保证 16 字节对齐 • 还有 _mm_malloc(n, aalign) 可以分配对齐 到任意 a 字节的内存。他在0 码力 | 147 页 | 18.88 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 07 深入浅出访存优化理论带宽极限 42672 MB/s ! • 而数据量足够大时, 才回落到正常的带宽 。 • 这是为什么? CPU 内部的高速缓存 • 原来 CPU 的厂商早就意识到了内存延迟高,读写效率低 下的问题。因此他们在 CPU 内部引入了一片极小的存储 器——虽然小,但是读写速度却特别快。这片小而快的 存储器称为缓存( cache )。 • 当 CPU 访问某个地址时,会先查找缓存中是否有对应的 分量用 AOS” 这个结论,是单从内存访问效率来看的,需 要 SIMD 矢量化的话可能还是要 SOA 或 AOSOA ,比如 hw04 那种的。而 “ pos 和 vel 应该用 SOA 分开存”是没问题的。 • 而且 SOA 在遇到存储不是 vector ,而是稀疏的哈希网格之类索引有一定 开销的数据结构,可能就不适合了。这就是为什么王鑫磊最喜欢 AOSOA :在高层保持 AOS 的统一索引,底层又享受 不过其实标准库的 new 和 malloc 已经 可以保证 16 字节对齐了。如果你只需要 用 _mm_load_ps 而不用 _mm256_load_ps 的话,那直接用标准库 的内存分配也没问题。 标准库的 new 和 malloc :只保证 16 字节对齐 • 还有 _mm_malloc(n, aalign) 可以分配对齐 到任意 a 字节的内存。他在0 码力 | 147 页 | 18.88 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 06  TBB 开启的并行编程之旅是性能毕竟不是线性增长。 • 为什么无法做到呢?首先,为了保证缓存一致性以及其他握手协议需要运行时间开销。在 今天,双核或者四核机器在多线程应用方面,其性能不见得的是单核机器的两倍或者四倍。 这一问题一直伴随 CPU 发展至今。 并发和并行的区别 • 运用多线程的方式和动机,一般分为两种。 • 并发:单核处理器,操作系统通过时间片调 度算法,轮换着执行着不同的线程,看起来 就好像是同时运行一样,其实每一时刻只有 com/chapter/10.1007%2F978-1-4842-4398-5_12 任务域: tbb::task_arena 任务域:指定使用 4 个线程 嵌套 for 循环 嵌套 for 循环:死锁问题 死锁问题的原因 • 因为 TBB 用了工作窃取法来分配任务: 当一个线程 t1 做完自己队列里全部的工 作时,会从另一个工作中线程 t2 的队列 里取出任务,以免 t1 闲置浪费时间。 • 因此内部 tbb::parallel_sort (和标准库串行的 std::sort )加速比: 4.80 倍 重新认识改进的并行缩并 • 其实之前提到“改进后的并行缩并”,也是一 种分治法的思想:大问题一分为二变成小 问题,分派到各个 CPU 核心上,问题足够 小时直接串行求解。 • 他也可以通过 parallel_invoke 分治来实现 : 第 9 章:流水线并行 案例:批量处理数据 注意到这里的 for (auto0 码力 | 116 页 | 15.85 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 06  TBB 开启的并行编程之旅是性能毕竟不是线性增长。 • 为什么无法做到呢?首先,为了保证缓存一致性以及其他握手协议需要运行时间开销。在 今天,双核或者四核机器在多线程应用方面,其性能不见得的是单核机器的两倍或者四倍。 这一问题一直伴随 CPU 发展至今。 并发和并行的区别 • 运用多线程的方式和动机,一般分为两种。 • 并发:单核处理器,操作系统通过时间片调 度算法,轮换着执行着不同的线程,看起来 就好像是同时运行一样,其实每一时刻只有 com/chapter/10.1007%2F978-1-4842-4398-5_12 任务域: tbb::task_arena 任务域:指定使用 4 个线程 嵌套 for 循环 嵌套 for 循环:死锁问题 死锁问题的原因 • 因为 TBB 用了工作窃取法来分配任务: 当一个线程 t1 做完自己队列里全部的工 作时,会从另一个工作中线程 t2 的队列 里取出任务,以免 t1 闲置浪费时间。 • 因此内部 tbb::parallel_sort (和标准库串行的 std::sort )加速比: 4.80 倍 重新认识改进的并行缩并 • 其实之前提到“改进后的并行缩并”,也是一 种分治法的思想:大问题一分为二变成小 问题,分派到各个 CPU 核心上,问题足够 小时直接串行求解。 • 他也可以通过 parallel_invoke 分治来实现 : 第 9 章:流水线并行 案例:批量处理数据 注意到这里的 for (auto0 码力 | 116 页 | 15.85 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 12 从计算机组成原理看 C 语言指针,这样不就区分开来了吗?这叫做原码表示 法。 • 的确可以,这种表示方式牺牲了一位作为符号位,剩下 7 位继续表示值。 • 这样的设计下无符号可以表示 0 到 255 ,而有符号可以表示 -127 到 127 。 • 但是有一个问题,那 00000000 就表示 0 , 10000000 就表示 -0 ,而 0 有没有负号其 实无所谓, 0 和 -0 根本是同一个数,却有着不同的表示,这显然不对吧? • 因此,可以如 10000000 这种奇怪的东西了,而且表示范围也扩大了一位,虽然是扩 大在负数部分。 有符号整数 vs 无符号整数 • 刚刚说的让 10000000 表示 -1 , 11111111 表示 -128 的方法就叫做反码表示法。 • 但是这样还有一个问题,那就是硬件电路上,需要完全重新设计,对符号位做一些特殊判 断,才能支持有符号整数的加减法,因此如今的计算机都采用了一种更聪明的表示法: • 他们让 11111111 表示 -1 , 10000000 语言标准并没有规定 int 就是 32 位 的。 • int 甚至可以是 16 位的!只不过主流操作系统一致认为是 32 位的而已,并不是标准所保 证的。 • 为了解决不同操作系统上对类型定义混乱的问题, C 语言标准引入了 stdint.h 这个头文件 。 • 他里面包含一系列类型别名 (typedef) ,这些别名保证不论是什么操作系统什么架构,都是 固定的大小,例如: • typedef0 码力 | 128 页 | 2.95 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 12 从计算机组成原理看 C 语言指针,这样不就区分开来了吗?这叫做原码表示 法。 • 的确可以,这种表示方式牺牲了一位作为符号位,剩下 7 位继续表示值。 • 这样的设计下无符号可以表示 0 到 255 ,而有符号可以表示 -127 到 127 。 • 但是有一个问题,那 00000000 就表示 0 , 10000000 就表示 -0 ,而 0 有没有负号其 实无所谓, 0 和 -0 根本是同一个数,却有着不同的表示,这显然不对吧? • 因此,可以如 10000000 这种奇怪的东西了,而且表示范围也扩大了一位,虽然是扩 大在负数部分。 有符号整数 vs 无符号整数 • 刚刚说的让 10000000 表示 -1 , 11111111 表示 -128 的方法就叫做反码表示法。 • 但是这样还有一个问题,那就是硬件电路上,需要完全重新设计,对符号位做一些特殊判 断,才能支持有符号整数的加减法,因此如今的计算机都采用了一种更聪明的表示法: • 他们让 11111111 表示 -1 , 10000000 语言标准并没有规定 int 就是 32 位 的。 • int 甚至可以是 16 位的!只不过主流操作系统一致认为是 32 位的而已,并不是标准所保 证的。 • 为了解决不同操作系统上对类型定义混乱的问题, C 语言标准引入了 stdint.h 这个头文件 。 • 他里面包含一系列类型别名 (typedef) ,这些别名保证不论是什么操作系统什么架构,都是 固定的大小,例如: • typedef0 码力 | 128 页 | 2.95 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 13 C++ STL 容器全解之 vectorvector(initializer_list C++高性能并行编程与优化 -  课件 - 13 C++ STL 容器全解之 vectorvector(initializer_list- list); • explicit vector(size_t n); vector 容器:构造函数 • 这在对于只能用花括号初始化的类成员来说,就 有很大问题: • vector - a{4}; • 会得到长度为 1 只有一个元素 4 的数组。 • 但还是可以用这种写法强制调用显式构造函数: • vector - a = vector - (4); vector(initializer_list - list); • explicit vector(size_t n); vector 容器:构造函数 • 这在对于只能用花括号初始化的类成员来说,就 有很大问题: • vector - a{4}; • 会得到长度为 1 只有一个元素 4 的数组。 • 但还是可以用这种写法强制调用显式构造函数: • vector - a = vector - (4); free 函 数。 • 这样当 vector 容器分配或是释放内 存的时候,我们就能轻松看到。 • 不过这个只能 Linux 系统可以用哦 。 vector 容器: push_back 的问题 • 由于不知道你究竟会推入多少个元素, vector 的初始容 量是零,而 push_back 和 resize 一样,每次遇到容量 不足时,都会扩容两倍,如图。 • 这也体现了实际容量 (capacity) 0 码力 | 90 页 | 4.93 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 10 从稀疏数据结构到量化数据类型4 = -3 • 也就是说 a % b 如果 a 是负数,则得到的模也是负数。 Python 的 % 就没问题 • 7 % 4 = 3 • -7 % 4 = 1 • Python 的模运算 a % b 的值始终是 [0, b) 区间内的正数,非常方便。 对稀疏数据结构造成的问题 • 如果这里的 x 是负数,则 x % B 也是负数,会造成对 m_block 的越界访问。 • 因此 % 使用位运算不仅更高效,还能够自动解决刚刚 % 会返回负数的问题: • (因为负数用补码表示,会直接把负号去掉) & = C 语言 / 的特色:负数 • 7 / 4 = 1 • -7 / 4 = -1 • 也就是说 a / b ,如果 a 是负数,则是向上取整,如果 a 是正数,则是向下取整。 Python 的 // 就没问题 • 7 // 4 = 1 • -7 // 4 = -2 -2 • Python 的整除运算 a // b 的值始终是向下取整,非常方便。 对稀疏数据结构造成的问题 • 也就是说,如果 x 是 [-3,0] 则 x / B 会是 0 ,如果 x 是 [0,3] 则 x / B 也是 0 。导致两个 同时跑到一个 block 上去,会出错。 高效的解决:位运算 >> • 如果 b 是 2 的幂次方,即: 2, 4, 8, 16, 32 等 。0 码力 | 102 页 | 9.50 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 10 从稀疏数据结构到量化数据类型4 = -3 • 也就是说 a % b 如果 a 是负数,则得到的模也是负数。 Python 的 % 就没问题 • 7 % 4 = 3 • -7 % 4 = 1 • Python 的模运算 a % b 的值始终是 [0, b) 区间内的正数,非常方便。 对稀疏数据结构造成的问题 • 如果这里的 x 是负数,则 x % B 也是负数,会造成对 m_block 的越界访问。 • 因此 % 使用位运算不仅更高效,还能够自动解决刚刚 % 会返回负数的问题: • (因为负数用补码表示,会直接把负号去掉) & = C 语言 / 的特色:负数 • 7 / 4 = 1 • -7 / 4 = -1 • 也就是说 a / b ,如果 a 是负数,则是向上取整,如果 a 是正数,则是向下取整。 Python 的 // 就没问题 • 7 // 4 = 1 • -7 // 4 = -2 -2 • Python 的整除运算 a // b 的值始终是向下取整,非常方便。 对稀疏数据结构造成的问题 • 也就是说,如果 x 是 [-3,0] 则 x / B 会是 0 ,如果 x 是 [0,3] 则 x / B 也是 0 。导致两个 同时跑到一个 block 上去,会出错。 高效的解决:位运算 >> • 如果 b 是 2 的幂次方,即: 2, 4, 8, 16, 32 等 。0 码力 | 102 页 | 9.50 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 08 CUDA 开启的 GPU 编程获取当前线程数量 ,也就是我们在尖括号里指定的 3 。 • 可是为什么叫 blockDim ?我觉得应该叫 threadNum 才比较合理? • 小彭老师也这么觉得,可能是历史遗留下 来的问题,就不追究了。 线程之上:板块 • CUDA 中还有一个比线程更大的概念,那就是板 块( block ),一个板块可以有多个线程组成。这 就是为什么刚刚获取线程数量的变量用的是 blockDim 需要兼容一些“老年 程序”爱用的 C++03 ,不然早该换成 C++11 的 std::tuple 和 C++17 的 structual-binding 语法了……反正我是不喜欢用他的迭代器这一套,简单的问题反而复杂化。 • 怪不得王鑫磊在 ZPC 里要自己造轮子哦,虽然是 C++03 ,总感觉是几百年前的编程语言。 • 现在很多“老年”教材对 cpp 的认识也停留在 C++03 , B 站 / 油管偶尔翻出几个介绍 拆分成四步: • 读取 sum[0] 到寄存器 A • 读取 arr[i] 到寄存器 B • 让寄存器 A 的值加上寄存器 B 的值 • 写回寄存器 A 到 sum[0] • 这样有什么问题呢? 经典案例:数组求和 • 假如有两个线程分别在 i=0 和 i=1 ,同时执行: • 线程 0 :读取 sum[0] 到寄存器 A ( A=0 ) • 线程 1 :读取 sum[0] 到寄存器0 码力 | 142 页 | 13.52 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 08 CUDA 开启的 GPU 编程获取当前线程数量 ,也就是我们在尖括号里指定的 3 。 • 可是为什么叫 blockDim ?我觉得应该叫 threadNum 才比较合理? • 小彭老师也这么觉得,可能是历史遗留下 来的问题,就不追究了。 线程之上:板块 • CUDA 中还有一个比线程更大的概念,那就是板 块( block ),一个板块可以有多个线程组成。这 就是为什么刚刚获取线程数量的变量用的是 blockDim 需要兼容一些“老年 程序”爱用的 C++03 ,不然早该换成 C++11 的 std::tuple 和 C++17 的 structual-binding 语法了……反正我是不喜欢用他的迭代器这一套,简单的问题反而复杂化。 • 怪不得王鑫磊在 ZPC 里要自己造轮子哦,虽然是 C++03 ,总感觉是几百年前的编程语言。 • 现在很多“老年”教材对 cpp 的认识也停留在 C++03 , B 站 / 油管偶尔翻出几个介绍 拆分成四步: • 读取 sum[0] 到寄存器 A • 读取 arr[i] 到寄存器 B • 让寄存器 A 的值加上寄存器 B 的值 • 写回寄存器 A 到 sum[0] • 这样有什么问题呢? 经典案例:数组求和 • 假如有两个线程分别在 i=0 和 i=1 ,同时执行: • 线程 0 :读取 sum[0] 到寄存器 A ( A=0 ) • 线程 1 :读取 sum[0] 到寄存器0 码力 | 142 页 | 13.52 MB | 1 年前3
共 26 条
- 1
- 2
- 3













