PlanB:基于线性化B+树与SIMD的无分支IPv6路由查找算法

IPv6路由查找最长前缀匹配线性化B+树
于 2026-05-30 03:17:37 修改
·本内容遵循CC 4.0 BY-SA版权协议

1. 项目概述与核心挑战

在高速网络数据平面的世界里,每一个纳秒都至关重要。当数据包以每秒数百万个的速度涌入路由器或交换机时,决定其去向的IP路由查找(Longest Prefix Match, LPM)操作,就成了整个转发流水线上最关键的瓶颈之一。尤其是在IPv6时代,128位的地址空间和日益庞大的全球路由表(FIB),让传统的查找算法显得力不从心。你或许听过或用过基于Trie(前缀树)的各种优化方案,比如压缩Trie、多比特Trie,它们确实在特定场景下有效。但深入其内核,你会发现一个共通的性能杀手:不可预测的控制流

传统的树形查找(无论是Trie还是多路范围树)本质上是一连串的“if-else”决策。从根节点开始,根据目标地址的若干比特位,决定下一步跳转到哪个子节点。这种模式对CPU的分支预测器极不友好。在高速、随机的流量冲击下,分支预测失败频频发生。一次预测失败意味着CPU需要清空已进行到一半的投机执行流水线,从正确路径重新开始,这动辄会浪费几十个CPU周期。当每秒要进行数亿次查找时,累积的惩罚足以让转发性能腰斩。此外,指针追逐(pointer chasing)带来的缓存不友好性,也让内存访问延迟成为另一个性能黑洞。

PlanB的诞生,正是为了从根本上解决这两个问题。它的核心思路非常巧妙:与其在复杂的树形结构中艰难地“匹配最长前缀”,不如将问题转化为在一个有序区间集合中“查找目标点所属的区间”。听起来像是换了个说法?但这背后是数据结构与算法设计的根本性变革。PlanB通过将IPv6前缀转换为一系列不重叠的“基本区间”,并将这些区间的边界点组织成一个线性化、扁平化的B+树,使得整个查找过程变成了一次确定性的、无分支的、可向量化的数组遍历。这就像把一座需要不断做出岔路选择的迷宫,变成了一条有清晰路标的单向快速路。

2. 核心设计:从LPM到一维区间搜索

要理解PlanB,首先得忘掉“前缀树”的思维定式。我们从一个更几何的视角来看待IP地址查找问题。

2.1 基本区间转换:化繁为简

一个IPv6前缀,例如 2001:db8::/32,定义了一个连续的地址范围。PlanB的第一步,就是将路由表中所有这样的前缀范围,转换为一组互不重叠的“基本区间”。具体方法是,提取每个前缀的起始地址和结束地址(即起始地址加上该前缀长度所覆盖的地址范围),将这些地址点排序去重。相邻的两个点就构成了一个基本区间。

这样做的妙处在于:每个基本区间都唯一对应一个下一跳信息。无论目标IP地址落在这个区间内的哪个具体位置,其下一跳都是确定的。于是,复杂的“最长前缀匹配”问题,被优雅地简化为一个更简单的“在一组有序点中,查找最后一个小于等于目标地址的点”的问题。这本质上是一个一维搜索问题。

2.2 线性化B+树:极致的内存友好性

有了有序的点数组,我们当然可以用二分查找。但朴素的二分查找在内存访问模式上并不理想(跳跃式访问),且依然存在条件分支。PlanB采用了线性化B+树作为其核心数据结构。

什么是线性化B+树?它不是我们在磁盘数据库中常见的那个充满指针的树形结构。相反,PlanB将一棵完整的B+树的所有节点(包括内部节点的键和叶子节点的键值对),按照层级顺序,平铺到一个或两个大数组中。树的逻辑结构通过数组下标计算来隐式表达,而非显式指针。

例如,对于一个分支因子为 k 的B+树,我们可以预先计算好每一层节点在数组中的起始偏移量。查找时,从根节点(存储在数组开头)开始,通过简单的算术计算(子节点索引 = 当前节点起始偏移 + 子节点编号)就能找到下一层的节点数据,完全不需要通过指针解引用。这种布局带来了几个决定性优势:

  1. 连续内存访问:一次加载一个缓存行,可以包含多个键值,极大提高了缓存利用率。
  2. 消除指针追逐:没有了随机内存访问,降低了缓存未命中率。
  3. 计算取代寻址:通过下标计算替代指针解引用,延迟更低且更可预测。

