Hidden Overhead of a Function APIperformance, we typically think about the function logic. We’ll see that a well designed function API can have an even larger impact.How will we compare performance? ● Benchmarks at this low level are advance(RandIter& iter, Diff n, random_access_iterator_tag) { iter += n; } ● Access token to make some API available only inside the library (like the default “package private” access modifier in Java) Empty0 码力 | 158 页 | 2.46 MB | 6 月前3
GraphBLAS: Building a C++ Matrix API for Graph Algorithmsthe important data structures and concepts? Prior work in the GraphBLAS community, C API Overview of our draft C++ API How might this interoperate with standard C++, graph library proposal? 4[DISTRIBUTION the important data structures and concepts? Prior work in the GraphBLAS community, C API Overview of our draft C++ API How might this interoperate with standard C++, graph library proposal? 5[DISTRIBUTION the important data structures and concepts? Prior work in the GraphBLAS community, C API Overview of our draft C++ API How might this interoperate with standard C++, graph library proposal? 6[DISTRIBUTION0 码力 | 172 页 | 7.40 MB | 6 月前3
C++高性能并行编程与优化 - 课件 - 08 CUDA 开启的 GPU 编程线程,用于处理大吞吐量的数据。 获取线程编号 • 可以通过 threadIdx.x 获取当前线程的编 号,我们打印一下试试看。 • 这是 CUDA 中的特殊变量之一,只有在 核函数里才可以访问。 • 可以看到线程编号从 0 开始计数,打印出 了 0 , 1 , 2 。这也是我们指定了线程数 量为 3 的缘故。 • 等等,为什么后面有个 .x ?稍后再说明。 获取线程数量 • 还可以用 ,而又不希望导回数据到 CPU 导致强制同步影响性能。 这种模式被称为动态并行( dynamic parallelism ), OpenGL 有一 个 glDispatchComputeIndirect 的 API 和这个很像,但毕竟没有 CUDA 可以直接在核函数里调用核函数并指定参数这么方便…… 不过,这个功能同样需要开启 CUDA_SEPARABLE_COMPILATION 。 第 2 章:内存管理 int 。 • 可以通过 cudaGetErrorName 获取该 enum 的具体名 字。这里显示错误号为 77 ,具体名字是 cudaErrorIllegalAddress 。意思是我们访问了非法的地 址,和 CPU 上的 Segmentation Fault 差不多。 封装好了: helper_cuda.h • 其实 CUDA toolkit 安装时,会默认附带一系列案例代码,0 码力 | 142 页 | 13.52 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 12 从计算机组成原理看 C 语言指针字的长度除了决定一次处理的整数大小之外,还决定了能访问的内存地址的范围。 • 这是因为内存是一维排列的,假如内存容量是 65536 字节,那所谓的内存地址实际上就 是一个从 0 到 65535 范围的整数,也就是两个字节组成的字。 • 处理器去读写内存的时候靠的是寄存器提供的地址,因此寄存器的大小(也就是字的大 小)决定了他能读写的内存大小,例如: • 由于 16 位计算机的寄存器只能存储 16 位,他只能访问 65536 字节( )的内存 。 • 由于 32 位计算机的寄存器只能存储 32 位,他只能访问 4 GB 的内存。 • 由于 64 位计算机的寄存器能存储 64 位,他理论上能访问 16777216 TB 的内存! • 因此,如果你的电脑内存超过了 4 GB ,那肯定是 32 位电脑不用说了。 • 而 64 位计算机理论上能访问如此大量的内存,虽然目前看来是用不到。 知识拓展 • 虽然 64 位计算机的寄存器能处理 x64 架构实际上只能访 问 512GB 内存,如果插了超过这个大小的内存条他也不会认出来。 • 此外, 16 位计算机实际上能通过额外的段寄存器访问到 20 位的内存地址( 1MB )。 • 32 位计算机还能通过 PAE 技术(物理地址扩展)访问到 36 位的内存地址( 64GB ) 。 • 64 位计算机反而是因为 16777216 TB 太大,内存地址被阉割到了 39 位( 512GB0 码力 | 128 页 | 2.95 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 13 C++ STL 容器全解之 vector4 。 • size_t size() const noexcept; vector 容器: operator[] • 要访问 vector 里的元素,只需用 [] 运算符 : • 例如 a[0] 访问第 0 个元素(人类的第一 个) • 例如 a[1] 访问第 1 个元素(人类的第二 个) • int &operator[](size_t i) noexcept; • int const const noexcept; vector 容器: operator[] • 值得注意的是, [] 运算符在索引超出数组大 小时并不会直接报错,这是为了性能的考虑。 • 如果你不小心用 [] 访问了越界的索引,可能 会覆盖掉别的变量导致程序行为异常,或是访 问到操作系统未映射的区域导致奔溃。 • int &operator[](size_t i) noexcept; • int const 存储的数组,因此只要得到了首地址,下一 个元素的地址只需指针 +1 即可。 • 因为指针的 p[i] 相当于 *(p + i) ,因此可以 把 data() 返回的首地址指针当一个数组来 访问。 • int *data() noexcept; • int const *data() const noexcept; vector 容器: data() 获取首地址指针 • data()0 码力 | 90 页 | 4.93 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 06 TBB 开启的并行编程之旅tbb::simple_partitioner 能够按照给定的粒度 大小( grain )将矩阵进行分块。块内部小区 域按照常规的两层循环访问以便矢量化,块外 部大区域则以类似 Z 字型的曲线遍历,这样 能保证每次访问的数据在地址上比较靠近,并 且都是最近访问过的,从而已经在缓存里可以 直接读写,避免了从主内存读写的超高延迟。 • 下次课会进一步深入探讨访存优化,详细剖析 这个案例,那么下周六 • 而 grow_by(n) 则可以一次扩充 n 个元素。 他同样是返回一个迭代器( iterator ),之 后可以通过迭代器的 ++ 运算符依次访问 连续的 n 个元素, * 运算符访问当前指 向的元素。 可安全地被多线程并发访问 • 除了内存不连续、指针和迭代器不失效的 特点, tbb::concurrent_vector 还是一个多 线程安全的容器,能够被多个线程同时并 不建议通过索引随机访问 • 因为 tbb::concurrent_vector 内存不连续 的特点,通过索引访问,比通过迭代器访 问的效率低一些。 • 因此不推荐像 a[i] 这样通过索引随机访问 其中的元素, *(it + i) 这样需要迭代器跨步 访问的也不推荐。 推荐通过迭代器顺序访问 • 最好的方式是用 begin() 和 end() 的迭代 器区间,按顺序访问。 parallel_for0 码力 | 116 页 | 15.85 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 17 由浅入深学习 map 容器小彭老师说“我在拉答辩。”那么同学认为这个答辩指的是答辩(物理),小彭老师在上厕所。 而不会认为小彭老师在制作三体动画。 • 所以这位同学是人类思维,相当于 Python 的精分 API 。而如果另一个同学是硬核的计算 机思维,相当于 C++ 的一视同仁 API ,他会以为小彭老师真的在吃答辩。 • 这是通常来说,不过万一小彭老师真的这么重口味在吃答辩呢?要怎么传达这个信息? C++ 一视同仁的接口就能处理这种罕见的情况,不过 m) • 由于刚刚说了, map 真正的“元素类型”是 K-V 对,所以这里的 auto 如果不省略应该是 : • for (pairtmp: m) • 如果要单独访问 K 或者 V 怎么办?我们看一下 pair 的定义,里面只有两个成 员: • struct pair { • T1 first; T2 second; • }; }; map 的遍历:用 C++17 range-based loop • 所以 for (auto tmp: m) 这里 tmp 的类型是 pair 。 • 如果要单独访问 K 或者 V 怎么办?我们看一下 pair 的定义,里面只有两个成 员: • struct pair { • T1 first; • T2 second; 0 码力 | 90 页 | 8.76 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 16 现代 CMake 模块化项目管理指南里写一些你常用的函数,宏,变量等。 macro 和 function 的区别 • macro 相当于直接把代码粘贴过去,直接访问调用者的作用域。这里写的相对路径 include 和 src ,是基于调用者所在路径。 • function 则是会创建一个闭包,优先访问定义者的作用域。这里写的相对路径 include 和 src ,则是基于定义者所在路径。 https://cmake.o include 和 add_subdirectory 的区别 • include 相当于直接把代码粘贴过去,直接访问调用者的作用域。这里创建的变量和外面共 享,直接 set(key val) 则调用者也有 ${key} 这个变量了。 • function 中则是基于定义者所在路径,优先访问定义者的作用域。这里需要 set(key val PARENT_SCOPE) 才能修改到外面的变量。 第二章:第三方库. • 例如: 1.2.0 , 0.6.8 , 18.11.0 • major 称为主版本号,出现功能重大变更,以至于和旧 API 不兼容的时候会增加该号。 • minor 称为次版本号,功能有所变更或增加,但依然和旧的 API 兼容时会增加该号。 • patch 称为补丁版号,功能没有改变,只是修复了一些 bug 就重新发布时会增加该号。 • 也有的软件不拘一格(例如我们的 0 码力 | 56 页 | 6.87 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 05 C++11 开始的多线程编程t0 + 3; // 当前时间的三秒后 • usleep(3000000); // 让程序休眠 3000000 微秒,也就是 3 秒 • C 语言原始的 API ,没有类型区分,导致很容易弄错单位,混淆时间点和时间段。 • 比如 t0 * 3 ,乘法对时间点而言根本是个无意义的计算,然而 C 语言把他们看做一样的 long 类型,从而容易让程序员犯错。 不传递任何实际的值。 第 3 章:互斥量 多线程打架案例 • 两个线程试图往同一个数组里推数据。 • 奔溃了!为什么? • vector 不是多线程安全( MT-safe )的容 器。 • 多个线程同时访问同一个 vector 会出现 数据竞争( data-race )现象。 std::mutex :上锁,防止多个线程同时进入某一代码段 • 调用 std::mutex 的 lock() 时,会检测 案例:多线程环境中使用 std::vector • 刚才说了, vector 不是多线程安全的容器 。 • 多个线程同时访问同一个 vector 会出现 数据竞争( data-race )现象。 封装一个线程安全的 vector • 因此,可以用一个类封装一下对 vector 的访问,使其访问都受到一个 mutex 的 保护。 • 然而却出错了:因为 size() 是 const 函 数,而 mutex::lock()0 码力 | 79 页 | 14.11 MB | 1 年前3
现代C++ 教程:高速上手C++11/14/17/20C++ 中的编程范式。 现代 C++ 还为自身的标准库增加了非常多的工具和方法,诸如在语言自身标准的层面上制定了 std::thread,从而支持了并发编程,在不同平台上不再依赖于系统底层的 API,实现了语言层面的跨 平台支持;std::regex 提供了完整的正则表达式支持等等。C++98 已经被实践证明了是一种非常成功 的『范型』,而现代 C++ 的出现,则进一步推动这种范型,让 C++ 基本类似,因此我们就不花费篇幅进 行介绍了。 需要知道的是,和 std::list 的双向链表的实现不同,std::forward_list 使用单向链表进行实现, 提供了 O(1) 复杂度的元素插入,不支持快速随机访问(这也是链表的特点),也是标准库容器中唯一一 个不提供 size() 方法的容器。当不需要双向迭代时,具有比 std::list 更高的空间利用率。 4.2 无序容器 我们已经熟知了传统 C++ true; 除此之外,它也可以用于获取指向原始对象的 std::shared_ptr 指针,其 lock() 方法在原始对象未 被释放时,返回一个指向原始对象的 std::shared_ptr 指针,进而访问原始对象的资源,否则返回 nullptr。 56 总结 第 6 章正则表达式 图 3: 图 5.2 总结 智能指针这种技术并不新奇,在很多语言中都是一种常见的技术,现代 C++ 将这项技术引进,在0 码力 | 83 页 | 2.42 MB | 1 年前3
共 202 条
- 1
- 2
- 3
- 4
- 5
- 6
- 21













