Using the Microsoft Graph API to get Office 365 data in your mobile apps
Using the Microsoft Graph API to get Office 365 data in your mobile apps Alex Ziskind Technical Director @digitalix www.nuvious.com Hi, I’m Alex From + 750 Million 50 Million per month Problems0 码力 | 15 页 | 7.00 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 08 CUDA 开启的 GPU 编程
__host__ 和 __device__ 。 通过 #ifdef 指令针对 CPU 和 GPU 生成不同的代码 • CUDA 编译器具有多段编译的特点。 • 一段代码他会先送到 CPU 上的编译器(通常是系统自带的编译 器比如 gcc 和 msvc )生成 CPU 部分的指令码。然后送到真 正的 GPU 编译器生成 GPU 指令码。最后再链接成同一个文件 ,看起来好像只编译了一次一样,实际上你的代码会被预处理很 这个变量 ,设置要针对哪个架构生成 GPU 指令码。 • 小彭老师的显卡是 RTX2080 ,他的版本号是 75 ,因 此最适合他用的指令码版本是 75 。 • 如果不指定,编译器默认的版本号是 52 ,他是针对 GTX900 系列显卡的。 • 不过英伟达的架构版本都是向前兼容的,即版本号为 75 的 RTX2080 也可以运行版本号为 52 的指令码,虽然 不够优化,但是至少能用。也就是要求:编译期指定的 ,而又不希望导回数据到 CPU 导致强制同步影响性能。 这种模式被称为动态并行( dynamic parallelism ), OpenGL 有一 个 glDispatchComputeIndirect 的 API 和这个很像,但毕竟没有 CUDA 可以直接在核函数里调用核函数并指定参数这么方便…… 不过,这个功能同样需要开启 CUDA_SEPARABLE_COMPILATION 。 第 2 章:内存管理0 码力 | 142 页 | 13.52 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 05 C++11 开始的多线程编程
t0 + 3; // 当前时间的三秒后 • usleep(3000000); // 让程序休眠 3000000 微秒,也就是 3 秒 • C 语言原始的 API ,没有类型区分,导致很容易弄错单位,混淆时间点和时间段。 • 比如 t0 * 3 ,乘法对时间点而言根本是个无意义的计算,然而 C 语言把他们看做一样的 long 类型,从而容易让程序员犯错。 (概念)。比起虚函数和 动态多态的接口抽象, concept 使实现和接口更加解 耦合且没有性能损失。 第 4 章:死锁 同时锁住多个 mutex :死锁难题 • 由于同时执行的两个线程,他们中发生的指令不 一定是同步的,因此有可能出现这种情况: • t1 执行 mtx1.lock() 。 • t2 执行 mtx2.lock() 。 • t1 执行 mtx2.lock() :失败,陷入等待 counter += i 在 CPU 看来会变成三个指令: 1. 读取 counter 变量到 rax 寄存器 2. rax 寄存器的值加上 1 3. 把 rax 写入到 counter 变量 • 即使编译器优化成 add [counter], 1 也没用,因为现代 CPU 为了高效,使用了大量奇技淫巧,比如他会把一 条汇编指令拆分成很多微指令 (micro-ops) ,三个甚至 有点保守估计了。0 码力 | 79 页 | 14.11 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 06 TBB 开启的并行编程之旅
先对 a 预留一定的内存,避免频繁扩容影响性能。 加速比: 5.98 倍 并行筛选 6 使用 tbb::spin_mutex 替代 std::mutex 。 spin_mutex (基 于硬件原子指令)会让 CPU 陷入循环等待,而不像 mutex (操作系统提供调度)会让线程进入休眠状态的等待 。 若上锁的区域较小,可以用轻量级的 spin_mutex 。若上锁 的区域很大,则循环等待只会浪费 ch 替代? 简单粗暴并行 for 加速比: 3.16 倍 很不理想,为什么? 很简单,循环体太大,每跑一遍指令缓存和数据缓存都 会重新失效一遍。且每个核心都在读写不同地方的数据 ,不能很好的利用三级缓存,导致内存成为瓶颈。 拆分为三个 for 加速比: 3.47 倍 解决了指令缓存失效问题,但是三次独立的 for 循环每次 结束都需要同步,一定程度上妨碍了 CPU 发挥性能;而 且每个 step 后依然写回了数组,数据缓存没法充分利用 。 另辟蹊径:流水线并行 加速比: 6.73 倍 反直觉的并行方式,但是加速效果却很理想,为什么? 流水线模式下每个线程都只做自己的那个步骤( filter ),从 而对指令缓存更友好。且一个核心处理完的数据很快会被另一 个核心用上,对三级缓存比较友好,也节省内存。 且 TBB 的流水线,其实比教科书上描述的传统流水线并行更加优化: 他在 t1 线程算完 d1 的 s10 码力 | 116 页 | 15.85 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 性能优化之无分支编程 Branchless Programming
分支预测成败对性能的影响 排序为什么对有分支的版本影响那么大 为什么需要流水线 • 为了高效, CPU 的内部其实是一个流水 线 (pipeline) 。流水线的目的是能把原本 串行的一系列指令并行化。为了理解为什 么需要流水线,我们先反过来,假设没有 流水线,会有什么坏处。 • 例如,右边你今天早上的任务清单。 • 请问你这些任务总共需要多少时间? 任务 时间 占用资源 洗脸 钟嘛!可以,不过这是在你每次只做一件 事的情况下,例如你烧开水时就站在旁边 干瞪眼,什么也不做,其实完全可以在烧 开水的同时洗脸刷牙呀!原始的 CPU 也 是这样, ALU 在运算的时候指令解码单元 就在旁边干瞪眼,要等 ALU 跑完写回寄 存器来指令解码单元才开始继续工作,很 低效。 任务 时间 占用资源 洗脸 5 分钟 眼睛,嘴巴,手 烧开水 10 分钟 煤气灶 刷牙 5 分钟 嘴巴,手 看比站 15 刷牙 烧开水 吃饭 看比站 拉粑粑 5 5 10 20 条件跳转指令 • 让不占用相同资源的任务同时进行,这也是 CPU 流水线的初衷。但理想是美好的,现实 是骨感的,对于程序来说,指令不只是一个 个简单的任务,有时候我们需要做判断,来 决定要执行的具体任务,这就是分支,在汇 编语言中体现为条件跳转指令。 • 例如我们这里给任务清单加一个,如果烧开 水时被烫伤,则直接去医院的特殊任务。0 码力 | 47 页 | 8.45 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 07 深入浅出访存优化
免从外部内存读写的超高延迟。 缓存的分级结构 查看高速缓存大小: lscpu • 可以看到我们 x86 电脑的缓存结构分为三级。 • 一级缓存分为数据缓存和指令缓存,其中数据缓存有 32 KB , 6 个物理核心每个都有一个,总共 192 KB 。而指令缓存的大小刚好和数据缓存一样也是 192 KB 。 • 二级缓存有 256 KB , 6 个物理核心每个都有一个, 总共 1.5 MB 。 缓存行预取技术:吃着一碗饭的同时,先喊妈妈烧下一碗饭 • 其实,当程序顺序访问 a[0], a[1] 时, CPU 会智能地预测到你接下来可 能会读取 a[2] ,于是会提前给缓存发送一个读取指令,让他读取 a[2] 、 a[3] 。缓存在后台默默读取数据的同时, CPU 自己在继续处理 a[0] 的数据。这样等 a[0], a[1] 处理完以后,缓存也刚好读取完 a[2] 了,从而 申请起始地址对齐到页边 界的一段内存,真正做到每个块内部不出现跨页现象。 手动预取: _mm_prefetch • 对于不得不随机访问很小一块的情况,还可以通过 _mm_prefetch 指令手动预取一个缓存行。 • 这里第一个参数是要预取的地址(最好对齐到缓存 行),第二个参数 _MM_HINT_T0 代表预取数据 到一级缓存, _MM_HINT_T1 代表只取到二级缓 存, _MM_HINT_T20 码力 | 147 页 | 18.88 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 04 从汇编角度看编译器优化
%rsi), %eax 相当于: eax = &*(rdi + rsi) 妙用本用于指针的指令,尽管此时 rdi 和 rsi 并不是指针 整数加常数乘整数:都可以被优化成 leal 因为这种线性变换在地址索引 中很常见,所以被 x86 做成 了单独一个指令。这里尽管不 是地址,但同样可以利用 lea 指令简化生成的代码大小。 eax = rdi + rsi * 8 指针访问对象:线性访问地址 省流助手: 如果你看到编译器生成的汇编里,有大量 ss 结尾 的指令则说明矢量化失败;如果看到大多数都是 ps 结尾则说明矢量化成功。 xmm0 xmm1 xmm0 addss %xmm1, %xmm0 addps %xmm1, %xmm0 xmm0 xmm1 xmm0 为什么需要 SIMD ?单个指令处理四个数据 • 这种单个指令处理多个数据的技术称为 SIMD ( single-instruction 在一定条件下,编译器能够把一个处理标量 float 的代码,转换成一个利用 SIMD 指令的 ,处理矢量 float 的代码,从而增强你程序的吞吐能力! • 通常认为利用同时处理 4 个 float 的 SIMD 指令可以加速 4 倍。但是如果你的算法不 适合 SIMD ,则可能加速达不到 4 倍;也有因为 SIMD 让访问内存更有规律,节约了指 令解码和指令缓存的压力等原因,出现加速超过 4 倍的情况。 第 1 章:化简0 码力 | 108 页 | 9.47 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 11 现代 CMake 进阶指南
Makefile • 执行本地的构建系统 make 真正开始构建( 4 进程并 行) • 让本地的构建系统执行安装步骤 • 回到源码目录 现代 CMake 提供了更方便的 -B 和 --build 指令,不同平台,统一命 令! • cmake -B build • cmake --build build -j4 • sudo cmake --build build --target install 则关闭 GCC 的扩展功能,只使用标准的 C++ 。 • 要兼容其他编译器(如 MSVC )的项目,都会设为 OFF 防止不小心用了 GCC 才有的 特性。 • 此外,最好是在 project 指令前设置 CMAKE_CXX_STANDARD 这一系列变量,这样 CMake 可以在 project 函数里对编译器进行一些检测,看看他能不能支持 C++17 的特 性。 https://crascit cmake_minimum_required 不仅是“最小所需版本” 虽然名字叫 minimum_required ,实际上不光是 >= 3.15 就不出错这么简单。 根据你指定的不同的版本号,还会决定接下来一系列 CMake 指令的行为。 此外,你还可以通过 3.15...3.20 来表示最高版本不超过 3.20 。 这会对 cmake_policy 有所影响,稍后再提。 https://runebook.dev/zh-C0 码力 | 166 页 | 6.54 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 10 从稀疏数据结构到量化数据类型
unsigned 类型的 >> n 会不会复制最高位, 只是单纯的位移,这会导致负数的符号位单独被位 移,补码失效,造成结果不对。 unsigned 类型的 >> 会生成 shr 指令, signed 类型的 >> 会生成 sar 指令。 我们需要负方向无限延伸的稀疏数据结果,那就只 要 signed 那个就行。 >> 2 = 没有重合时可以用高效的加法:位运算 | • 如果可以保证 a 和 b )不一定就是完美解决方案,要根据实际情况判断。 真正的解决: tbb::spin_mutex 其实主要的瓶颈在于 std::mutex 会切换到操作系统内核中去调度 ,非常低效。而 tbb::spin_mutex 则是基于硬件原子指令的,完全 用户态的实现。区别: std::mutex 的陷入等待会让操作系统挂起 该线程,以切换到另一个;而 tbb::spin_mutex 的陷入等待是通过 不断地 while (locked);0 码力 | 102 页 | 9.50 MB | 1 年前3C++高性能并行编程与优化 - 课件 - 12 从计算机组成原理看 C 语言指针
字的长度决定了计算机中寄存器的大小,从而决定计算机一次能处理多大的整数。 • 例如 32 位计算机的寄存器都是 32 位,因此只能做 32 位整数的加减乘除,超过 32 位 整数的加减乘除就要用特殊的指令来模拟了。 整数的表示范围受位数限制 • 8 位长的整数能表示的范围是 0 到 2^8-1 ,也就是 0 到 255 。 • 16 位长的整数能表示的范围是 0 到 2^16-1 ,也就是 0 实际把数据写入到你分配的数组。这样就不需要让 func 负责 malloc ,对调用者自由度更 高。 • 例子: clGetPlatformIDs , glGetProgramInfo 等专业 API 函数刻在 DNA 里的调用方法 。 错误典型:返回栈上数组的地址 • 看右边这个例子,这是我在一份看起来很 专业的 CFD 代码里看到的,真的泵不住 了。 • int a[1024] 是一个定长数组,是在栈上分0 码力 | 128 页 | 2.95 MB | 1 年前3
共 32 条
- 1
- 2
- 3
- 4