2.3 无分支遍历与SIMD加速:榨干CPU性能

这是PlanB性能腾飞的关键。在传统的树遍历中,每个节点内需要将目标键与多个键比较,以决定走向哪个子节点。这通常是一个循环或一串if-else语句。

PlanB彻底摒弃了这种模式。它利用现代CPU的SIMD(单指令多数据流)指令集,例如Intel的AVX-512或ARM的NEON,实现无分支(Branch-Free)的节点内搜索

具体是如何工作的? 在一个存储了k个键的树节点(对应数组中的一段连续内存)中,PlanB使用一条SIMD比较指令,将目标IPv6地址(或地址的关键部分)与这k个键同时进行比较。这条指令会生成一个位掩码(bitmask),其中每一位代表一次比较的结果(例如,1表示目标大于等于该键,0表示小于)。

接下来,通过一条人口计数(popcnt) 指令,统计这个位掩码中“1”的个数。这个计数值,直接就是目标地址应该进入的子节点的编号!因为B+树节点的键是有序的,统计“大于等于”的键的数量,正好给出了正确的分支索引。

这个过程没有任何“if”或“jump”指令。控制流的依赖性被转化为数据流的依赖性,现代CPU的乱序执行引擎可以极其高效地处理这种模式,完全避免了分支预测失败的惩罚。

实操心得:指令选择 在x86平台上,_mm512_cmp_epu64_mask 可用于AVX-512的64位无符号整数比较,配合 _mm_popcnt_u32 进行位计数。如果硬件不支持AVX-512,可以使用更窄的SSE或AVX2指令,通过多次操作完成。关键是要确保数据内存对齐,以发挥最大SIMD性能。

2.4 批处理与循环展开:隐藏延迟,提升吞吐

单次查找的优化还不够。网络数据包通常是成批到达的(例如DPDK的burst机制)。PlanB设计了批处理查找

  1. 指令级并行:对一个批次(比如16个)的IP地址,分别进行独立的SIMD比较和位计数操作。由于这些操作之间没有数据依赖,CPU可以同时执行多个查找的早期阶段,更好地利用流水线。
  2. 循环展开:PlanB的线性化B+树深度很浅(对于百万级的路由表,6-7层足矣)。PlanB在编译时就将整个树的遍历循环完全展开。这意味着,一次完整的查找就是一段长长的、直来直去的指令序列,没有循环计数器,没有条件跳转回循环开头。这消除了所有循环控制开销,并且给了编译器极大的优化空间去重排指令、隐藏内存读取延迟。

设计权衡:批处理和循环展开会增大代码体积,但对L1指令缓存的影响在可控范围内,因为核心查找逻辑本身非常紧凑。带来的性能提升——尤其是消除了分支预测和循环开销——是决定性的。

3. 动态更新机制:重建与原子交换

一个不能动态更新的路由查找引擎是没有实用价值的。然而,在高度优化的只读数据结构上做更新,通常很棘手。PlanB采用了一种经典而有效的策略:批处理重建与原子指针交换

3.1 更新流程

  1. 批量收集:更新(增删改前缀)不是立即执行,而是被收集到一个缓冲区中。这可以由一个独立的控制平面线程或核心来完成。
  2. 后台重建:当累积了足够多的更新,或经过一个时间窗口后,系统在后台基于完整的最新前缀列表,重新运行一遍PlanB的构建算法(算法2)。这包括重新生成基本区间、构建全新的线性化B+树数组。这个过程与前台查找线程完全并行。
  3. 原子切换:新数据结构构建完成后,执行一次原子的指针/引用交换,将前台的查找逻辑指向新的数据数组。正在执行中的查找请求继续使用旧数据结构,完成后退出。当所有旧数据结构的引用都被释放后,其内存可以被安全回收。

