You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

同逻辑素数查找Swift代码远慢于C的原因及优化方案

同逻辑素数计算Swift性能远低于C的原因与优化方案

测试背景

分别使用Swift、C编写逻辑完全一致的素数查找程序,预期Swift性能接近C,但实际测试结果差距明显:计算到第4000个素数时,C语言统计耗时1秒,初始版本Swift耗时38.8秒;修正遍历逻辑(从遍历2到候选数的所有整数,改为仅遍历已找到的素数集合做整除校验)后,Swift仍需4.7秒,耗时为同逻辑C实现的4倍。

核心性能差距原因

  • 内存模型差异:C代码中prime是提前声明在栈上的10万元素固定长度数组,元素访问是直接计算内存偏移的裸操作,无任何额外开销;Swift的Array是存储在堆上的动态数组,默认带写时复制机制,未提前分配容量时每次append都可能触发内存重分配、全量元素拷贝,且默认所有下标访问都带越界检查,天然比C的裸数组访问多一层安全校验成本。
  • 编译优化默认差异:日常调试用的Swift Debug模式默认关闭所有性能优化,保留完整运行时类型检查、调试符号,循环逻辑不会被编译为最精简的CPU指令;而C语言哪怕不手动开高等级优化,编译器默认就会折叠冗余变量、简化循环判断,开O2优化后还会自动做指令重排、循环展开,性能差距会被进一步放大。
  • 基础操作开销差:Swift的Range类型遍历在未优化状态下会生成区间迭代的额外调度代码,不像C的三段式for循环直接映射为CPU的比较、跳转原生指令;Swift整数取模运算默认带溢出检查逻辑,未优化时不会直接编译为CPU原生取模指令,比C的整数运算多一层安全判断成本。
  • 计时逻辑本身的误差:Swift用的clock()是微秒级精度,会把Swift运行时初始化、动态库加载的时间都算入耗时;C用的time(NULL)是秒级精度,统计粒度极粗,测出来的1秒本身就有±0.5秒的误差,实际同逻辑C代码计算第4000个素数的耗时远低于1秒,两者的实际性能差没有测试结果显示的4倍那么夸张。

Swift代码可行优化方案

  • 调整编译配置:把Swift运行方案切到Release模式,编译器优化等级选择Optimize for Speed [-O],同时开启Remove Safety Checks选项移除运行时数组越界、整数溢出检查,仅这一步就能把Swift代码性能提升5-10倍,基本追平C的性能水平。
  • 数组提前预分配容量:在读取用户输入的countMax之后,给prime数组调用prime.reserveCapacity(countMax),提前申请好足够的堆内存,彻底避免后续append触发的动态扩容、元素拷贝开销。
  • 清理冗余逻辑:删掉完全无意义的let completedPrimeNumber = prime.map { $0 }拷贝操作,输出时直接遍历原数组即可;简化flag判断逻辑,不用单独写分支重置flag,每次外层循环开始时直接把flag置0就行,减少不必要的分支判断。
  • 算法层优化:第一,遍历素数做整除判断时,只需要遍历到值不大于primeCandidate.squareRoot()的素数即可——如果n存在大于sqrt(n)的因数,对应的配对因数必然小于sqrt(n),已经被遍历过,不需要检查后面的素数;第二,除了2之外所有素数都是奇数,初始化时直接存2、3,候选数从5开始每次步进2,跳过所有偶数,直接减少一半的遍历量。
  • 极致性能优化:如果追求和C完全对齐的访问开销,可以用withUnsafeBufferPointer获取prime数组的连续裸内存指针,用while循环做下标递增遍历,完全绕过Swift数组的默认访问调度。

附实现代码

初始Swift版本(存在遍历逻辑问题)

import CoreFoundation
/*
var calendar = Calendar.current
calender.locale = .init(identifier: "ja.JP")
*/

var primeCandidate: Int
var prime: [Int] = []

var countMax: Int

print("いくつ目まで?(最小2、最大100000まで)\n→ ", terminator: "")

countMax = Int(readLine()!)!

var flagPrint: Int

print("表示方法を選んでください。(1:全て順番に表示、2:\(countMax)番目の一つだけ表示)\n→ ", terminator: "")
flagPrint = Int(readLine()!)!

prime.append(2)
prime.append(3)

var currentMaxCount: Int = 2
var numberCount: Int

primeCandidate = 4

var flag: Int = 0
var ix: Int

let startedTime = clock()
//let startedTime = time()
//.addingTimeInterval(0.0)

while currentMaxCount < countMax {
    for ix in 2..<primeCandidate {
        if primeCandidate % ix == 0 {
            flag = 1
            break
        }
    }
    
    if flag == 0 {
        prime.append(primeCandidate)
        currentMaxCount += 1
    } else if flag == 1 {
        flag = 0
    }
    
    primeCandidate += 1
}

let endedTime = clock()
//let endedTime = Time()
//.timeIntervalSince(startedTime)

if flagPrint == 1 {
    print("計算された素数の一覧:", terminator: "")
    
    let completedPrimeNumber = prime.map {
        $0
    }
    
    
    print(completedPrimeNumber)
    //print("\(prime.map)")
    
    print("\n\n終わり。")
    
} else if flagPrint == 2 {
    print("\(currentMaxCount)番目の素数は\(prime[currentMaxCount - 1])です。")
}

print("\(countMax)番目の素数まで計算。")
print("計算経過時間: \(round(Double((endedTime - startedTime) / 100000)) / 10)秒")

对照C语言实现

#include <stdio.h>
#include <time.h> //経過時間計算のため

int main(void)
{
    int primeCandidate;
    unsigned int prime[100000];
    
    int countMax;
    
    printf("いくつ目まで?(最小2、最大100000まで)\n→ ");
    scanf("%d", &countMax);
    
    int flagPrint;
    
    printf("表示方法を選んでください。(1:全て順番に表示、2:%d番目の一つだけ表示)\n→ ", countMax);
    scanf("%d", &flagPrint);
    
    prime[0] = 2;
    prime[1] = 3;
    
    int currentMaxCount = 2;
    int numberCount;
    
    primeCandidate = 4;
    
    int flag = 0;
    
    int ix;
    
    int startedTime = time(NULL);
    for(;currentMaxCount < countMax;primeCandidate++){
        /*
        for(numberCount = 0;numberCount < currentMaxCount - 1;numberCount++){
            if(primeCandidate % prime[numberCount] == 0){
                flag = 1;
                break;
            }
        }
            */
            
        for(ix = 2;ix < primeCandidate;++ix){
            if(primeCandidate % ix == 0){
                flag = 1;
                break;
            }
        }
            
        if(flag == 0){
            prime[currentMaxCount] = primeCandidate;
            currentMaxCount++;
        } else if(flag == 1){
            flag = 0;
        }
    }
    int endedTime = time(NULL);
    
    if(flagPrint == 1){
        printf("計算された素数の一覧:");
        for(int i = 0;i < currentMaxCount - 1;i++){
            printf("%d, ", prime[i]);
        }
        printf("%d.\n\n終わり", prime[currentMaxCount - 1]);
    } else if(flagPrint == 2){
        printf("%d番目の素数は「%d」です。\n",currentMaxCount ,prime[currentMaxCount - 1]);
    }
    
    printf("%d番目の素数まで計算", countMax);
    printf("計算経過時間: %d秒\n", endedTime - startedTime);
    
    return 0;
}

修正后遍历逻辑核心代码段

for ix in 0..<currentMaxCount - 1 {
    if primeCandidate % prime[ix] == 0 {
        flag = 1
        break
    }
}

内容的提问来源于stack exchange,提问作者Luna

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 03:27:32