KubeCon2020/大型Kubernetes集群的资源编排优化0 码力 | 27 页 | 3.91 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 07 深入浅出访存优化2667*16*2=42672 MB/s • 那么,频率相同的情况下,可以考虑插两块 8GB 的内存, 比插一块 16GB 的内存更快,不过价格可能还是翻倍的。 • 系统会自动在两者之间均匀分配内存,保证读写均匀分配 到两个内存上,实现内存的并行读写,这和磁盘 RAID 有 一定相似之处。 验证一下刚刚的 parallel_add 是不是用足了全部带宽 • 刚刚 a 数组的大小是 1024 MB 等待数据抵达前空转浪费时间。 页对齐的重要性 • 为什么要 4KB ?原来现在操作系统管理内存是用分页 ( page ),程序的内存是一页一页贴在地址空间中的, 有些地方可能不可访问,或者还没有分配,则把这个页设 为不可用状态,访问他就会出错,进入内核模式。 • 因此硬件出于安全,预取不能跨越页边界,否则可能会触 发不必要的 page fault 。所以我们选用页的大小,因为本 来就不 同学们可以课后研究一下。 第 5 章:内存分配与分页 vector :写入两次,时间都是一样的(理所当然) malloc :写入两次,第一次明显比第二次慢? new int[n] :和 malloc 一样,写入两次,第一次明显比第二次慢? new int[n]{} :后面加个花括号,就和 vector 一样,两次一样快了 结论 • 原理,当调用 malloc 时,操作系统并不会实际分配那一块内存,而是将这一段内存标记0 码力 | 147 页 | 18.88 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 02 现代 C++ 入门:RAII 内存管理,数组大小 nv 将多个逻辑上相关的变量包装成一个类 因此 C++ 的 vector 将他俩打包起来,避免程序员犯错 封装:不变性 比如当我要设置数组大小为 4 时,不能只 nv = 4 还要重新分配数组内存,从而修改数组起始地址 v 常遇到:当需要修改一个成员时,其他也成员需要被修改,否则出错 这种情况出现时,就意味着你需要把成员变量的读写封装为成员函数 不变性:请勿滥用封装 • 仅当出现“修改一个成员时,其他也成员要 getter/setter 函数分离了声明和定 义,实现在另一个文件时! C++ 思想: RAII ( Resource Acquisition Is Initialization ) 资源获取视为初始化,反之,资源释放视为销毁 C++ 除了用于初始化的构造函数( constructor ) 还包括了用于销毁的解构函数( destructor ) 离开 {} 作用域自动释放 手动释放 RAII Python 等垃圾回收语言不同, C++ 的 解构函数是显式的,离开作用域自动销毁,毫不含 糊(有好处也有坏处,对高性能计算而言利大于 弊) 如果没有解构函数,则每个带有返回的分 支都要手动释放所有之前的资源 : RAII :异常安全( exception-safe ) C++ 标准保证当异常发生时,会调用已创建对象的解构函数 。 因此 C++ 中没有(也不需要) finally 语句。 如果此处不关闭,则可等0 码力 | 96 页 | 16.28 MB | 1 年前3
 Zadig 面向开发者的云原生 DevOps 平台项目间依赖复杂,环境管理难 • 交付版本依赖工单,发布风险高 • 公共资源 / 业务资源利用率低 赋能多业务:一个平台解决了多异构项目的管理和规范 团队高效协作:定义团队角色工作流模板,随时可用云上环境 价值清晰呈现:为管理者提供全视角效能数据,赋能数字决策 人工低效操作减少 80% 构建资源利用率提升 60% 业务资源利用率提升 30% 统一治理内部规范,开发 自助上线;解放运维,工 面向多服务并行部署,安全发布, 0 维护负担 支撑云原生构建 / 运行环境,多云异构支持及企业 级登录权限支持 传统运维管理类平台 蓝鲸 Rainbond KubeSphere KubeVela 面向资源管理的运维工具集 面向开发者,需结合 CI/CD 工具额外搭建 全流程能力 专门面向开发者的生产力平台,涵盖全流程需求到 开发,测试,运维的云原生一体化技术底座支撑 云厂商 DevOps 平台 度,无需与运维持续沟通 降低个人心智负担 • 通过平台工程,将底层的复杂性 抽象化,降低个人心智负担,提 高开发效率 可重用降低运维成本 • 一些组织可能过度依赖高级工程 师管理发布流程和基础设施,导 致资源浪费和效率低下 Zadig 平台工程模式 工程规模数据: • 1500+ 产研工程师 Vs. 2 人运维 • 50 + 个 Kubernetes 全球集群 • 300+ 个数字产品(资产沉淀)0 码力 | 59 页 | 81.43 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 05 C++11 开始的多线程编程内存之后从开始执行到执行结束的这样一个 过程。简单来说,进程是程序(应用程序,可执行文件)的一次执行。比如双击打开一个 桌面应用软件就是开启了一个进程。 • 线程是进程中的一个实体,是被系统独立分配和调度的基本单位。也有说,线程是 CPU 可 执行调度的最小单位。也就是说,进程本身并不能获取 CPU 时间,只有它的线程才可以。 • 从属关系:进程 > 线程。一个进程可以拥有多个线程。 • 类的成员函数 join() 来等待该进程结束。 std::thread 的解构函数会销毁线程 • 作为一个 C++ 类, std::thread 同样遵循 RAII 思想和三五法则:因为管理着资源, 他自定义了解构函数,删除了拷贝构造 / 赋 值函数,但是提供了移动构造 / 赋值函数。 • 因此,当 t1 所在的函数退出时,就会调用 std::thread 的解构函数,这会销毁 t1 是个厕所, A 同学在用了, B 同学就不能进去,要等 A 同学用完了才能进 去。 std::lock_guard :符合 RAII 思想的上锁和解锁 • 根据 RAII 思想,可将锁的持有视为资源 ,上锁视为锁的获取,解锁视为锁的释放 。 • std::lock_guard 就是这样一个工具类,他 的构造函数里会调用 mtx.lock() ,解构函 数会调用 mtx.unlock()0 码力 | 79 页 | 14.11 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 12 从计算机组成原理看 C 语言指针小端序的电脑会得到 0x04030201 。 动态数组的分配与释放 如果不是固定长度为 4 的数组呢? • 刚才的 char a[4] ,数组的长度是一个编译期常 量。如果不是常量呢? • 比如 char a[n] ,在 gcc 上居然是可以编译通 过的,这是因为他调用了 gcc 特有的 alloca(n) 函数,会在当前函数的栈上分配内存,函数退出 时也会自动释放。 如果不是固定长度为 是 gcc 特有的函数,微软比较笨,所以不支持。 • 因此栈上动态数组不是标准的 C 语言特 性,是无法跨平台使用的。 • 所以一般认为栈上的东西都是固定长度的 。 • DIDU_KNOW_THAT_MICROPIG_BUYS_GITHUB 使用 malloc 函数:在堆上分配内存,实现动态数组 • 栈上不能动态分配内存,堆上就可以! • char a[4] 可以在编译期确定一片栈上的连 malloc(n) 就可以从堆 上分配一段 n 字节的连续内存,这里的 n 就可以不是常量了。 • 栈上的内存会在函数退出时自动释放,而 堆上的内存不会,需要手动 free(a) 释放 。 动态数组作为函数参数?不仅要传首地址,还要传数组长度! • 刚才说定长数组(长度是编译期常量,在栈上分 配的)只需一个起始地址就能确定。 • 而动态长度的数组(通过 malloc 在堆上分配 的)就需要起始地址和数组长度两个,才能确定0 码力 | 128 页 | 2.95 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 08 CUDA 开启的 GPU 编程,也就是说核函数 可以调用另一个核函数,且其三重尖括号 里的板块数和线程数可以动态指定,无需 先传回到 CPU 再进行调用,这是 CUDA 特有的能力。 常用于这种情况:需要从 GPU 端动态计算出 blockDim 和 gridDim ,而又不希望导回数据到 CPU 导致强制同步影响性能。 这种模式被称为动态并行( dynamic parallelism ), OpenGL 有一 个 ize()) • 即可自动帮你检查错误代码并打印在终端,然后退出。还会 报告出错所在的行号,函数名等,很方便。 堆上分配试试? • 那你可能会想,难道是因为我的 ret 创建 在栈上,所以 GPU 不能访问,才出错的 ? • 于是你试图用 malloc 在堆上分配一个 int 来给 GPU 访问,结果还是失败了。 原因: GPU 使用独立的显存,不能访问 CPU 内存 • 原来, (host) 。 GPU 使 用的内存称为设备内存 (device) ,他是显卡上板载 的,速度更快,又称显存。 • 而不论栈还是 malloc 分配的都是 CPU 上的内存 ,所以自然是无法被 GPU 访问到。 • 因此可以用用 cudaMalloc 分配 GPU 上的显存, 这样就不出错了,结束时 cudaFree 释放。 • 注意到 cudaMalloc 的返回值已经用来表示错误代 码,所以返回指针只能通过0 码力 | 142 页 | 13.52 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 13 C++ STL 容器全解之 vectorfunctor ) C++ 标准库五大件:分配器( allocator ) 侯捷 STL 侯捷 STL vector 容器 vector 容器:构造函数 • vector 的功能是长度可变的数组,他里面的数据 存储在堆上。 • vector 是一个模板类,第一个模板参数是数组里 元素的类型。 • 例如,声明一个元素是 int 类型的动态数组 a : • vectora; noexcept; vector 容器: data() 获取首地址指针 • data() 返回的首地址指针,通常配合 size() 返回的数组长度一起使用(见上一课《 C 语言指针》中提到,连续的动态数组只需要 知道首地址和数组长度即可完全确定)。 • 用他来获取一个 C 语言原始指针 int * , 很方便用于调用 C 语言的函数和 API 等 ,同时还能享受到 vector 容器 RAII 容器:生命周期由主对象管理 • C++ 中哪个运算符是最强的?我觉得是 } • 因为 } 标志着一个语句块的结束,在这里,他 会调用所有身处其中的对象的解构函数。比如 这里的 vector ,他的解构函数会释放动态数组 的内存(即自动 delete )。 • vector 会在退出作用域时释放内存,这时候所 有指向其中元素的指针,包括 data() 都会失效。 因此如果你是在语句块内获取的 data() 0 码力 | 90 页 | 4.93 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 06  TBB 开启的并行编程之旅互。并在主线程中等待该任务组里的任务 全部执行完毕。 • 区别在于,一个任务不一定对应一个线程 ,如果任务数量超过 CPU 最大的线程数, 会由 TBB 在用户层负责调度任务运行在 多个预先分配好的线程,而不是由操作系 统负责调度线程运行在多个物理核心。 封装好了: parallel_invoke 更好的例子 第 1 章:并行循环 时间复杂度( time-efficiency )与工作量复杂度( 8-5_12 任务域: tbb::task_arena 任务域:指定使用 4 个线程 嵌套 for 循环 嵌套 for 循环:死锁问题 死锁问题的原因 • 因为 TBB 用了工作窃取法来分配任务: 当一个线程 t1 做完自己队列里全部的工 作时,会从另一个工作中线程 t2 的队列 里取出任务,以免 t1 闲置浪费时间。 • 因此内部 for 循环有可能“窃取”到另一个 外部 for :创建另一个任务域,这样不同域之间就不会窃取工作 解决 3 :同一个任务域,但用 isolate 隔离,禁止其内部的工作被窃取 (推荐) 第 5 章:任务分配 https://link.springer.com/chapter/10.1007%2F978-1-4842-4398-5_12 并行:如何均匀分配任务到每个线程? • 对于并行计算,通常都是 CPU 有几个核心就开 几个线程,因为我们只要同时执行就行了嘛。 • 比如0 码力 | 116 页 | 15.85 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 15 C++ 系列课:字符与字符串char * 类型 传递给其他函数时,其数组的长度无法知晓。为了确切知 道数组在什么地方结束,规定用 ASCII 码中的“空字符”也 就是 0 来表示数组的结尾。这样只需要一个首地址指针就 能表示一个动态长度的数组,高,实在是高。 “0 结尾字符串”知识点应用举例 • 利用 C 语言字符串“以 0 结尾”这个特点,我们可以在一个 本来非 0 的字符处写入 0 ,来提前结束字符串。例如在第 n string 。 字符串胖指针 第 7 章 C 语言 0 结尾字符串 • C 语言为什么喜欢 0 结尾字符串( null-terminated string )呢? • 众所周知,要描述一个动态长度的数组(此处为字符串),需要首地址指针和 数组长度两个参数。 • void cihou_array(char *ptr, size_t len); • 这样好麻烦,明明是一个字符串,却要传两个参数进去!能不能简化? O(n) ,如果需要频繁的查询长度,势必会非常低效。 • 4. 若需要在尾部切片就得修改字符串本身(写入一个 ‘ \0’ ),影响其他弱引 用。 胖指针大法横空出世 • 刚刚说了,要描述一个动态长度的数组(此处为字符串),需要首地址指针和 数组长度两个参数。 • void cihou_array(char *ptr, size_t len); • 可以把这描述同一个东西的两个参数,打包进一个结构体(0 码力 | 162 页 | 40.20 MB | 1 年前3
共 27 条
- 1
 - 2
 - 3
 