3.2 优势与考量

  • 无锁查找:前台查找路径完全不需要任何锁或同步原语,保证了极高的并发性能。
  • 一致性视图:每次查找看到的都是一个完整的、一致的数据结构快照,不存在读到中间状态的风险。
  • 平摊开销:批量重建平摊了单次更新的成本,对于路由更新频繁的场景也能高效处理。

注意事项:内存与延迟权衡 这种“写时复制”的模式需要短暂地存在两份数据结构,内存开销会翻倍。但由于PlanB本身的内存效率极高(远低于其他方案),这个开销通常是可接受的。主要的延迟来自于重建过程本身。对于百万条前缀的FIB,我们的实测重建时间在亚秒级(~850ms),这对于路由收敛时间(通常是秒级)来说是足够的。对于需要极速更新的场景,可以调整批量大小,在更新延迟和吞吐之间取得平衡。

4. 系统实现与集成:以DPDK为例

理论再优美,也需要坚实的工程实现。PlanB原型系统基于DPDK(数据平面开发套件) 实现,这是一个用于高性能用户态网络包处理的标杆框架。

4.1 三阶段流水线:RX-LPM-TX

为了最大化性能并减少干扰,PlanB没有采用简单的RX-TX两阶段模型,而是引入了专用的查找阶段,形成 RX-LPM-TX三阶段流水线

  1. RX线程:专用于从NIC队列轮询接收数据包,并打包成批(burst),放入无锁环形队列(Ring Queue)。
  2. LPM线程:专用于从队列中取出数据包批,提取目标IP地址,调用PlanB引擎进行批量查找,获得下一跳信息,并将结果标注回数据包元数据。
  3. TX线程:专用于根据元数据,将处理完的数据包批发送到正确的网络端口。

这种职责分离的好处是:

  • 可独立扩展:可以根据需要,为每个阶段分配不同数量的CPU核心。例如,在查找密集型场景下,可以分配更多核心给LPM线程。
  • 缓存友好:LPM线程可以独占一个或多个核心,其工作集(主要是PlanB的查找数组)可以很好地驻留在该核心的私有缓存(L2)或共享缓存(L3)中,避免被RX/TX线程的数据冲刷掉。

4.2 缓存拓扑感知的线程绑定

在现代多核CPU(如AMD Zen系列)中,L3缓存通常在几个核心组成的集群(CCD/CCX)内共享。PlanB的实现可以利用这一点进行缓存分区

  • 将所有的LPM线程绑定到同一个CCX内的核心上。这样,PlanB的查找数据结构主要在这个CCX的共享L3缓存中活动。
  • 将RX和TX线程绑定到其他CCX的核心上。 这样,即使RX/TX线程在疯狂地存取数据包缓冲区,也不会轻易地驱逐LPM线程赖以生存的查找表缓存,从而保证了查找性能的稳定性和可预测性。

4.3 灵活的部署模式

三阶段流水线虽然性能最优,但也增加了数据包穿越线程间的队列延迟。PlanB提供了灵活性:

  • 解耦模式(RX-LPM-TX):适用于查找开销大、追求极致吞吐的场景(如大型IPv6 FIB)。
  • 耦合模式(RX-(LPM+TX)):将LPM和TX逻辑合并到一个线程中,减少了一次线程间队列操作。适用于查找开销相对较小、或对延迟更敏感的场景。

5. 性能评估与对比分析

我们在一台搭载24核Intel Xeon服务器和一台12核AMD Ryzen移动处理器上,使用真实世界(RIPE RIS数据集)和合成的IPv6路由表(最多100万条前缀),对PlanB进行了全面评估,并与当前最先进的软件方案进行了对比:PopTrie, CP-Trie, Neurotrie, HBS。

5.1 查找速度:数量级的提升

  • 单核吞吐(MLPS):在Intel服务器上,PlanB达到了 191-197 Million Lookups Per Second (MLPS)。这比PopTrie和CP-Trie快 2.1-5.8倍,比Neurotrie快 4.3-9.6倍,比HBS快 1.4-5.6倍。在AMD移动处理器上,由于其强大的单核性能,PlanB甚至达到了 374-393 MLPS,优势更加明显。
  • 多核扩展(BLPS):PlanB展示了近乎完美的线性扩展能力。在24核Intel服务器上,总吞吐达到 4.6 Billion Lookups Per Second (BLPS)。在12核AMD上达到 3.4 BLPS。相比之下,其他方案由于内存访问瓶颈和同步开销,扩展效率远低于PlanB。

