代码之家  ›  专栏  ›  技术社区  ›  Luna

(编辑)我用Swift和C lang(查找素数)编写了相同的代码,但C lang比Swift快得多

  •  1
  • Luna  · 技术社区  · 2 年前

    (下面有一些编辑)
    嗯,我用Swift和C lang写了完全相同的代码。这是一个查找素数并显示它的代码。
    我希望Swift lang的代码比C lang的程序快得多,但事实并非如此。

    Swift语言比C语言代码慢得多,有什么原因吗?

    当我找到第4000个素数时,C郎只用了一秒钟就完成了计算。 但是,斯威夫特以38.8秒的成绩结束了比赛。 这比我想象的要慢得多。

    这是我写的代码。

    (代码中的日语注释或文本很抱歉。)

    敏捷的

    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)秒")
    
    

    叮当声

    #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
            }
        }
    

    我写了一个代码来比较所有的数字。那是个错误。 但是,我用这个代码修复,也在4.7秒内完成了计算。 它也比C语言慢4倍。
    2 回复  |  直到 2 年前
        1
  •  4
  •   Alexander    2 年前

    根本原因

    正如大多数人所说,“为什么同一个程序在两种不同的语言中表现不同?”,答案几乎总是:“因为它们不是同一个程序。”

    它们在高层意图上可能类似,但它们的实现差异很大,因此可以区分它们的性能。

    有时它们在控制方式上有所不同(例如,在一个程序中使用数组,在另一个程序中使用哈希集),有时在控制方式上有所不同(例如,与编译的C函数调用相比,您使用的是CPython,并且您正在经历解释和动态方法调度的开销)。

    一些示例差异

    在这种情况下,我可以看到一些显著的差异:

    1. prime C代码中的数组使用 unsigned int ,这通常类似于 UInt32 . 您的Swift代码使用 Int ,这通常相当于 Int64 . 它的大小是原来的两倍,这使内存使用量加倍,并降低了CPU缓存的效率。
    2. 首要的 数组,而Swift代码以空开头 Array ,并根据需要反复种植。
    3. 您的C代码不会预初始化 首要的 大堆内存中可能残留的任何垃圾仍有待观察,而Swift代码将在使用前清空所有数组内存。
    4. 检查所有Swift算术运算是否溢出。这将在每个 + , % &+ , &-

    一般来说,您会注意到一种趋势,Swift优化了安全性和开发人员体验,而C优化了接近硬件的性能。Swift优化允许开发人员表达其对业务逻辑的意图,而C优化允许开发人员表达其对运行的最终机器代码的意图。

    Swift中通常有“逃生舱口”,可以让您牺牲安全性或便利性来获得类似C的性能。这听起来很糟糕,但可以说,你可以认为C只是在使用这些逃生舱口。没有 , Dictionary ,自动参考计数, Sequence 算法等,例如Swift调用的内容 UnsafePointer 只是C中的一个“指针”。“不安全”与领土一起出现。

    提高性能

    您可以通过以下方式在达到性能平价方面取得很大进展:

    1. 预分配足够大的阵列 [Array.reserveCapacity(_:) ]( https://developer.apple.com/documentation/swift/array/reservecapacity(_:) Array documentation :

      每个数组都保留特定数量的内存来保存其内容。当您向阵列中添加元素时,该阵列开始超过其保留容量,阵列会分配更大的内存区域,并将其元素复制到新的存储器中。新存储器是旧存储器大小的倍数。这种指数增长策略意味着附加元素的时间恒定,平均了许多附加操作的性能。触发重新分配的追加操作具有性能成本,但随着阵列的增大,它们发生的频率越来越低。

      如果您知道大约需要存储多少个元素,请在附加到阵列之前使用reserveCapacity(_:)方法,以避免中间重新分配。使用capacity和count属性确定阵列在不分配较大存储空间的情况下可以再存储多少个元素。

    2. 使用 UInt32 Int32 而不是 整数

    3. 如有必要,请下拉至 UnsafeMutableBuffer<UInt32> 而不是 Array<UInt32> . 这更接近于C示例中使用的简单指针实现。

    4. 可以使用未选中的算术运算符,如 &+ , &- , &% 绝对地 确信溢出是不可能的。考虑到有数千个与无声溢出相关的bug来了又去,这几乎总是一个糟糕的赌注,但如果你坚持的话,上膛的枪是可以给你的。

    这些不是你通常应该做的事情。如果需要提高关键代码的性能,它们只是存在的可能性。

    例如,Swift公约通常使用 整数 除非你有充分的理由使用其他东西。例如 Array.count 返回一个 ,即使它永远不可能是消极的,也不可能需要超过 UInt32.max .

        2
  •  4
  •   Rob Napier    2 年前

    你忘了打开优化器。在没有优化的情况下,Swift比C慢得多,但在这样的情况下,优化后大致相同:

    ➜  x swift -O prime.swift
    いくつ目まで?(最小2、最大100000まで)
    → 40000
    表示方法を選んでください。(1:全て順番に表示、2:40000番目の一つだけ表示)
    → 2
    40000番目の素数は479909です。
    40000番目の素数まで計算。
    計算経過時間: 5.9秒
    ➜  x clang -O3 prime.c && ./a.out
    いくつ目まで?(最小2、最大100000まで)
    → 40000
    表示方法を選んでください。(1:全て順番に表示、2:40000番目の一つだけ表示)
    → 2
    40000番目の素数は「479909」です。
    40000番目の素数まで計算計算経過時間: 6秒
    

    这是没有做任何工作来改进您的代码( 最重要的可能是像在C中一样预分配缓冲区 这实际上并不重要)。