Hello 算法 1.0.0b4 C++版度,因此更易于理解。在本书中,重点和难点知识 将主要通过动画和图解形式展示,而文字则作为动画和图片的解释与补充。 在阅读本书时,如果发现某段内容提供了动画或图解,建议以图为主线,以文字(通常位于图像上方)为辅, 综合两者来理解内容。 Figure 0‑2. 动画图解示例 0. 前言 hello‑algo.com 4 0.2.3. 在代码实践中加深理解 本书的配套代码托管在GitHub 4. 数组与链表 hello‑algo.com 52 4.1.2. 数组缺点 数组在初始化后长度不可变。由于系统无法保证数组之后的内存空间是可用的,因此数组长度无法扩展。而 若希望扩容数组,则需新建一个数组,然后把原数组元素依次拷贝到新数组,在数组很大的情况下,这是非 常耗时的。 // === File: array.cpp === /* 扩展数组长度 */ int *extend(int 1]; } // 更新元素数量 numsSize--; // 返回被删除元素 return num; } /* 列表扩容 */ void extendCapacity() { // 新建一个长度为原数组 extendRatio 倍的新数组 int newCapacity = capacity() * extendRatio; int *tmp = nums; nums = new0 码力 | 343 页 | 27.39 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 06 TBB 开启的并行编程之旅器的电脑,他正在渲染 cornell box 的图像, 这个图像在单核上渲染需要 4 分钟。 • 他把图像切成 4 份,每个是原来的 1/4 大小 ,这样每个小块渲染只需要 1 分钟。 • 然后他把 4 个小块发给 4 个处理器核心, 1 分钟后 4 个处理器都渲染完毕得到结果。 • 最后只需将 4 个小块拼接起来即可得到完整 的 cornell box 图像。总共只花了 1 分钟。 图形学爱 1 2 3 4 1 分 15 秒 1 分 30 秒 0 分 45 秒 0 分 30 秒 解决 1 :线程数量超过 CPU 核心数量,让系统调度保证各个核心始终饱和 • 因此,最好不是按照图像大小均匀等分,而是按照工 作量大小均匀等分。然而工作量大小我们没办法提前 知道……怎么办? • 最简单的办法:只需要让线程数量超过 CPU 核心数量 ,这时操作系统会自动启用时间片轮换调度,轮流执 15 13 5 解决 2 :线程数量不变,但是用一个队列分发和认领任务 • 但是线程数量太多会造成调度的 overhead 。 • 所以另一种解法是:我们仍是分配 4 个线程,但 还是把图像切分为 16 份,作为一个“任务”推送到 全局队列里去。每个线程空闲时会不断地从那个 队列里取出数据,即“认领任务”。然后执行,执行 完毕后才去认领下一个任务,从而即使每个任务 工作量不一也能自动适应。0 码力 | 116 页 | 15.85 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 07 深入浅出访存优化也不会越界。 ndarray :解决访问越界问题 • 我们采用了“索性分配更大数组”的办法。 • 因此我们现在给 ndarray 的模板加了一个 额外参数,用来控制边界层的大小。 • 这里我们的图像模糊操作需要向外扩张访 问 8 个元素,因此需要把 a 的边界层大小 声明为 8 ,即 ndarray<2, float, 8> 。 ndarray :解决起始地址对齐问题 有些 SIMD 指令要求地址对齐到一定字节数,否则会 structured grid )表示,那就是一个插桩操作。 • 插桩的内核( kernel )指的就是这个“周围范围”的形状(如右图三个例子) 和每个地方读取到值对修改自身值的权重等信息。 • 个人认为,图像处理中的模糊操作,或者是滤波操作,就属于插桩。有的 插桩内核各轴向是对称的(比如高斯模糊),有的是单单往一个方向延伸 很长(比如径向模糊),有的内核是正方形(箱滤波)。 • 人工智障圈子里好像管这个叫卷积( 两步走的高斯模糊 两步走的箱型滤波 黑边是因为我们 ndarray 采用的 ghost cell 避免越界,可以手动填充一下这些 ghost cell 为 clamp 之类,不过我们是性能优化课,不是图像处理课所以不卷 了 什么是两步走? 第 8 章:矩阵与莫顿码 案例:矩阵转置 • 效率很低,为什么? • 循环是 YX 序的,虽然 b(x, y) 也是 YX 序的没问题,但是 a(y0 码力 | 147 页 | 18.88 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 08 CUDA 开启的 GPU 编程dim3(n, 1, 1)>>> 的简写而已。 图片解释三维的板块和线程 • 之所以会把 blockDim 和 gridDim 分三维主要是因为 GPU 的业务常常涉及到三维图形学和二维图像,觉得 这样很方便,并不一定 GPU 硬件上是三维这样排列 的。 • 三维情况下同样可以获取总的线程编号(扁平化)。 • 如需总的线程数量: blockDim * gridDim • 如需总的线程编号: 效,可以把数组故意搞成不对齐的 33 跨步来避免。 • 顺便一提,英伟达的 warp 大小是 32 ,而 AMD 的显卡则是 64 ,其他概念如共享内存基本类似 。 第 10 章:插桩操作实战 读写图像 • 首先是读写图像的函数,利用了 stb_image 这个单头文件库。 X 方向模糊 • 然后看实现径向模糊的核函数。 Y 方向模糊 • Y 方向同理。 经典案例: jacobi 迭代 • 相比第七课0 码力 | 142 页 | 13.52 MB | 1 年前3
Hello 算法 1.0.0b1 C++版nums[randomIndex]; return randomNum; } 4.1.2. 数组缺点 数组在初始化后长度不可变。由于系统无法保证数组之后的内存空间是可用的,因此数组长度无法扩展。而若 希望扩容数组,则需新建一个数组,然后把原数组元素依次拷贝到新数组,在数组很大的情况下,这是非常耗 时的。 4. 数组与链表 hello‑algo.com 46 // === File: array.cpp === 1]; } // 更新元素数量 numsSize--; // 返回被删除元素 return num; } /* 列表扩容 */ void extendCapacity() { // 新建一个长度为 size * extendRatio 的数组,并将原数组拷贝到新数组 int newCapacity = capacity() * extendRatio; int* tmp = nums; change”按钮;页面跳转后,点击“Create pull request”按钮发起拉取请求即可。 12. 附录 hello‑algo.com 182 Figure 12‑1. 页面编辑按键 图片无法直接修改,需要通过新建 Issue 或评论留言来描述图片问题,我会第一时间重新画图并替换图片。 12.2.2. 内容创作 如果您想要参与本开源项目,包括翻译代码至其他编程语言、拓展文章内容等,那么需要实施 Pull Request0 码力 | 187 页 | 14.71 MB | 1 年前3
Hello 算法 1.0.0b2 C++版nums[randomIndex]; return randomNum; } 4.1.2. 数组缺点 数组在初始化后长度不可变。由于系统无法保证数组之后的内存空间是可用的,因此数组长度无法扩展。而若 希望扩容数组,则需新建一个数组,然后把原数组元素依次拷贝到新数组,在数组很大的情况下,这是非常耗 时的。 4. 数组与链表 hello‑algo.com 46 // === File: array.cpp === 1]; } // 更新元素数量 numsSize--; // 返回被删除元素 return num; } /* 列表扩容 */ void extendCapacity() { // 新建一个长度为 size * extendRatio 的数组,并将原数组拷贝到新数组 int newCapacity = capacity() * extendRatio; int* tmp = nums; change”按钮;页面跳转后,点击“Create pull request”按钮发起拉取请求即可。 12. 附录 hello‑algo.com 192 Figure 12‑1. 页面编辑按键 图片无法直接修改,需要通过新建 Issue 或评论留言来描述图片问题,我会第一时间重新画图并替换图片。 12.2.2. 内容创作 如果您想要参与本开源项目,包括翻译代码至其他编程语言、拓展文章内容等,那么需要实施 Pull Request0 码力 | 197 页 | 15.72 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 01 学 C++ 从 CMake 学起标准库的功能,难免会用到一些第三方库。 • 最友好的一类库莫过于纯头文件库了,这里是一些好用的 header-only 库: 1. nothings/stb - 大名鼎鼎的 stb_image 系列,涵盖图像,声音,字体等,只需单头文件! 2. Neargye/magic_enum - 枚举类型的反射,如枚举转字符串等(实现方式很巧妙) 3. g-truc/glm - 模仿 GLSL 语法的数学矢量0 码力 | 32 页 | 11.40 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 12 从计算机组成原理看 C 语言指针这里我们是在 func 内部调用了 malloc ,当然我们需要告诉调用者去 free 我们返回的指 针。 • size_t* n 用于额外返回数组的大小,如果不需要也可以不加。 • 例子:读取图像像素值作为数组的 stbi_load 函数(稍后要 stbi_free 释放他返回的指 针)。 函数需要输入或输出数组?分类讨论 • 第三种情况有一个更好的解决方案,那就是分成两个函数: •0 码力 | 128 页 | 2.95 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 13 C++ STL 容器全解之 vectorresize(4); • void resize(size_t n); vector 容器: resize • 当然, resize 也有一个接受第二参数的重载 ,他会用这个参数的值填充所有新建的元素。 • vectora(4, 233); • 等价于: • vector a; • a.resize(4, 233); • void resize(size_t 0 码力 | 90 页 | 4.93 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 17 由浅入深学习 map 容器[] 和 at() 只是返回引用,不管你是读取还是写入这个引用,函数本身的特性是不变的。 • 唯一的区别是等号在他后面,是往 K 对应的 V 里赋值。 • [] 创建在先, = 写入在后。成功写入了新建的元素。 • at 报错在先, = 写入在后。结果是报错了,没有写入。 写入 map 元素 • mapm; • m[“key”] = val; • 写入键值为 “ 0 码力 | 90 页 | 8.76 MB | 1 年前3
共 17 条
- 1
- 2













