 C++高性能并行编程与优化 -  课件 - 17 由浅入深学习 map 容器这是通常来说,不过万一小彭老师真的这么重口味在吃答辩呢?要怎么传达这个信息? C++ 一视同仁的接口就能处理这种罕见的情况,不过 Python 用一些 if 语句套一套一样可以。 深入理解 Python 中 [] 能自动区分是读是写的原理 • 写入要创建元素,而读取则要在元素不存在时出错,确实应该是两个不同的函数。 • 为什么 Python 不用区分读取和写入两个函数?只有统一的 [] ?因为 Python 作为老牌胶水语言,为了 val) 。 • val = m[key] 实际上是 val = m.__getitem__(key) 。 • C++ 的 [] 就不论读取还是写入都是同一个运算符重载,他只是返回引用,无法区分你是读是写: • value_type &operator[](key_type key); // [] 返回引用,你 = val 写入的是这个引用, [] 是不知道的 • [] 被调用的时候根本 建立引用 ) map 中的 堆空间 • 何况 structural-binding 捕获的引用比刚刚图示的还要优化。他只会保存一个指向 pair 类 型的指针,然后在你使用 k 和 v 时再去按偏移量访问里面的 first 和 second ,所以 k , v 两个变量的 structural-binding 引用其实是一个引用,只占一个指针的空间( 8 字 节)。 • 也就是说,现在只要0 码力 | 90 页 | 8.76 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 17 由浅入深学习 map 容器这是通常来说,不过万一小彭老师真的这么重口味在吃答辩呢?要怎么传达这个信息? C++ 一视同仁的接口就能处理这种罕见的情况,不过 Python 用一些 if 语句套一套一样可以。 深入理解 Python 中 [] 能自动区分是读是写的原理 • 写入要创建元素,而读取则要在元素不存在时出错,确实应该是两个不同的函数。 • 为什么 Python 不用区分读取和写入两个函数?只有统一的 [] ?因为 Python 作为老牌胶水语言,为了 val) 。 • val = m[key] 实际上是 val = m.__getitem__(key) 。 • C++ 的 [] 就不论读取还是写入都是同一个运算符重载,他只是返回引用,无法区分你是读是写: • value_type &operator[](key_type key); // [] 返回引用,你 = val 写入的是这个引用, [] 是不知道的 • [] 被调用的时候根本 建立引用 ) map 中的 堆空间 • 何况 structural-binding 捕获的引用比刚刚图示的还要优化。他只会保存一个指向 pair 类 型的指针,然后在你使用 k 和 v 时再去按偏移量访问里面的 first 和 second ,所以 k , v 两个变量的 structural-binding 引用其实是一个引用,只占一个指针的空间( 8 字 节)。 • 也就是说,现在只要0 码力 | 90 页 | 8.76 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 07 深入浅出访存优化常见操作所花费的时间 • 图中加法 (add) 和乘法 (mul) 都指的整数。 • 区别是浮点的乘法和加法基本是一样速度。 • L1/2/3 read 和 Main RAM read 的时间指的是 读一个缓存行( 64 字节)所花费的时间。 • 根据计算: 125/64*4≈8 • 即从主内存读取一次 float 花费 8 个 cycle , 符合小彭老师的经验公式。 • “right” 和“ 宽。三级缓存也装不下,那就取决于主内存 的带宽了。 • 结论:要避免 mem-bound ,数据量尽量足 够小,如果能装的进缓存就高效了。 L2: 256 KB L3: 12 MB 缓存的工作机制:读 • 缓存中存储的数据结构: • struct CacheEntry { • bool valid; • uint64_t address; • char data[64]; CacheEntry cache[512]; • 当 CPU 读取一个地址时: • 缓存会查找和该地址匹配的条目。如果找到,则给 CPU 返 回缓存中的数据。如果找不到,则向主内存发送请求,等读 取到该地址的数据,就创建一个新条目。 • 在 x86 架构中每个条目的存储 64 字节的数据,这个条目 又称之为缓存行( cacheline )。 • 当访问 0x0048~0x0050 这0 码力 | 147 页 | 18.88 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 07 深入浅出访存优化常见操作所花费的时间 • 图中加法 (add) 和乘法 (mul) 都指的整数。 • 区别是浮点的乘法和加法基本是一样速度。 • L1/2/3 read 和 Main RAM read 的时间指的是 读一个缓存行( 64 字节)所花费的时间。 • 根据计算: 125/64*4≈8 • 即从主内存读取一次 float 花费 8 个 cycle , 符合小彭老师的经验公式。 • “right” 和“ 宽。三级缓存也装不下,那就取决于主内存 的带宽了。 • 结论:要避免 mem-bound ,数据量尽量足 够小,如果能装的进缓存就高效了。 L2: 256 KB L3: 12 MB 缓存的工作机制:读 • 缓存中存储的数据结构: • struct CacheEntry { • bool valid; • uint64_t address; • char data[64]; CacheEntry cache[512]; • 当 CPU 读取一个地址时: • 缓存会查找和该地址匹配的条目。如果找到,则给 CPU 返 回缓存中的数据。如果找不到,则向主内存发送请求,等读 取到该地址的数据,就创建一个新条目。 • 在 x86 架构中每个条目的存储 64 字节的数据,这个条目 又称之为缓存行( cacheline )。 • 当访问 0x0048~0x0050 这0 码力 | 147 页 | 18.88 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 性能优化之无分支编程 Branchless Programming比一堆 if-else 更高效。但是实际上在编译 器看来是一样的,不管你 if-else 还是 switch ,他都会想方设法帮你优化成查表 法。 • 所以不用纠结性能,你觉得哪种写起来可 读性强,容易维护,你就怎么写。 无分支优化的方法:查表法 • 如果每个判断的值是连续的,这种情况一般 会建立一个表(数组)。 • 这个表里每个元素就是原来要返回的一个个 值,索引就是要判断的参数 • 而函数指针也是无条件跳转指令: jmp [pointer] 或者说 call [pointer] ,区别在于他的地址 不是写死的,而是动态从内存中读取出来的。 • 普通函数调用的目的地址(或偏移量)写死在指令里, CPU 可以自动预取这个地址的指令。 • 但是函数指针的调用,因为这个目的地址是需要计算得出的,或者说他存在内存中,随时可 能被改写, CPU 难以预判执行到 call [pointer]0 码力 | 47 页 | 8.45 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 性能优化之无分支编程 Branchless Programming比一堆 if-else 更高效。但是实际上在编译 器看来是一样的,不管你 if-else 还是 switch ,他都会想方设法帮你优化成查表 法。 • 所以不用纠结性能,你觉得哪种写起来可 读性强,容易维护,你就怎么写。 无分支优化的方法:查表法 • 如果每个判断的值是连续的,这种情况一般 会建立一个表(数组)。 • 这个表里每个元素就是原来要返回的一个个 值,索引就是要判断的参数 • 而函数指针也是无条件跳转指令: jmp [pointer] 或者说 call [pointer] ,区别在于他的地址 不是写死的,而是动态从内存中读取出来的。 • 普通函数调用的目的地址(或偏移量)写死在指令里, CPU 可以自动预取这个地址的指令。 • 但是函数指针的调用,因为这个目的地址是需要计算得出的,或者说他存在内存中,随时可 能被改写, CPU 难以预判执行到 call [pointer]0 码力 | 47 页 | 8.45 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 10 从稀疏数据结构到量化数据类型函数,他位于 sys/mman.h 头文件里。 • Windows 可以用 VirtualAllocateEx 之类。 • mmap 出来的起始地址保证是对齐到 4KB 的,读写访问其 中偏移地址时,会按页的粒度自动分配和释放内存,从而满 足稀疏数据结构“按需分配”的需求。且由于分页是硬件自动 来做的,比我们软件哈希和指针数组的稀疏更高效,写起来 就和普通的二维数组没什么两样,就好像顺序访问。也用不 着什么访问者缓存坐标和块指针了,硬件的 TLB 就是我们 的访问者缓存,而且超快不需要用户自己写。 • 垃圾回收可用 madvice 提前释放一段页面。 • 除此之外, mmap 还有一个好处,他会保证其内存(被读 取访问时)是零初始化的。 配合莫顿分块, AOSOA 等第七课的技术,就得到 SPGrid(sparse-paged grid) SPGrid 还支持自适应的网格 SPGrid 的利弊 •0 码力 | 102 页 | 9.50 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 10 从稀疏数据结构到量化数据类型函数,他位于 sys/mman.h 头文件里。 • Windows 可以用 VirtualAllocateEx 之类。 • mmap 出来的起始地址保证是对齐到 4KB 的,读写访问其 中偏移地址时,会按页的粒度自动分配和释放内存,从而满 足稀疏数据结构“按需分配”的需求。且由于分页是硬件自动 来做的,比我们软件哈希和指针数组的稀疏更高效,写起来 就和普通的二维数组没什么两样,就好像顺序访问。也用不 着什么访问者缓存坐标和块指针了,硬件的 TLB 就是我们 的访问者缓存,而且超快不需要用户自己写。 • 垃圾回收可用 madvice 提前释放一段页面。 • 除此之外, mmap 还有一个好处,他会保证其内存(被读 取访问时)是零初始化的。 配合莫顿分块, AOSOA 等第七课的技术,就得到 SPGrid(sparse-paged grid) SPGrid 还支持自适应的网格 SPGrid 的利弊 •0 码力 | 102 页 | 9.50 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 08 CUDA 开启的 GPU 编程*dst = src; • 为什么需要这么复杂的一个原子指令? atomicCAS :可以实现任意原子操作 • atomicCAS 的作用在于他可以用来实现任 意 CUDA 没有提供的原子读 - 修改 - 写回 指令。比如这里我们通过 atomicCAS 实 现了整数 atomicAdd 同样的效果。 atomicCAS :可以实现任意原子操作 • 里面换成 expect * src 了。 • 但是因为我们的目的是做矩阵转置,无论是 in 还是 out 必然有一个是需要跨步的,怎么办? • 因此可以先通过把 in 分块,按块跨步地读,而块内部则仍是连续地读——从低效全局的内存读 到高效的共享内存中,然后在共享内存中跨步地读,连续地写到 out 指向的低效的全局内存中 。 • 这样跨步的开销就开在高效的共享内存上,而不是低效的全局内存上,因此会变快。 共享内存:什么是区块(0 码力 | 142 页 | 13.52 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 08 CUDA 开启的 GPU 编程*dst = src; • 为什么需要这么复杂的一个原子指令? atomicCAS :可以实现任意原子操作 • atomicCAS 的作用在于他可以用来实现任 意 CUDA 没有提供的原子读 - 修改 - 写回 指令。比如这里我们通过 atomicCAS 实 现了整数 atomicAdd 同样的效果。 atomicCAS :可以实现任意原子操作 • 里面换成 expect * src 了。 • 但是因为我们的目的是做矩阵转置,无论是 in 还是 out 必然有一个是需要跨步的,怎么办? • 因此可以先通过把 in 分块,按块跨步地读,而块内部则仍是连续地读——从低效全局的内存读 到高效的共享内存中,然后在共享内存中跨步地读,连续地写到 out 指向的低效的全局内存中 。 • 这样跨步的开销就开在高效的共享内存上,而不是低效的全局内存上,因此会变快。 共享内存:什么是区块(0 码力 | 142 页 | 13.52 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 05 C++11 开始的多线程编程喝厕所里的水时,可以多个人插着吸管一起喝。 • 而拉的时候,只能一个人独占厕所,不能多个人一起往里面拉。 • 喝水的人如果发现厕所里已经有人在拉,那他也不能去喝,否则会喝到“脏数据”。 • 结论:读可以共享,写必须独占,且写和读不能共存。 • 针对这种更具体的情况,又发明了读写锁,他允许的状态有: 1. n 个人读取,没有人写入。 2. 1 个人写入,没有人读取。 3. 没有人读取,也没有人写入。 读写锁: Viewer 模式,王鑫磊常用于设计 GPU 容器 OpenVDB 数据结构的访问,也是采用了 Accessor 的设计…… 并且还有 ConstAccessor 和 Accessor 两种,分别对应于读和 写 同学们可以想想看,如果这里的 m_mtx 改成读写锁 ,要如何实现 ConstAccessor access() const ? 第 6 章:条件变量 条件变量:等待被唤醒 • cv.wait(lck) 写入原子变量, 同时返回其旧的值。 compare_exchange_strong :读取,比较是否相等,相等则写入 • compare_exchange_strong(old, val) 会读 取原子变量的值,比较他是否和 old 相等 : • 如果不相等,则把原子变量的值写入 old 。 • 如果相等,则把 val 写入原子变量。 • 返回一个 bool 值,表示是否相等。0 码力 | 79 页 | 14.11 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 05 C++11 开始的多线程编程喝厕所里的水时,可以多个人插着吸管一起喝。 • 而拉的时候,只能一个人独占厕所,不能多个人一起往里面拉。 • 喝水的人如果发现厕所里已经有人在拉,那他也不能去喝,否则会喝到“脏数据”。 • 结论:读可以共享,写必须独占,且写和读不能共存。 • 针对这种更具体的情况,又发明了读写锁,他允许的状态有: 1. n 个人读取,没有人写入。 2. 1 个人写入,没有人读取。 3. 没有人读取,也没有人写入。 读写锁: Viewer 模式,王鑫磊常用于设计 GPU 容器 OpenVDB 数据结构的访问,也是采用了 Accessor 的设计…… 并且还有 ConstAccessor 和 Accessor 两种,分别对应于读和 写 同学们可以想想看,如果这里的 m_mtx 改成读写锁 ,要如何实现 ConstAccessor access() const ? 第 6 章:条件变量 条件变量:等待被唤醒 • cv.wait(lck) 写入原子变量, 同时返回其旧的值。 compare_exchange_strong :读取,比较是否相等,相等则写入 • compare_exchange_strong(old, val) 会读 取原子变量的值,比较他是否和 old 相等 : • 如果不相等,则把原子变量的值写入 old 。 • 如果相等,则把 val 写入原子变量。 • 返回一个 bool 值,表示是否相等。0 码力 | 79 页 | 14.11 MB | 1 年前3
 谈谈MYSQL那点事话, 就将读写都放在 M1 上面, M2 只是作为 standby 。 比如,订单处理流程,那么对读需要强一致性,实时写实 时读,类似种涉及交易的或者动态实时报表统计的都要采 用这种架构模式 弱一致性 如果是弱一致性的话,可以通过在 M2 上面分担一些读压力 和流量,比如一些报表的读取以及静态配置数据的读取模块 都可以放到 M2 上面。比如月统计报表,比如首页推荐商品 业务实时性要求不是很高,完全可以采用这种弱一致性的设 用来存放索引区块的缓存值 , 建议 128M 以上,不要大于内存的 30% read_buffer_size 128K 64M 用来做 MyISAM 表全表扫描的缓冲大 小 . 为从数据表顺序读取数据的读操 作保留的缓存区的长度 myisam_sort_buffer_size 16M 128M 设置 , 恢复 , 修改表的时候使用的缓冲 大小,值不要设的太大 服务优化 服务优化 InnoDB InnoDB0 码力 | 38 页 | 2.04 MB | 1 年前3 谈谈MYSQL那点事话, 就将读写都放在 M1 上面, M2 只是作为 standby 。 比如,订单处理流程,那么对读需要强一致性,实时写实 时读,类似种涉及交易的或者动态实时报表统计的都要采 用这种架构模式 弱一致性 如果是弱一致性的话,可以通过在 M2 上面分担一些读压力 和流量,比如一些报表的读取以及静态配置数据的读取模块 都可以放到 M2 上面。比如月统计报表,比如首页推荐商品 业务实时性要求不是很高,完全可以采用这种弱一致性的设 用来存放索引区块的缓存值 , 建议 128M 以上,不要大于内存的 30% read_buffer_size 128K 64M 用来做 MyISAM 表全表扫描的缓冲大 小 . 为从数据表顺序读取数据的读操 作保留的缓存区的长度 myisam_sort_buffer_size 16M 128M 设置 , 恢复 , 修改表的时候使用的缓冲 大小,值不要设的太大 服务优化 服务优化 InnoDB InnoDB0 码力 | 38 页 | 2.04 MB | 1 年前3
 Go读书会第二期技术图书阅读:从外刊到内刊 Go 中文图书 Go 外文图书 读书方法 精读 • 选择高质量图书 • 脑图 + 细节摘录 + 行动清单(输出) 泛读 • 闲书 ( 不烧脑 ) • 碎片化(快读) + 听书 小结 第四部分 小结 • 写书三要素 • Go 精进之路导读:思维先行,践行哲学,遵循惯例,认清本 质,理解原理 • 读书:选高质量图书精读 ( 脑图 + 细节摘录 + 行动清单)0 码力 | 26 页 | 4.55 MB | 1 年前3 Go读书会第二期技术图书阅读:从外刊到内刊 Go 中文图书 Go 外文图书 读书方法 精读 • 选择高质量图书 • 脑图 + 细节摘录 + 行动清单(输出) 泛读 • 闲书 ( 不烧脑 ) • 碎片化(快读) + 听书 小结 第四部分 小结 • 写书三要素 • Go 精进之路导读:思维先行,践行哲学,遵循惯例,认清本 质,理解原理 • 读书:选高质量图书精读 ( 脑图 + 细节摘录 + 行动清单)0 码力 | 26 页 | 4.55 MB | 1 年前3
 新一代分布式高性能图数据库的构建 - 沈游人i32 i64 u32 u32 string string 定长 变长 高可用技术方案 基于 Chain Replication ( CRAQ ) 算法实现,进行数据副本处理,头 结点写,多结点读,支持读写分离 ,提供更好的并发查询能力 数据高可用实现 Chain Replication 数据高可用方案 服务高可用实现 系统中 Meta , TS 服务采用主备架 构,基于 Raft0 码力 | 38 页 | 24.68 MB | 1 年前3 新一代分布式高性能图数据库的构建 - 沈游人i32 i64 u32 u32 string string 定长 变长 高可用技术方案 基于 Chain Replication ( CRAQ ) 算法实现,进行数据副本处理,头 结点写,多结点读,支持读写分离 ,提供更好的并发查询能力 数据高可用实现 Chain Replication 数据高可用方案 服务高可用实现 系统中 Meta , TS 服务采用主备架 构,基于 Raft0 码力 | 38 页 | 24.68 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 15 C++ 系列课:字符与字符串’ h’ ,只保留前三个字符 • string(“hello”, 12) 会得到 “ hello\0[ 数据删除 ]” • ↑ len 为 12 , ptr 指向 ’ h’ ,超出了 6 个字符,内存读越界(出错) • string(“hello\0world!”, 12) 会得到 “ hello\0world!” • ↑ len 为 12 , ptr 指向 ’ h’ ,字符串内可以包含 ‘ \0’0 码力 | 162 页 | 40.20 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 15 C++ 系列课:字符与字符串’ h’ ,只保留前三个字符 • string(“hello”, 12) 会得到 “ hello\0[ 数据删除 ]” • ↑ len 为 12 , ptr 指向 ’ h’ ,超出了 6 个字符,内存读越界(出错) • string(“hello\0world!”, 12) 会得到 “ hello\0world!” • ↑ len 为 12 , ptr 指向 ’ h’ ,字符串内可以包含 ‘ \0’0 码力 | 162 页 | 40.20 MB | 1 年前3
共 10 条
- 1