性能根源分析

  • PopTrie/CP-Trie/Neurotrie:这些基于Trie的方案,其性能随着前缀深度增加而下降。每次查找需要多次内存访问(指针追逐),并且节点内部的位操作或决策逻辑引入了分支,容易导致预测失败。
  • HBS:虽然采用了哈希和二分搜索,但其性能受哈希函数质量和前缀长度分布影响较大,且更新逻辑复杂。
  • PlanB:固定的、浅层的树深度(6-7层),完全连续的内存访问模式,以及彻底的无分支、向量化操作,使得其单次查找延迟极低且可预测,从而为高吞吐和线性扩展奠定了基础。

5.2 内存效率:更小的足迹,更多的缓存命中

内存占用直接关系到缓存效率。PlanB的数据结构极其紧凑。对于百万前缀的FIB,其内存占用比PopTrie减少 60.8-79.6%,比Neurotrie减少 82.8-92.5%

这意味着什么?对于一颗拥有36MB共享L3缓存的CPU,PlanB的整个查找结构可以轻松地完全驻留在缓存中。而其他方案的数据结构可能已经溢出到主存(DRAM)中。一次DRAM访问的延迟大约是200-300纳秒,而L3缓存访问可能只有10-20纳秒。这数十倍的延迟差异,在每秒数十亿次查找的背景下,被放大成了巨大的性能鸿沟。PlanB通过极简的数据布局,赢得了“缓存友好”这场关键战役。

5.3 更新开销:批量重建的优势

尽管采用完全重建的策略,PlanB的更新开销仍然优于或可比于其他方案。在真实路由表更新轨迹测试中,PlanB的平均更新开销比PopTrie低 32.5-44.6%,比Neurotrie低 49.3-60%。对于百万前缀FIB,一次完整重建仅需 850毫秒

这证明了批量重建策略的有效性。虽然重建整个表听起来很重,但由于PlanB的构建算法高效,且数据结构紧凑,其重建时间是可接受的。更重要的是,这种策略将更新开销从关键的前台查找路径中完全剥离。

5.4 消融实验:每个优化点的贡献

为了量化每个技术点的贡献,我们进行了一组消融实验:

  1. 基线:使用标准C++ std::lower_bound 在排序后的基本区间数组上进行二分查找。性能仅 5.8-10.7 MLPS。瓶颈在于缓存不友好和分支预测。
  2. +线性化B+树与无分支标量逻辑:替换为线性化B+树遍历,并使用条件移动指令消除分支。性能提升至基线的 3.8-4.6倍。这验证了数据结构变革的基础性作用。
  3. +编译器自动向量化:开启编译器自动向量化优化。带来约 1.15-1.3倍 的额外提升。这表明编译器能做一些优化,但还不够。
  4. +手动AVX-512向量化:使用SIMD intrinsics手动实现节点内并行比较。性能在上一阶段基础上再提升 2-2.7倍。这凸显了手动针对微架构优化的重要性。
  5. +批处理:引入批处理查找,隐藏SIMD指令延迟。实现最终性能,再带来 3-4.5倍 的提升。

这个实验清晰地表明,PlanB的高性能是线性化B+树、无分支逻辑、SIMD向量化、批处理等多个技术协同作用的结果,缺一不可。

6. 深入探讨:设计权衡、适用性与未来

6.1 为何选择线性化B+树而非其他结构?

我们考虑过跳表、ART自适应基数树等。线性化B+树的优势在于:

  • 极致的空间局部性:所有数据连续存储,预取效果好。
  • 计算规整:子节点索引通过乘加计算即可得到,比指针解引用更快。
  • 易于向量化:节点内键的连续存储是SIMD比较的理想前提。
  • 深度可控:通过调整分支因子,可以在树深度(查找步数)和节点大小(SIMD比较宽度)之间取得最佳平衡。对于IPv6,我们通常选择分支因子为8或16,使得树深度仅为6-7层,且节点大小恰好匹配SIMD寄存器的宽度(如8个64位键)。

