面向亿行 C/C++ 代码的静态分析系统设计及实践-肖枭“这是以前的业务逻辑,不用修” “这别人写的代码,不关我事” 大量报告引起不适 刚写的代码立即自动扫描,程序员强迫使用 只体现新增代码问题,责任边界清晰 评审流程多人督促 渐进式开启更多检查器 增量分析减少不适 • 软件工程师一天被邀请进行多次 代码评审,这些都不是他们自身 的KPI • 一旦工期紧996,人工评审容易 变成走形式 • 任务挂着还容易造成工程师焦虑 降低工程师劳动强度0 码力 | 39 页 | 6.88 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 08 CUDA 开启的 GPU 编程?稍后会说明。 • 运行以后,就会在 GPU 上执行 printf 了。 • 这里的 kernel 函数在 GPU 上执行,称为核 函数,用 __global__ 修饰的就是核函数。 没有反应?同步一下! • 然而如果直接编译运行刚刚那段代码,是不会打印出 Hello, world! 的。 • 这是因为 GPU 和 CPU 之间的通信,为了高效,是异 步的。也就是 CPU 调用 kernel<<<1 里的板块数和线程数可以动态指定,无需 先传回到 CPU 再进行调用,这是 CUDA 特有的能力。 常用于这种情况:需要从 GPU 端动态计算出 blockDim 和 gridDim ,而又不希望导回数据到 CPU 导致强制同步影响性能。 这种模式被称为动态并行( dynamic parallelism ), OpenGL 有一 个 glDispatchComputeIndirect 的 API 和这个很像,但毕竟没有 CUDA cudaMemcpyDeviceToHost 。 • 同理,还有 cudaMemcpyHostToDevice 和 cudaMemcpyDeviceToDevice 。 cudaMemcpy 会自动同步! • 注意: cudaMemcpy 会自动进行同步操作 ,即和 cudaDeviceSynchronize() 等价! 因此前面的 cudaDeviceSynchronize() 实 际上可以删掉了。 统一内存地址技术(0 码力 | 142 页 | 13.52 MB | 1 年前3
现代C++ 教程:高速上手C++11/14/17/20需要结果的时候,调用一个线程等待函数来获得执行的结果。 而 C++11 提供的 std::future 简化了这个流程,可以用来获取异步任务的结果。自然地,我们很 容易能够想象到把它作为一种简单的线程同步手段,即屏障(barrier)。 为了看一个例子,我们这里额外使用 std::packaged_task,它可以用来封装任何可以调用的目标, 从而用于实现异步的调用。举例来说: #include result.get() << std::endl; return 0; } 在封装好要调用的目标后,可以使用 get_future() 来获得一个 std::future 对象,以便之后实施 线程同步。 7.4 条件变量 条件变量 std::condition_variable 是为了解决死锁而生,当互斥操作不够用而引入的。比如,线程 可能需要等待某个条件为真才能继续执行,而一个忙等待循环中可能会导致所有其他线程都无法进入临 提供线程间自动的状态转换,即『锁住』这个状态 2. 保障在互斥锁操作期间,所操作变量的内存与临界区外进行隔离 这是一组非常强的同步条件,换句话说当最终编译为 CPU 指令时会表现为非常多的指令(我们之 后再来看如何实现一个简单的互斥锁)。这对于一个仅需原子级操作(没有中间态)的变量,似乎太苛刻 了。 关于同步条件的研究有着非常久远的历史,我们在这里不进行赘述。读者应该明白,现代 CPU 体系 结构提供了 CPU0 码力 | 83 页 | 2.42 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 05 C++11 开始的多线程编程void , 这时 future 对象 的类型为 std::future。 • 同理有 std::promise ,他的 set_value() 不接受参数,仅仅作为同步用, 不传递任何实际的值。 第 3 章:互斥量 多线程打架案例 • 两个线程试图往同一个数组里推数据。 • 奔溃了!为什么? • vector 不是多线程安全( MT-safe )的容 动态多态的接口抽象, concept 使实现和接口更加解 耦合且没有性能损失。 第 4 章:死锁 同时锁住多个 mutex :死锁难题 • 由于同时执行的两个线程,他们中发生的指令不 一定是同步的,因此有可能出现这种情况: • t1 执行 mtx1.lock() 。 • t2 执行 mtx2.lock() 。 • t1 执行 mtx2.lock() :失败,陷入等待 • t2 执行 • 因此可以用更轻量级的 atomic ,对他的 += 等 操作,会被编译器转换成专门的指令。 • CPU 识别到该指令时,会锁住内存总线,放弃 乱序执行等优化策略(将该指令视为一个同步点 ,强制同步掉之前所有的内存操作),从而向你 保证该操作是原子 (atomic) 的(取其不可分割 之意),不会加法加到一半另一个线程插一脚进 来。 • 对于程序员,只需把 int 改成 atomic 0 码力 | 79 页 | 14.11 MB | 1 年前3
《深入浅出MFC》2/e產生㆒個 Worker Thread / 759 產生㆒個 UI Thread / 761 執行緒的結束 / 763 執行緒與同步控制 / 763 MFC 多緒程式實例 / 766 目 錄 25 * 第 15 章 定製㆒個 AppWizard / 771 到底 Wizard 是什麼? 的内存容量还很小,多任务是 个新奇观念,更别提什么多执行线程了。因此以当时产品为基础所演化的C runtime 函 数库在多线程(multithreaded)的表现上有严重问题,无法被多线程程序使用。 利用各种同步机制(synchronous mechanism)如critical section、mutex、semaphore、 event,可以重新开发一套支持多执行线程的runtime 函数库。问题是,加上这样的能 的折衷方案是提供两种版本的C runtime 函数库。一种版本给单线程程序 使用,一种版本给多线程程序使用。多线程版本的重大改变是,第一,变量如errno 者 现在变成每个执行线程各拥有一个。第二,多线程版中的数据结构以同步机制加以保护。 Visual C++ 一共有六个C runtime 函数库产品供你选择: Single-Threaded(static) libc.lib 898,826 Multithreaded(static)0 码力 | 1009 页 | 11.08 MB | 1 年前3
C++高性能并行编程与优化 - 课件 - 06 TBB 开启的并行编程之旅运用多线程的方式和动机,一般分为两种。 • 并发:单核处理器,操作系统通过时间片调 度算法,轮换着执行着不同的线程,看起来 就好像是同时运行一样,其实每一时刻只有 一个线程在运行。目的:异步地处理多个不 同的任务,避免同步造成的阻塞。 • 并行:多核处理器,每个处理器执行一个线 程,真正的同时运行。目的:将一个任务分 派到多个核上,从而更快完成任务。 举个例子 • 并发:某互联网公司购置了一台单核处理 器的服务器,他正同时处理 :每个线程一个任务队列,做完本职工作后可以认领其他线程的任务 工作窃取法( work-stealing ) 原始的单一任务队列 解决 4 :随机分配法(通过哈希函数或线性函数) • 然而队列的实现较复杂且需要同步机制,还是有一 定的 overhead ,因此另一种神奇的解法是: • 我们仍是分配 4 个线程,但还是把图像切分为 16 份。然后规定每一份按照 xy 轴坐标位置编号,比 如 (1,3) 等。 会重新失效一遍。且每个核心都在读写不同地方的数据 ,不能很好的利用三级缓存,导致内存成为瓶颈。 拆分为三个 for 加速比: 3.47 倍 解决了指令缓存失效问题,但是三次独立的 for 循环每次 结束都需要同步,一定程度上妨碍了 CPU 发挥性能;而 且每个 step 后依然写回了数组,数据缓存没法充分利用 。 另辟蹊径:流水线并行 加速比: 6.73 倍 反直觉的并行方式,但是加速效果却很理想,为什么?0 码力 | 116 页 | 15.85 MB | 1 年前3
Hello 算法 1.0.0b1 C++版「线性探测」使用固定步长的线性查找来解决哈希冲突。 插入元素:如果出现哈希冲突,则从冲突位置向后线性遍历(步长一般取 1 ),直到找到一个空位,则将元素 插入到该空位中。 查找元素:若出现哈希冲突,则使用相同步长执行线性查找,会遇到两种情况: 1. 找到对应元素,返回 value 即可; 2. 若遇到空位,则说明查找键值对不在哈希表中; Figure 6‑5. 线性探测 线性探测存在以下缺陷: ‧0 码力 | 187 页 | 14.71 MB | 1 年前3
Hello 算法 1.0.0b2 C++版「线性探测」使用固定步长的线性查找来解决哈希冲突。 插入元素:如果出现哈希冲突,则从冲突位置向后线性遍历(步长一般取 1 ),直到找到一个空位,则将元素 插入到该空位中。 查找元素:若出现哈希冲突,则使用相同步长执行线性查找,会遇到两种情况: 1. 找到对应元素,返回 value 即可; 2. 若遇到空位,则说明查找键值对不在哈希表中; Figure 6‑5. 线性探测 线性探测存在以下缺陷: 60 码力 | 197 页 | 15.72 MB | 1 年前3
Hello 算法 1.0.0b4 C++版操作方法为: ‧ 插入元素:通过哈希函数计算数组索引,若发现桶内已有元素,则从冲突位置向后线性遍历(步长通常 为 1 ),直至找到空位,将元素插入其中。 ‧ 查找元素:若发现哈希冲突,则使用相同步长向后线性遍历,直到找到对应元素,返回 value 即可;如 果遇到空位,说明目标键值对不在哈希表中,返回 None 。 Figure 6‑6. 线性探测 然而,线性探测存在以下缺陷: ‧0 码力 | 343 页 | 27.39 MB | 1 年前3
Hello 算法 1.1.0 C++ 版通哈希表有所不同。 ‧ 插入元素:通过哈希函数计算桶索引,若发现桶内已有元素,则从冲突位置向后线性遍历(步长通常为 1 ),直至找到空桶,将元素插入其中。 ‧ 查找元素:若发现哈希冲突,则使用相同步长向后进行线性遍历,直到找到对应元素,返回 value 即 可;如果遇到空桶,说明目标元素不在哈希表中,返回 None 。 图 6‑6 展示了开放寻址(线性探测)哈希表的键值对分布。根据此哈希函数,最后两位相同的0 码力 | 379 页 | 18.47 MB | 1 年前3
共 14 条
- 1
- 2













