C++高性能并行编程与优化 - 课件 - 10 从稀疏数据结构到量化数据类型从稀疏数据结构到量化数据类型 by 彭于斌( @archibate ) 往期录播: https://www.bilibili.com/video/BV1fa411r7zp 课程 PPT 和代码: https://github.com/parallel101/course 本课涵盖:稀疏矩阵、 unordered_map 、空间稀 疏网格、位运算、浮点的二进制格式、内存带宽优 化 面向人群:图形学、 >> 3 。 >> 2 = 位运算 >> 对负数的处理 signed 类型的 >> n 会把最高位复制 n 次。 因为补码的特性,这导致负数 >> 的结果仍是负 数。 这样就实现了和 Python 一样的始终向下取整除 法。 >> 2 = unsigned 类型的位运算 >> 不一样 而 unsigned 类型的 >> n 会不会复制最高位, 只是单纯的位移,这会导致负数的符号位单独被位 只是单纯的位移,这会导致负数的符号位单独被位 移,补码失效,造成结果不对。 unsigned 类型的 >> 会生成 shr 指令, signed 类型的 >> 会生成 sar 指令。 我们需要负方向无限延伸的稀疏数据结果,那就只 要 signed 那个就行。 >> 2 = 没有重合时可以用高效的加法:位运算 | • 如果可以保证 a 和 b 满足 a & b = 0 , 如: • 1011000 和 00001100 码力 | 102 页 | 9.50 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 05 C++11 开始的多线程编程3000000 微秒,也就是 3 秒 • C 语言原始的 API ,没有类型区分,导致很容易弄错单位,混淆时间点和时间段。 • 比如 t0 * 3 ,乘法对时间点而言根本是个无意义的计算,然而 C 语言把他们看做一样的 long 类型,从而容易让程序员犯错。 C++11 引入的时间标准库: std::chrono • 利用 C++ 强类型的特点,明确区分时间点与时间段,明确区分不同的时间单位。 • • 时间点例子: 2022 年 1 月 8 日 13 点 07 分 10 秒 • 时间段例子: 1 分 30 秒 • 时间点类型: chrono::steady_clock::time_point 等 • 时间段类型: chrono::milliseconds , chrono::seconds , chrono::minutes 等 • 方便的运算符重载:时间点 + 时间段 = 时间点,时间点 案例:计算花费的时间 时间段:作为 double 类型 duration_cast 可以在任意的 duration 类型之间转换 duration表示用 T 类型表示,且时间单位是 R R 省略不写就是秒, std::milli 就是毫秒, std::micro 就是微秒 seconds 是 duration 的类型别名 milliseconds 是 duration 0 码力 | 79 页 | 14.11 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 08 CUDA 开启的 GPU 编程如需总的线程编号: blockDim * blockIdx + threadIdx 三维的板块和线程编号 • CUDA 也支持三维的板块和线程区间。 • 只要在三重尖括号内指定的参数改成 dim3 类型即可。 dim3 的构造函数就是接受三 个无符号整数( unsigned int )非常简单 。 • dim3(x, y, z) • 这样在核函数里就可以通过 threadIdx.y 获取 可以直接在核函数里调用核函数并指定参数这么方便…… 不过,这个功能同样需要开启 CUDA_SEPARABLE_COMPILATION 。 第 2 章:内存管理 如何从核函数里返回数据? • 我们试着把 kernel 的返回类型声明为 int ,试 图从 GPU 返回数据到 CPU 。 • 但发现这样做会在编译期出错,为什么? • 刚刚说了 kernel 的调用是异步的,返回的时候 ,并不会实际让 GPU 把核函数执行完毕,必须 等待他执行完毕(和 线程的 join 很像)。所以,不可能从 kernel 里 通过返回值获取 GPU 数据,因为 kernel 返回 时核函数并没有真正在 GPU 上执行。所以核函 数返回类型必须是 void 。 试图解决:通过指针传递 • 那你可能会想,既然不能返回,那作为指 针传入局部变量的引用,不就好了。 • 这样,在 cudaDeviceSynchronize() 以后0 码力 | 142 页 | 13.52 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 15 C++ 系列课:字符与字符串后一次性显示出来的,并不是真的说 Ctrl 就是 ‘ ^’ 这 个字符。 C 语言字符串 第 2 章 C 语言中的字符类型 char • char c = ‘a’; • assert(c == 97); • c = c + 1; • assert(c == ‘b’); • C 语言中规定字符类型为 char 类型,是个 8 位整数。 • 这是因为 ASCII 码只有 0~127 这些整数,而 8 位整数的表示范围是 返回。 帮手函数大全 http://c.biancheng.net/ref/ctype_h/ 关于 char 类型的一个冷知识 • C 语言其实只规定了 unsigned char 是无符号 8 位整数, signed char 是有 符号 8 位整数,而 char 类型只需是 8 位整数即可,可以是有符号也可以 是无符号,任凭编译器决定( C 标准委员会传统异能, khronos 直呼内行) 的指令集处理无符号 8 位整数更高效”,所以擅自把 char 魔改成无 符号的…… • 顺便一提, C++ 标准保证 char , signed char , unsigned char 是三个完全 不同的类型, std::is_same_v 分别判断他们总会得到 false ,无论 x86 还是 arm 。 • 但是奇葩的 C 语言却规定 short , int , long , long long0 码力 | 162 页 | 40.20 MB | 1 年前3
新一代分布式高性能图数据库的构建 - 沈游人支持在用户筛选出的子图上计算 灵活的参数设定 自研图计算系统架构、极致的性能优化 深度适应客户的系统环境和算法需求 • 机器数量有限,通常小于 10 • 网络带宽不高(千兆、万兆以太网) • 需要支持各种不同类型的图计算算法 双重执行模式 • 单机和分布式两套计算系统,在不同的使用 环境中都能达到最佳性能 针对常用算法逐个设计优化方案 • 对于常用算法,跳过固定的编程模型,分 别设计最佳的计算方案 01 可静态分发的 Trait 在不带来性 能损失的同时也提高代码组织性 02 03 强大的跨平台能力,在不同架构 下可以准确的控制代码行为 编译期间对生命周期检查确保内 存安全,无 GC 和运行时损耗 01 完善的测试类型支持,包括单元 测试、集成测试、基准测试等 02 03 和文档系统以及 CI/CD 工具的良 好集成 完整的断言系统 异步协程 零成本抽象 强大的测试框架 实时图结构预览 • 用户授权管理 • 中文及显示别名支持 图模型设计 WebUI—— 可视化图探索分析 【亮点】 • K 步邻居查询、属性过滤 • 最短路径、全路径分析 • 按实体、边类型匹配查询 • 子图识别、环路识别等在 线图挖掘分析 • 实体、边可视化统计分析 • 树、层次、分组等多种布 局方式 • 基于 D3 自研的万级别实 体展现 可视化图探索分析 AtlasGraph0 码力 | 38 页 | 24.68 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 12 从计算机组成原理看 C 语言指针• 11111100 表示 252 11111101 表示 253 11111110 表示 254 11111111 表示 255 • 字节实际上就是 C 语言中的 unsigned char 类型。 表示更大范围的整数:字( word ) • 但是单单一个字节表示的范围还是太有限了,只能表示 0 到 255 的值。 • 如何扩大表示范围?简单,用两个字节合在一起即可,例如: • 00000000-00000000 11111111-11111110 表示 65534 11111111-11111111 表示 65535 • 这就是两个字节合成了一个字( word ),实际上就是 C 语言里的 unsigned short 类型 。 不同位数的计算机,字( word )的大小也不一样 • 刚刚说把 2 个字节( byte )拼成一个字( word ),实际上是 16 位计算机的做法。 • 16 位计算机得名就是因为他的字由 位计算机反而是因为 16777216 TB 太大,内存地址被阉割到了 39 位( 512GB )。 • 64 位计算机:小丑竟是我自己 lscpu 命令查看处理器相关信息 C 语言中的整数类型 C 语言的基础整数类型 类型 Unix 32 位 Unix 64 位 Windows 32 位 Windows 64 位 char 8 位 8 位 8 位 8 位 short 16 位 16 位 16 位 160 码力 | 128 页 | 2.95 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 03 现代 C++ 进阶:模板元编程multiply(Numeric *) 的话 依然会违背你们的开 - 闭原则:比如 3.14f * 3 ,两 端是不同的类型,怎么处理所有可能类型的排列组合 ? 不如放弃类和方法的概念,欣然接受全局函数和重载 。 模板函数:定义 • 使用 template• 其中 T 可以变成任意类型。 • 调用时 twice 即可将 T 替换为 int 。 • 注意有的教材上写做: • template • 是完全等价的,只是个人喜好不同。 模板函数:自动推导参数类型 • 那这样需要手动写 , 用起 来还不如重载方便了? • 别担心, C++ 规定: • 当模板类型参数 T 作为函数参数时,则可 以省略该模板参数。自动根据调用者的参 数判断。 模板函数:特化的重载 • 有时候,一个统一的实现(比如 t 模板函数:默认参数类型 • 但是如果模板类型参数 T 没有出现在函数 的参数中,那么编译器就无法推断,就不 得不手动指定了。 • 但是,可以通过 • template • 表示调用者没有指定时, T 默认为 int 。 模板参数:整数也可以作为参数 • template • 可以声明类型 T 作为模板尖括号里的参数。除了 类型,任意整数也可以作为模板参数: 0 码力 | 82 页 | 12.15 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 17 由浅入深学习 map 容器defl; • } • } • 封装成函数方便使用: • auto val = map_get(m, “key”, “default”); • ss map 常用函数不同情况下的行为分析 类型 C++ 代码 key 已存在 key 不存在 读取 val = m.at(key) 读取这个值 抛出 out_of_range 异常 val = m[key] 读取这个值 创建并零初始化(默认构造函数) 删除这个值 默默放弃 小彭老师四定律: 读取,要用 at 。 写入,要用 [] 。 判断存在,用 count 。 删除,用 erase 。 这四个已经够用了。 map 常用函数不同情况下的行为分析 类型 C++ 代码 key 已存在 key 不存在 读取 val = m.at(key) 读取这个值 抛出 out_of_range 异常 val = m[key] 读取这个值 创建并零初始化(默认构造函数) 判断是否存在,用 m.count(key) 若存在则删除,用 m.erase(key) 第四章:迭代与遍历 物理格式 逻辑格式 面壁者罗辑监督你鞋习 ! 面壁者罗辑监督你鞋习 ! map 的元素类型是…… • set::value_type 是 V 。 • map ::value_type 是 pair 。 • 这很合理,虽然只针对 K 排序,但实际上 0 码力 | 90 页 | 8.76 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 02 现代 C++ 入门:RAII 内存管理我知道可以用 accumulate 啦!但是为了引出 lambda 表达式…… 近现代: C++11 引入了 lambda 表达式 现代: C++14 的 lambda 允许用 auto 自动推断类型 当代: C++17 CTAD / compile-time argument deduction / 编 译期参数推断 当代: C++17 引入常用数值算法 未来: C++20 引入区间( ranges 其实谷歌在其 Code Style 中也明确提出别再通过 () 调用构造函数,需要类型转换时应该 用: 1. static_cast(3.14f) 而不是 int(3.14f) 2. reinterpret_cast (0xb8000) 而不是 (void *)0xb8000 • 更加明确用的哪一种类型转换( cast ),从而避免一些像是 static_cast (ptr) 译器会自动生成一 个无参构造函数 Pig() ,他会调用每个成员的无参构造函数。 • 但是请注意,这些类型不会被初始化为 0 : 1. int, float, double 等基础类型 2. void *, Object * 等指针类型 3. 完全由这些类型组成的类 • 这些类型被称为 POD ( plain-old-data )。 • POD 的存在是出于兼容性和性能的考虑。 << 0 码力 | 96 页 | 16.28 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - Zeno 中的现代 C++ 最佳实践 https://github.com/zenustech/zeno/tree/zeno2 • Zeno 1.0 所在的分支: https://github.com/zenustech/zeno/ Zeno 中的基本类型 • IObject 一切对象的公共基类。 • INode 一切节点的公共基类。 多态的经典案例 • IObject 具有一个 eatFood 纯虚函数,而 CatObject 和 DogObject IObject * 存储的指针在 delete 时只 会释放 IObject 里的成员,而不会释放 CatObject 里的成员 string m_catFood 。所以 这里的解构函数也是多态的,他根据类型的不同 调用不同派生类的解构函数。 多态用于设计模式之“模板模式” • 这样之后如果有一个任务是要基于 eatFood 做文章,比如要重复 eatFood 两遍。 • 就可以封装到一个函数 eatTwice IOBJECT_DEFINE_CLONE ,其内容是 clone 的实现。这里我们用 std::decay_t快速获取了 this 指针所指向的类型,也就是当前所在类的类型 。 • 宏的缺点是他不遵守命名空间的规则,宏的名 字是全局可见的,不符合 C++ 的高大尚封装思 想。 • 宏: IOBJECT_DEFINE_CLONE • 高大尚 C++ 0 码力 | 54 页 | 3.94 MB | 1 年前3
共 25 条
- 1
- 2
- 3