6.2 对硬件平台的普适性

PlanB的设计不依赖于特定硬件。

  • 无AVX-512的CPU:可以使用更窄的SSE/AVX2指令,通过多次操作完成宽位比较,或回退到高效的无分支标量比较。
  • FPGA/ASIC硬件实现:线性化B+树可以完美地映射到片上SRAM中。查找过程就是一系列对齐的内存读取和比较操作,无需复杂的控制逻辑,非常适合硬件流水线。
  • 异构计算:甚至可以设想一种混合架构,由CPU控制平面负责重建更新线性化B+树,然后将其下载到智能网卡(SmartNIC)或FPGA的片上内存中,由硬件数据平面实现超低延迟的查找。

6.3 应对不可预测流量与攻击

传统算法在遭受随机地址流量攻击时,性能会急剧下降,因为分支预测和缓存命中率崩溃。PlanB对此具有天生的韧性:

  • 恒定查找深度:无论输入地址如何,查找都经过固定的树层数。
  • 无分支:没有预测失败惩罚。
  • 高缓存命中率:紧凑的数据结构常驻缓存。 这意味着PlanB即使在最恶劣的流量模式下,也能保持接近峰值的稳定吞吐,这为网络设备提供了可靠的性能保障。

6.4 与相关工作的对比

  • 多路范围树:这是PlanB的思想先驱,也将前缀转为区间搜索。但传统MRT仍然是普通的指针型B树,存在指针追逐和缓存不友好问题。PlanB的线性化布局和无分支遍历是关键的演进。
  • 混合CAM/RAM架构:如TCAM+SRAM方案,虽然硬件查找快,但TCAM功耗高、容量小、成本昂贵,难以应对超大规模IPv6 FIB。PlanB是纯软件方案,可在通用CPU上实现媲美硬件的性能,且成本、灵活性和可编程性优势明显。

7. 实现细节与避坑指南

如果你打算在自己的项目中尝试实现或集成PlanB,以下是一些从实战中总结的经验:

7.1 内存对齐是生命线

SIMD指令通常要求数据在内存中按特定字节数(如64字节)对齐。确保你的线性化B+树数组是按照CPU最友好的边界(alignas(64))进行分配和 aligned_alloc 的。未对齐的访问会导致性能大幅下降,甚至引发硬件异常。

CPP
// C++ 示例:分配对齐的内存
# include <memory>
constexpr size_t kAlignment = 64;
uint64_t* keys_array = static_cast<uint64_t*>(aligned_alloc(kAlignment, total_size));
// ... 使用 keys_array
free(keys_array);

7.2 谨慎处理“哨兵”值

在构建基本区间数组时,需要在首尾添加“哨兵”值(例如,全0和全1的地址),以确保查找算法对于任何输入地址都能找到有效的区间。在构建线性化B+树时,非满的节点也需要用特定的“哨兵”键值填充。这些哨兵值必须被精心选择,确保它们不会干扰正常的比较逻辑(例如,使用可能的最大值或最小值)。

7.3 批量大小的选择

批处理的大小(batch size)需要微调。太小无法充分隐藏指令延迟,太大会增加单批处理延迟,并可能影响缓存行为。一个实用的起点是16或32,这通常与DPDK的burst大小以及SIMD寄存器的容量(如AVX-512可同时处理8个64位比较)相匹配。需要通过性能剖析工具(如perf)来观察指令周期和缓存命中率,找到最适合你硬件和工作负载的甜蜜点。

7.4 更新线程的优先级

负责后台重建的更新线程,其CPU优先级应该设置为低于前台转发线程。在Linux上,可以使用sched_setattr设置SCHED_IDLE策略或较低的nice值。这可以确保即使在执行大规模路由更新的重构建时,也不会对高优先级的转发平面线程造成可感知的性能干扰。

7.5 性能剖析工具是你的朋友

  • perf:使用 perf stat 查看整体CPI(每指令周期数)、分支预测失败率、缓存命中率。使用 perf record/report 定位热点函数。
  • Intel VTune / AMD uProf:更图形化地分析微架构层面的问题,如前端/后端端口压力、内存带宽等。
  • Cachegrind:模拟缓存行为,帮助你理解数据结构的内存访问模式。

