使用Delphi的TParallel.&For优化哥德巴赫猜想验证为何性能下降?
Delphi TParallel.&For优化哥德巴赫猜想反而变慢的问题分析与解决
问题根源分析
1. 任务粒度过于精细
当前TParallel.&For把每个数(从6到MaxInt-1)都作为独立任务分发,而每个偶数的验证逻辑非常轻量化——尤其是小偶数,循环几次就会命中素数对并Break。线程调度、上下文切换的开销远远超过了单个任务的执行时间,直接导致整体效率下降。
2. 缓存局部性破坏与伪共享
单线程验证时,顺序遍历偶数的方式让IsPrime数组的访问具有极强的连续性,CPU缓存命中率极高。但并行后,多个线程分散访问数组的不同位置,不仅破坏了缓存局部性,还可能引发伪共享(多个线程同时访问同一缓存行的不同数据),导致缓存频繁失效、刷新,大幅增加内存访问开销。
3. 无效的奇数处理
TParallel.&For遍历了所有数,其中一半是奇数,每个任务都要执行ANumber mod 2 <> 0 then Exit的判断,这部分额外逻辑累积起来也是不小的开销。
4. 冗余的素数判断
APrimes是欧拉筛生成的素数表,里面的元素必然是素数,因此IsPrime[Index]的判断完全多余,每次循环多了一次不必要的数组访问。
优化方案与代码调整
1. 增大任务粒度,保持缓存局部性
使用TParallel.&For的范围分区器重载版本,让每个线程处理连续的偶数块,既减少线程切换次数,又能利用CPU缓存的局部性优势。
2. 直接遍历偶数,跳过奇数
从6开始,步长设为2,直接遍历所有偶数,避免对奇数的无效判断。
3. 移除冗余的素数判断
删掉IsPrime[Index]的判断,因为APrimes中的元素都是预先生成的素数。
优化后的代码
procedure GoldBachCheckParallel(APrimes: TArray<Int64>; IsPrime: TArray<Boolean>); var MaxEven: Int64; begin // 确保上限是偶数 MaxEven := IfThen(MaxInt mod 2 = 0, MaxInt, MaxInt - 1); // 使用范围分区器,每个线程处理连续的10000个偶数块,提升缓存命中率 TParallel.&For(6, MaxEven, 2, TPartitioner.CreateRange(6, MaxEven, 2, 10000), procedure(ANumber: Int64) var Prime: Int64; begin for Prime in APrimes do begin if IsPrime[ANumber - Prime] then Break; if Prime * Prime > ANumber then Break; end; end); end;
额外优化建议
- 如果
APrimes数组体积很大,可以考虑按CPU缓存行大小对齐,进一步减少伪共享的影响; - 预先计算
APrimes中小于等于√MaxInt的素数子集,避免每次循环计算Prime * Prime; - 对于大偶数,验证到
ANumber/2即可停止,因为素数对是对称的,能减少一半的循环次数。
内容的提问来源于stack exchange,提问作者Kirito
相关产品推荐
相关产品推荐

