 面向亿行 C/C++ 代码的静态分析系统设计及实践-肖枭150 200 250 300 不编译 编译 报告数  编译流程融合静态分析  分布式编译与分析  大量使用缓存  提升静态分析的理论速度 如何做到10分钟反馈分析结果 系统地改进分析时间 编译流程 分析流程 依赖关系分析 分布式 编译 分布式 分析 分布式链接 跨模块分析 报告整合 缓存 缓存 缓存 缓存 硬核玩家:从理论上改进静态分析能力 关键步骤高亮和行为解释  配套完善的文档  代码交叉索引 降低感知误报率 回忆下代码评审 时最不能忍的事 是啥?对,就是 不能像在IDE里 面一样查看符号 定义使用。 代码交叉索引 方法4:Bug生命周期跟踪  精确查找类似Bug,利用 标记数据排除潜在误报  通过修复率等参数对分析 器进行综合评价 降低感知误报率 方法5:防止误标和作弊  标记量,间隔时间,标记内容  用基线数据训练模型0 码力 | 39 页 | 6.88 MB | 1 年前3 面向亿行 C/C++ 代码的静态分析系统设计及实践-肖枭150 200 250 300 不编译 编译 报告数  编译流程融合静态分析  分布式编译与分析  大量使用缓存  提升静态分析的理论速度 如何做到10分钟反馈分析结果 系统地改进分析时间 编译流程 分析流程 依赖关系分析 分布式 编译 分布式 分析 分布式链接 跨模块分析 报告整合 缓存 缓存 缓存 缓存 硬核玩家:从理论上改进静态分析能力 关键步骤高亮和行为解释  配套完善的文档  代码交叉索引 降低感知误报率 回忆下代码评审 时最不能忍的事 是啥?对,就是 不能像在IDE里 面一样查看符号 定义使用。 代码交叉索引 方法4:Bug生命周期跟踪  精确查找类似Bug,利用 标记数据排除潜在误报  通过修复率等参数对分析 器进行综合评价 降低感知误报率 方法5:防止误标和作弊  标记量,间隔时间,标记内容  用基线数据训练模型0 码力 | 39 页 | 6.88 MB | 1 年前3
 Hello 算法 1.0.0b5 C++版在 C、C++、Go 和 Rust 等支持指针的语言中,上述的“引用”应被替换为“指针”。 如以下代码所示,链表节点 ListNode 除了包含值,还需额外保存一个引用(指针)。因此在相同数据量下,链 表比数组占用更多的内存空间。 /* 链表节点结构体 */ struct ListNode { int val; // 节点值 ListNode *next; // 指向下一节点的指针 ListNode(int 节点的引用两项数据。我们将首 个节点称为头节点,将最后一个节点称为尾节点,尾节点指向空 None 。 ‧ 环形链表:如果我们令单向链表的尾节点指向头节点(即首尾相接),则得到一个环形链表。在环形链 表中,任意节点都可以视作头节点。 ‧ 双向链表:与单向链表相比,双向链表记录了两个方向的引用。双向链表的节点定义同时包含指向后继 节点(下一个节点)和前驱节点(上一个节点)的引用(指针)。相较于单向链表,双向链表更具灵活 ‧ 栈与队列:当插入和删除操作都在链表的一端进行时,它表现出先进后出的的特性,对应栈;当插入操 作在链表的一端进行,删除操作在链表的另一端进行,它表现出先进先出的特性,对应队列。 ‧ 哈希表:链地址法是解决哈希冲突的主流方案之一,在该方案中,所有冲突的元素都会被放到一个链表 中。 ‧ 图:邻接表是表示图的一种常用方式,在其中,图的每个顶点都与一个链表相关联,链表中的每个元素 都代表与该顶点相连的其他顶点。0 码力 | 377 页 | 30.69 MB | 1 年前3 Hello 算法 1.0.0b5 C++版在 C、C++、Go 和 Rust 等支持指针的语言中,上述的“引用”应被替换为“指针”。 如以下代码所示,链表节点 ListNode 除了包含值,还需额外保存一个引用(指针)。因此在相同数据量下,链 表比数组占用更多的内存空间。 /* 链表节点结构体 */ struct ListNode { int val; // 节点值 ListNode *next; // 指向下一节点的指针 ListNode(int 节点的引用两项数据。我们将首 个节点称为头节点,将最后一个节点称为尾节点,尾节点指向空 None 。 ‧ 环形链表:如果我们令单向链表的尾节点指向头节点(即首尾相接),则得到一个环形链表。在环形链 表中,任意节点都可以视作头节点。 ‧ 双向链表:与单向链表相比,双向链表记录了两个方向的引用。双向链表的节点定义同时包含指向后继 节点(下一个节点)和前驱节点(上一个节点)的引用(指针)。相较于单向链表,双向链表更具灵活 ‧ 栈与队列:当插入和删除操作都在链表的一端进行时,它表现出先进后出的的特性,对应栈;当插入操 作在链表的一端进行,删除操作在链表的另一端进行,它表现出先进先出的特性,对应队列。 ‧ 哈希表:链地址法是解决哈希冲突的主流方案之一,在该方案中,所有冲突的元素都会被放到一个链表 中。 ‧ 图:邻接表是表示图的一种常用方式,在其中,图的每个顶点都与一个链表相关联,链表中的每个元素 都代表与该顶点相连的其他顶点。0 码力 | 377 页 | 30.69 MB | 1 年前3
 Hello 算法 1.0.0b1 C++版相比于「线性探测」,「多次哈希」方法更不容易产生聚集,代价是多个哈希函数增加了额外计算量。 � 工业界方案 Java 采用「链式地址」。在 JDK 1.8 之后,HashMap 内数组长度大于 64 时,长度大于 8 的链 表会被转化为「红黑树」,以提升查找性能。 Python 采用「开放寻址」。字典 dict 使用伪随机数进行探测。 6.3. 小结 ‧ 向哈希表中输入一个键 key ,查询到值 value 的时间复杂度为 hello‑algo.com 96 Figure 7‑7. 平衡二叉树 7.1.4. 二叉树的退化 当二叉树的每层的结点都被填满时,达到「完美二叉树」;而当所有结点都偏向一边时,二叉树退化为「链 表」。 ‧ 完美二叉树是一个二叉树的“最佳状态”,可以完全发挥出二叉树“分治”的优势; ‧ 链表则是另一个极端,各项操作都变为线性操作,时间复杂度退化至 ?(?) ; Figure 7‑8. 二叉树的术语较多,包括根结点、叶结点、层、度、边、高度、深度等。 ‧ 二叉树的初始化、结点插入、结点删除操作与链表的操作方法类似。 ‧ 常见的二叉树类型包括完美二叉树、完全二叉树、完满二叉树、平衡二叉树。完美二叉树是理想状态,链 表则是退化后的最差状态。 ‧ 二叉树可以使用数组表示,具体做法是将结点值和空位按照层序遍历的顺序排列,并基于父结点和子结 点之间的索引映射公式实现指针。 二叉树遍历 ‧ 二叉树层序遍历是一0 码力 | 187 页 | 14.71 MB | 1 年前3 Hello 算法 1.0.0b1 C++版相比于「线性探测」,「多次哈希」方法更不容易产生聚集,代价是多个哈希函数增加了额外计算量。 � 工业界方案 Java 采用「链式地址」。在 JDK 1.8 之后,HashMap 内数组长度大于 64 时,长度大于 8 的链 表会被转化为「红黑树」,以提升查找性能。 Python 采用「开放寻址」。字典 dict 使用伪随机数进行探测。 6.3. 小结 ‧ 向哈希表中输入一个键 key ,查询到值 value 的时间复杂度为 hello‑algo.com 96 Figure 7‑7. 平衡二叉树 7.1.4. 二叉树的退化 当二叉树的每层的结点都被填满时,达到「完美二叉树」;而当所有结点都偏向一边时,二叉树退化为「链 表」。 ‧ 完美二叉树是一个二叉树的“最佳状态”,可以完全发挥出二叉树“分治”的优势; ‧ 链表则是另一个极端,各项操作都变为线性操作,时间复杂度退化至 ?(?) ; Figure 7‑8. 二叉树的术语较多,包括根结点、叶结点、层、度、边、高度、深度等。 ‧ 二叉树的初始化、结点插入、结点删除操作与链表的操作方法类似。 ‧ 常见的二叉树类型包括完美二叉树、完全二叉树、完满二叉树、平衡二叉树。完美二叉树是理想状态,链 表则是退化后的最差状态。 ‧ 二叉树可以使用数组表示,具体做法是将结点值和空位按照层序遍历的顺序排列,并基于父结点和子结 点之间的索引映射公式实现指针。 二叉树遍历 ‧ 二叉树层序遍历是一0 码力 | 187 页 | 14.71 MB | 1 年前3
 Hello 算法 1.0.0b2 C++版相比于「线性探测」,「多次哈希」方法更不容易产生聚集,代价是多个哈希函数增加了额外计算量。 � 工业界方案 Java 采用「链式地址」。在 JDK 1.8 之后,HashMap 内数组长度大于 64 时,长度大于 8 的链 表会被转化为「红黑树」,以提升查找性能。 Python 采用「开放寻址」。字典 dict 使用伪随机数进行探测。 Golang 采用「链式地址」。Go 规定每个桶最多存储 8 个键值对,超出容量则连接一个溢出桶; hello‑algo.com 96 Figure 7‑7. 平衡二叉树 7.1.4. 二叉树的退化 当二叉树的每层的结点都被填满时,达到「完美二叉树」;而当所有结点都偏向一边时,二叉树退化为「链 表」。 ‧ 完美二叉树是一个二叉树的“最佳状态”,可以完全发挥出二叉树“分治”的优势; ‧ 链表则是另一个极端,各项操作都变为线性操作,时间复杂度退化至 ?(?) ; Figure 7‑8. 二叉树的术语较多,包括根结点、叶结点、层、度、边、高度、深度等。 ‧ 二叉树的初始化、结点插入、结点删除操作与链表的操作方法类似。 ‧ 常见的二叉树类型包括完美二叉树、完全二叉树、完满二叉树、平衡二叉树。完美二叉树是理想状态,链 表则是退化后的最差状态。 ‧ 二叉树可以使用数组表示,具体做法是将结点值和空位按照层序遍历的顺序排列,并基于父结点和子结 点之间的索引映射公式实现指针。 二叉树遍历 ‧ 二叉树层序遍历是一0 码力 | 197 页 | 15.72 MB | 1 年前3 Hello 算法 1.0.0b2 C++版相比于「线性探测」,「多次哈希」方法更不容易产生聚集,代价是多个哈希函数增加了额外计算量。 � 工业界方案 Java 采用「链式地址」。在 JDK 1.8 之后,HashMap 内数组长度大于 64 时,长度大于 8 的链 表会被转化为「红黑树」,以提升查找性能。 Python 采用「开放寻址」。字典 dict 使用伪随机数进行探测。 Golang 采用「链式地址」。Go 规定每个桶最多存储 8 个键值对,超出容量则连接一个溢出桶; hello‑algo.com 96 Figure 7‑7. 平衡二叉树 7.1.4. 二叉树的退化 当二叉树的每层的结点都被填满时,达到「完美二叉树」;而当所有结点都偏向一边时,二叉树退化为「链 表」。 ‧ 完美二叉树是一个二叉树的“最佳状态”,可以完全发挥出二叉树“分治”的优势; ‧ 链表则是另一个极端,各项操作都变为线性操作,时间复杂度退化至 ?(?) ; Figure 7‑8. 二叉树的术语较多,包括根结点、叶结点、层、度、边、高度、深度等。 ‧ 二叉树的初始化、结点插入、结点删除操作与链表的操作方法类似。 ‧ 常见的二叉树类型包括完美二叉树、完全二叉树、完满二叉树、平衡二叉树。完美二叉树是理想状态,链 表则是退化后的最差状态。 ‧ 二叉树可以使用数组表示,具体做法是将结点值和空位按照层序遍历的顺序排列,并基于父结点和子结 点之间的索引映射公式实现指针。 二叉树遍历 ‧ 二叉树层序遍历是一0 码力 | 197 页 | 15.72 MB | 1 年前3
 现代C++ 教程:高速上手C++11/14/17/20std::boolalpha << a.is_lock_free() << std::endl; return 0; } 一致性模型 并行执行的多个线程,从某种宏观层面上讨论,可以粗略的视为一种分布式系统。在分布式系统中, 任何通信乃至本地操作都需要消耗一定时间,甚至出现不可靠的通信。 如果我们强行将一个变量 v 在多个线程之间的操作设为原子操作,即任何一个线程在操作完 v 后, 其他线程均能同步感知到0 码力 | 83 页 | 2.42 MB | 1 年前3 现代C++ 教程:高速上手C++11/14/17/20std::boolalpha << a.is_lock_free() << std::endl; return 0; } 一致性模型 并行执行的多个线程,从某种宏观层面上讨论,可以粗略的视为一种分布式系统。在分布式系统中, 任何通信乃至本地操作都需要消耗一定时间,甚至出现不可靠的通信。 如果我们强行将一个变量 v 在多个线程之间的操作设为原子操作,即任何一个线程在操作完 v 后, 其他线程均能同步感知到0 码力 | 83 页 | 2.42 MB | 1 年前3
 Hello 算法 1.0.0b4 C++版‧ 栈与队列:当插入和删除操作都在链表的一端进行时,它表现出先进后出的的特性,对应栈;当插入操 作在链表的一端进行,删除操作在链表的另一端进行,它表现出先进先出的特性,对应队列。 ‧ 散列表:链地址法是解决哈希冲突的主流方案之一,在该方案中,所有冲突的元素都会被放到一个链表 中。 ‧ 图:邻接表是表示图的一种常用方式,在其中,图的每个顶点都与一个链表相关联,链表中的每个元素 都代表与该顶点相连的其他顶点。 会执行一次特殊的等量扩容操作,以确保性能。 6.3. 哈希算法 在上两节中,我们了解了哈希表的工作原理和哈希冲突的处理方法。然而无论是开放寻址还是链地址法,它 们只能保证哈希表可以在发生冲突时正常工作,但无法减少哈希冲突的发生。 如果哈希冲突过于频繁,哈希表的性能则会急剧劣化。对于链地址哈希表,理想情况下键值对平均分布在各 个桶中,达到最佳查询效率;最差情况下所有键值对都被存储到同一个桶中,时间复杂度退化至 com 123 Figure 7‑11. 前序遍历的递归过程 7.3. 二叉树数组表示 在链表表示下,二叉树的存储单元为节点 TreeNode ,节点之间通过指针相连接。在上节中,我们学习了在链 表表示下的二叉树的各项基本操作。 那么,能否用「数组」来表示二叉树呢?答案是肯定的。 7.3.1. 表示完美二叉树 先分析一个简单案例。给定一个完美二叉树,我们将所有节点按照层序遍历的顺序存储在一个数组中,则每0 码力 | 343 页 | 27.39 MB | 1 年前3 Hello 算法 1.0.0b4 C++版‧ 栈与队列:当插入和删除操作都在链表的一端进行时,它表现出先进后出的的特性,对应栈;当插入操 作在链表的一端进行,删除操作在链表的另一端进行,它表现出先进先出的特性,对应队列。 ‧ 散列表:链地址法是解决哈希冲突的主流方案之一,在该方案中,所有冲突的元素都会被放到一个链表 中。 ‧ 图:邻接表是表示图的一种常用方式,在其中,图的每个顶点都与一个链表相关联,链表中的每个元素 都代表与该顶点相连的其他顶点。 会执行一次特殊的等量扩容操作,以确保性能。 6.3. 哈希算法 在上两节中,我们了解了哈希表的工作原理和哈希冲突的处理方法。然而无论是开放寻址还是链地址法,它 们只能保证哈希表可以在发生冲突时正常工作,但无法减少哈希冲突的发生。 如果哈希冲突过于频繁,哈希表的性能则会急剧劣化。对于链地址哈希表,理想情况下键值对平均分布在各 个桶中,达到最佳查询效率;最差情况下所有键值对都被存储到同一个桶中,时间复杂度退化至 com 123 Figure 7‑11. 前序遍历的递归过程 7.3. 二叉树数组表示 在链表表示下,二叉树的存储单元为节点 TreeNode ,节点之间通过指针相连接。在上节中,我们学习了在链 表表示下的二叉树的各项基本操作。 那么,能否用「数组」来表示二叉树呢?答案是肯定的。 7.3.1. 表示完美二叉树 先分析一个简单案例。给定一个完美二叉树,我们将所有节点按照层序遍历的顺序存储在一个数组中,则每0 码力 | 343 页 | 27.39 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 04 从汇编角度看编译器优化懂!总之非常高效就对了! 第 5 章:循环 循环中的矢量化:还在伺候指针别名 我们可怜的编译器啊!他还在担心 a 和 b 指向的数组是否有重合。 考虑 func(a, a + 1) 的情况,那样会产生数据依赖链,没法 SIMD 化 。 为了优化而不失正确性,他索性生成两份代码: 一份是 SIMD 的,一份是传统标量的 他在运行时检测 a, b 指针的差是否超过 1024 来判断是否有重叠现 象。 1. 如果没有重叠,则跳转到0 码力 | 108 页 | 9.47 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 04 从汇编角度看编译器优化懂!总之非常高效就对了! 第 5 章:循环 循环中的矢量化:还在伺候指针别名 我们可怜的编译器啊!他还在担心 a 和 b 指向的数组是否有重合。 考虑 func(a, a + 1) 的情况,那样会产生数据依赖链,没法 SIMD 化 。 为了优化而不失正确性,他索性生成两份代码: 一份是 SIMD 的,一份是传统标量的 他在运行时检测 a, b 指针的差是否超过 1024 来判断是否有重叠现 象。 1. 如果没有重叠,则跳转到0 码力 | 108 页 | 9.47 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 11 现代 CMake 进阶指南系统,需要设置全部 6 个属性,是不是非常繁琐? 不懂就问,为什么说 Linux 系统是永远滴神? • 而 Linux 系统支持 RPATH , CMake 会让生成出来可执行文件的 RPATH 字段指向他链 接了的 .so 文件所在目录,运行时会优先从 RPATH 里找链接库,所以即使不在同目录也 能找到。 • 所以还有第三种解决方案:微软,我卸卸你全家(指卸载)。然后安装 Arch Linux 系统0 码力 | 166 页 | 6.54 MB | 1 年前3 C++高性能并行编程与优化 -  课件 - 11 现代 CMake 进阶指南系统,需要设置全部 6 个属性,是不是非常繁琐? 不懂就问,为什么说 Linux 系统是永远滴神? • 而 Linux 系统支持 RPATH , CMake 会让生成出来可执行文件的 RPATH 字段指向他链 接了的 .so 文件所在目录,运行时会优先从 RPATH 里找链接库,所以即使不在同目录也 能找到。 • 所以还有第三种解决方案:微软,我卸卸你全家(指卸载)。然后安装 Arch Linux 系统0 码力 | 166 页 | 6.54 MB | 1 年前3
 Hello 算法 1.1.0 C++ 版在 C、C++、Go 和 Rust 等支持指针的语言中,上述“引用”应被替换为“指针”。 如以下代码所示,链表节点 ListNode 除了包含值,还需额外保存一个引用(指针)。因此在相同数据量下,链 表比数组占用更多的内存空间。 /* 链表节点结构体 */ struct ListNode { int val; // 节点值 ListNode *next; // 指向下一节点的指针 ListNode(int (1) 。 ‧ 划分阶段:可以使用“迭代”替代“递归”来实现链表划分工作,从而省去递归使用的栈帧空间。 ‧ 合并阶段:在链表中,节点增删操作仅需改变引用(指针)即可实现,因此合并阶段(将两个短有序链 表合并为一个长有序链表)无须创建额外链表。 具体实现细节比较复杂,有兴趣的读者可以查阅相关资料进行学习。 11.7 堆排序 Tip 阅读本节前,请确保已学完“堆“章节。 堆排序(heap0 码力 | 379 页 | 18.47 MB | 1 年前3 Hello 算法 1.1.0 C++ 版在 C、C++、Go 和 Rust 等支持指针的语言中,上述“引用”应被替换为“指针”。 如以下代码所示,链表节点 ListNode 除了包含值,还需额外保存一个引用(指针)。因此在相同数据量下,链 表比数组占用更多的内存空间。 /* 链表节点结构体 */ struct ListNode { int val; // 节点值 ListNode *next; // 指向下一节点的指针 ListNode(int (1) 。 ‧ 划分阶段:可以使用“迭代”替代“递归”来实现链表划分工作,从而省去递归使用的栈帧空间。 ‧ 合并阶段:在链表中,节点增删操作仅需改变引用(指针)即可实现,因此合并阶段(将两个短有序链 表合并为一个长有序链表)无须创建额外链表。 具体实现细节比较复杂,有兴趣的读者可以查阅相关资料进行学习。 11.7 堆排序 Tip 阅读本节前,请确保已学完“堆“章节。 堆排序(heap0 码力 | 379 页 | 18.47 MB | 1 年前3
 Hello 算法 1.0.0 C++版在 C、C++、Go 和 Rust 等支持指针的语言中,上述“引用”应被替换为“指针”。 如以下代码所示,链表节点 ListNode 除了包含值,还需额外保存一个引用(指针)。因此在相同数据量下,链 表比数组占用更多的内存空间。 /* 链表节点结构体 */ struct ListNode { int val; // 节点值 ListNode *next; // 指向下一节点的指针 ListNode(int (1) 。 ‧ 划分阶段:可以使用“迭代”替代“递归”来实现链表划分工作,从而省去递归使用的栈帧空间。 ‧ 合并阶段:在链表中,节点增删操作仅需改变引用(指针)即可实现,因此合并阶段(将两个短有序链 表合并为一个长有序链表)无须创建额外链表。 具体实现细节比较复杂,有兴趣的读者可以查阅相关资料进行学习。 11.7 堆排序 � 阅读本节前,请确保已学完“堆“章节。 「堆排序 heap s0 码力 | 378 页 | 17.59 MB | 1 年前3 Hello 算法 1.0.0 C++版在 C、C++、Go 和 Rust 等支持指针的语言中,上述“引用”应被替换为“指针”。 如以下代码所示,链表节点 ListNode 除了包含值,还需额外保存一个引用(指针)。因此在相同数据量下,链 表比数组占用更多的内存空间。 /* 链表节点结构体 */ struct ListNode { int val; // 节点值 ListNode *next; // 指向下一节点的指针 ListNode(int (1) 。 ‧ 划分阶段:可以使用“迭代”替代“递归”来实现链表划分工作,从而省去递归使用的栈帧空间。 ‧ 合并阶段:在链表中,节点增删操作仅需改变引用(指针)即可实现,因此合并阶段(将两个短有序链 表合并为一个长有序链表)无须创建额外链表。 具体实现细节比较复杂,有兴趣的读者可以查阅相关资料进行学习。 11.7 堆排序 � 阅读本节前,请确保已学完“堆“章节。 「堆排序 heap s0 码力 | 378 页 | 17.59 MB | 1 年前3
共 12 条
- 1
- 2