PlanB的设计哲学是清晰的:通过改变游戏规则来赢得性能。它不再在传统的树形查找算法上修修补补,而是通过数学转换和体系结构感知的优化,将问题重塑为现代CPU最擅长处理的形式。其开源实现为社区提供了一个高性能、可扩展的IPv6查找基础组件,无论是用于研究、构建高性能路由器还是下一代网络功能虚拟化平台,都是一个强有力的工具。在网络流量持续增长、IPv6普及深化的今天,像PlanB这样从根本上重新思考数据平面算法的努力,显得愈发重要。

C3 线性化算法与 MRO
本文深入探讨了Python中的方法解析顺序(MRO),包括经典类和新式类的不同MRO计算方式,以及C3线性化算法如何解决多继承场景下的方法搜索顺序问题。
意念回复
1543
python C3 线性化算法与 MRO
本文介绍了Python中的方法解析顺序(MRO),它定义了多继承时解释器查找函数解析的顺序。随着Python版本发展,MRO算法有变化。文中回顾了新旧类定义方式,阐述了旧类基于深度优先遍历的MRO算法,新类的MRO算法及C3线性化算法的计算过程。
hllyzms
594
28、揭秘Undercover分支策略推动非凸MINLP问题线性化
本文介绍Undercover分支策略,一种针对非凸MINLP问题的新型分支方法。该策略通过最小覆盖确定关键变量集,缩小分支候选范围,推动子问题线性化。实验表明其能显著减少搜索节点和计算时间,尤其适用于具备明显非线性结构或高求解难度的问题,且可现有分支规则高效融合。
61
35、揭秘Undercover分支策略推动非凸MINLP问题走向线性化
博客深入探讨Undercover分支策略,推动非凸MINLP问题子问题向线性化发展。回顾常见分支规则,介绍Undercover分支策略的最小覆盖定义、计算及算法,通过实验表明该策略能减少分支定界节点数和计算时间,还分析其优势、适用性,探讨其他规则协同及未来研究方向。
101
多继承与线性化继承
本文探讨C++多继承中的菱形问题构造顺序难题,提出通过编译期拓扑排序将继承关系线性化的方法。利用模板元编程实现自动排序继承链展开,支持虚拟与非虚拟继承混合场景,并兼容CRTP等设计模式,有效规避传统多继承带来的复杂性。
Je1lyfish
1144
深入解析ISAM2基于贝叶斯的增量优化SLAM应用
本文深入剖析ISAM2算法的核心原理工程实践,重点阐述其基于因子图建模、通过变量消元构造贝叶斯实现高效增量更新的机制。详解带约束COLAMD重排序、流体重线性化及部分状态更新三大关键技术,并结合GTSAM框架说明参数调优回环处理方法,突出其在实时SLAM系统中毫秒级响应、局部计算稀疏性保持的能力。
pipecat
439
运筹系列73MINLP算法综述
博客介绍了求解凸MINLP问题的相关算法。常见松弛策略有整型变量松弛和非线性约束线性化。求解算法思想包括迭代法和分支定界法。具体介绍了BB算法、广义Benders分解算法、外逼近算法等,还提及了基于线性/非线性 - 分支定界算法、扩展割平面算法和混合算法
IE06
6374
数据结构学习笔记(3-5):树
本文深入探讨这一数据结构的核心概念,包括的定义、性质、遍历方法,以及二叉树、平衡二叉树、堆、哈夫曼的详细解析。从静态查找与动态查找的区别,到树的存储结构,再到二叉搜索树的构建调整,全面覆盖的理论应用。
呆呆象呆呆
1771
运筹学基础(二)求解整数规划的分支定界法(branch and bound)
本文详细介绍了分支定界法用于解决整数规划问题的步骤,包括将问题线性化、求解子问题、更新上下界,通过实例展示了如何通过分支策略逐步缩小搜索范围并找到最优整数解。,
WhyNot?
3274
深度学习驱动的sRGB图像线性化:基于CIE XYZ空间的高精度逆处理下游应用
本文提出一种基于深度分解学习的sRGB图像线性化方法,将非线性sRGB精确逆映射至线性CIE XYZ空间。通过分离全局(伽马校正)局部(锐化/降噪)处理建模,设计双分支网络架构,并结合像素重建、颜色一致性感知损失进行训练。该线性化结果显著提升图像去噪、去模糊、离焦估计等下游任务性能,并支撑RAW重建、去雾、低光增强等实际应用,为计算摄影提供高保真物理一致的数据基础。
孙瑞宇
606
Git 中 Merge Rebase 的区别选择合适的方式整合分支
本文详细解析Git中MergeRebase的区别Merge保留完整历史,适合团队协作;Rebase重写历史,使提交线性化。通过原理、示例和对比,指导开发者在私有分支使用rebase,在公共分支使用merge,确保代码管理的安全整洁。
苍煜
1014
【数据结构】树与二叉树
本文围绕数据结构展开,详细介绍了的定义术语,重点阐述二叉树的定义、性质、存储结构、遍历线索化。还说明了、森林和二叉树的关系及相互转换,最后讲解哈夫曼的概念、构造及哈夫曼编码的概念实现。
just learn it
1416
数据结构与算法(三)树与二叉树
本文深入探讨了树和二叉树的概念,包括的基本性质、二叉树的分类和存储,以及二叉树的遍历算法。特别地,详细讲解了红黑树这一自平衡二叉查找树的原理,包括其性质、插入删除操作及其平衡过程。
奔跑的大伟哥
328
36、优化算法:Undercover分支与二次外逼近法
本文介绍Undercover分支与二次外逼近法两种优化算法。Undercover分支用于混合整数非线性规划,有性能和组合优势,但受影响实例比例小。二次外逼近法解决凸整数规划问题,通过二次逼近减少迭代次数,还提出快速算法解决替代问题。文章对比算法并给出实际应用选择及改进方向。
106
课堂笔记:树、森林二叉树的转换、哈夫曼
本文详细介绍了、森林二叉树之间的转换方法,包括转换为二叉树、森林转换为二叉树以及二叉树转换为树或森林的过程。此外,还深入探讨了哈夫曼的概念、特点及其构建算法,以及哈夫曼编码的应用。最后,文章讲解了线索二叉树的概念、存储结构和遍历算法。
pink_pink.
517
【python高级编程教程】笔记(python教程、python进阶)第三节(5)多重继承时的变量和方法解析顺序(mro()函数)(C3线性化算法)(多继承搜索顺序、方法搜索顺序)
本文详细解释了Python中的MRO(MethodResolutionOrder,方法解析顺序)算法,用于处理多继承中的变量和方法查找顺序。MRO结合了深度优先和广度优先策略,确保了合理的查找顺序,避免了菱形继承问题。
Dontla
1522
C语言——数据结构之树与二叉树(下)(线索二叉树、树与二叉树的转换、哈夫曼
本文详细介绍了线索二叉树的概念及其遍历算法,探讨了树与二叉树之间的转换方法,并深入解析了哈夫曼的构建原理应用场景。
柠檬茶@
806
广播机制ops-math 的维度扩展对齐
本文详解ops-math库中广播机制的高性能实现,聚焦虚拟步长、线性化遍历、SIMD向量化及内存对齐等核心技术。通过零拷贝维度对齐步长预计算消除分支,结合增量坐标更新AVX2向量化提升吞吐;优化输入缓存行利用输出32字节对齐增强缓存友好性,并支持多操作数融合原地广播。实测较NumPy提速3.77倍。
风指引着方向
549
BLC与线性化技术在计算机视觉中的应用
本文介绍了计算机视觉中的BLC(亮度补偿)和线性化技术,这两种技术在图像处理中至关重要。BLC用于校正图像亮度不均匀,而线性化则将非线性数据转换为线性,改善图像质量并提升后续算法性能。文中通过Python和OpenCV库提供了实现这两个技术的示例代码。
IdfdFsharp
444
29、优化算法新进展Undercover分支与二次外逼近法
本文介绍两种优化算法新进展Undercover分支用于混合整数非线性规划,可显著减少计算时间节点数;二次外逼近法通过二次低估函数加速凸整数规划求解,迭代次数更少且性能更具竞争力。两者各有优势局限,未来发展方向包括扩展适用范围优化代理问题求解。
69