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

Lun*_*una 3 c performance swift calculation

(下面有一些编辑)
\n好吧,我用 Swift 和 C lang 编写了完全相同的代码。这是一个寻找素数并显示它的代码。
\n我期望 Swift lang 的代码比 C lang 的程序快得多,但事实并非如此。
\n
\nSwift 语言比 C 语言代码慢得多有什么原因吗?

\n

当我找到第4000个素数时,C lang只用了一秒就完成了计算。\n但是,Swift用了38.8秒完成了。\n比我想象的慢得多。

\n

这是我写的代码。

\n

有没有任何解决方案可以加快 Swift 代码的速度?\n(对于代码中的日语注释或文本感到抱歉。)

\n

迅速

\n
import CoreFoundation\n/*\nvar calendar = Calendar.current\ncalender.locale = .init(identifier: "ja.JP")\n*/\n\nvar primeCandidate: Int\nvar prime: [Int] = []\n\nvar countMax: Int\n\nprint("\xe3\x81\x84\xe3\x81\x8f\xe3\x81\xa4\xe7\x9b\xae\xe3\x81\xbe\xe3\x81\xa7\xef\xbc\x9f(\xe6\x9c\x80\xe5\xb0\x8f2\xe3\x80\x81\xe6\x9c\x80\xe5\xa4\xa7100000\xe3\x81\xbe\xe3\x81\xa7)\\n\xe2\x86\x92 ", terminator: "")\n\ncountMax = Int(readLine()!)!\n\nvar flagPrint: Int\n\nprint("\xe8\xa1\xa8\xe7\xa4\xba\xe6\x96\xb9\xe6\xb3\x95\xe3\x82\x92\xe9\x81\xb8\xe3\x82\x93\xe3\x81\xa7\xe3\x81\x8f\xe3\x81\xa0\xe3\x81\x95\xe3\x81\x84\xe3\x80\x82\xef\xbc\x881\xef\xbc\x9a\xe5\x85\xa8\xe3\x81\xa6\xe9\xa0\x86\xe7\x95\xaa\xe3\x81\xab\xe8\xa1\xa8\xe7\xa4\xba\xe3\x80\x812\xef\xbc\x9a\\(countMax)\xe7\x95\xaa\xe7\x9b\xae\xe3\x81\xae\xe4\xb8\x80\xe3\x81\xa4\xe3\x81\xa0\xe3\x81\x91\xe8\xa1\xa8\xe7\xa4\xba\xef\xbc\x89\\n\xe2\x86\x92 ", terminator: "")\nflagPrint = Int(readLine()!)!\n\nprime.append(2)\nprime.append(3)\n\nvar currentMaxCount: Int = 2\nvar numberCount: Int\n\nprimeCandidate = 4\n\nvar flag: Int = 0\nvar ix: Int\n\nlet startedTime = clock()\n//let startedTime = time()\n//.addingTimeInterval(0.0)\n\nwhile currentMaxCount < countMax {\n    for ix in 2..<primeCandidate {\n        if primeCandidate % ix == 0 {\n            flag = 1\n            break\n        }\n    }\n    \n    if flag == 0 {\n        prime.append(primeCandidate)\n        currentMaxCount += 1\n    } else if flag == 1 {\n        flag = 0\n    }\n    \n    primeCandidate += 1\n}\n\nlet endedTime = clock()\n//let endedTime = Time()\n//.timeIntervalSince(startedTime)\n\nif flagPrint == 1 {\n    print("\xe8\xa8\x88\xe7\xae\x97\xe3\x81\x95\xe3\x82\x8c\xe3\x81\x9f\xe7\xb4\xa0\xe6\x95\xb0\xe3\x81\xae\xe4\xb8\x80\xe8\xa6\xa7\xef\xbc\x9a", terminator: "")\n    \n    let completedPrimeNumber = prime.map {\n        $0\n    }\n    \n    \n    print(completedPrimeNumber)\n    //print("\\(prime.map)")\n    \n    print("\\n\\n\xe7\xb5\x82\xe3\x82\x8f\xe3\x82\x8a\xe3\x80\x82")\n    \n} else if flagPrint == 2 {\n    print("\\(currentMaxCount)\xe7\x95\xaa\xe7\x9b\xae\xe3\x81\xae\xe7\xb4\xa0\xe6\x95\xb0\xe3\x81\xaf\\(prime[currentMaxCount - 1])\xe3\x81\xa7\xe3\x81\x99\xe3\x80\x82")\n}\n\nprint("\\(countMax)\xe7\x95\xaa\xe7\x9b\xae\xe3\x81\xae\xe7\xb4\xa0\xe6\x95\xb0\xe3\x81\xbe\xe3\x81\xa7\xe8\xa8\x88\xe7\xae\x97\xe3\x80\x82")\nprint("\xe8\xa8\x88\xe7\xae\x97\xe7\xb5\x8c\xe9\x81\x8e\xe6\x99\x82\xe9\x96\x93: \\(round(Double((endedTime - startedTime) / 100000)) / 10)\xe7\xa7\x92")\n\n
Run Code Online (Sandbox Code Playgroud)\n

\n
#include <stdio.h>\n#include <time.h> //\xe7\xb5\x8c\xe9\x81\x8e\xe6\x99\x82\xe9\x96\x93\xe8\xa8\x88\xe7\xae\x97\xe3\x81\xae\xe3\x81\x9f\xe3\x82\x81\n\nint main(void)\n{\n    int primeCandidate;\n    unsigned int prime[100000];\n    \n    int countMax;\n    \n    printf("\xe3\x81\x84\xe3\x81\x8f\xe3\x81\xa4\xe7\x9b\xae\xe3\x81\xbe\xe3\x81\xa7\xef\xbc\x9f(\xe6\x9c\x80\xe5\xb0\x8f2\xe3\x80\x81\xe6\x9c\x80\xe5\xa4\xa7100000\xe3\x81\xbe\xe3\x81\xa7)\\n\xe2\x86\x92 ");\n    scanf("%d", &countMax);\n    \n    int flagPrint;\n    \n    printf("\xe8\xa1\xa8\xe7\xa4\xba\xe6\x96\xb9\xe6\xb3\x95\xe3\x82\x92\xe9\x81\xb8\xe3\x82\x93\xe3\x81\xa7\xe3\x81\x8f\xe3\x81\xa0\xe3\x81\x95\xe3\x81\x84\xe3\x80\x82\xef\xbc\x881\xef\xbc\x9a\xe5\x85\xa8\xe3\x81\xa6\xe9\xa0\x86\xe7\x95\xaa\xe3\x81\xab\xe8\xa1\xa8\xe7\xa4\xba\xe3\x80\x812\xef\xbc\x9a%d\xe7\x95\xaa\xe7\x9b\xae\xe3\x81\xae\xe4\xb8\x80\xe3\x81\xa4\xe3\x81\xa0\xe3\x81\x91\xe8\xa1\xa8\xe7\xa4\xba\xef\xbc\x89\\n\xe2\x86\x92 ", countMax);\n    scanf("%d", &flagPrint);\n    \n    prime[0] = 2;\n    prime[1] = 3;\n    \n    int currentMaxCount = 2;\n    int numberCount;\n    \n    primeCandidate = 4;\n    \n    int flag = 0;\n    \n    int ix;\n    \n    int startedTime = time(NULL);\n    for(;currentMaxCount < countMax;primeCandidate++){\n        /*\n        for(numberCount = 0;numberCount < currentMaxCount - 1;numberCount++){\n            if(primeCandidate % prime[numberCount] == 0){\n                flag = 1;\n                break;\n            }\n        }\n            */\n            \n        for(ix = 2;ix < primeCandidate;++ix){\n            if(primeCandidate % ix == 0){\n                flag = 1;\n                break;\n            }\n        }\n            \n        if(flag == 0){\n            prime[currentMaxCount] = primeCandidate;\n            currentMaxCount++;\n        } else if(flag == 1){\n            flag = 0;\n        }\n    }\n    int endedTime = time(NULL);\n    \n    if(flagPrint == 1){\n        printf("\xe8\xa8\x88\xe7\xae\x97\xe3\x81\x95\xe3\x82\x8c\xe3\x81\x9f\xe7\xb4\xa0\xe6\x95\xb0\xe3\x81\xae\xe4\xb8\x80\xe8\xa6\xa7\xef\xbc\x9a");\n        for(int i = 0;i < currentMaxCount - 1;i++){\n            printf("%d, ", prime[i]);\n        }\n        printf("%d.\\n\\n\xe7\xb5\x82\xe3\x82\x8f\xe3\x82\x8a", prime[currentMaxCount - 1]);\n    } else if(flagPrint == 2){\n        printf("%d\xe7\x95\xaa\xe7\x9b\xae\xe3\x81\xae\xe7\xb4\xa0\xe6\x95\xb0\xe3\x81\xaf\xe3\x80\x8c%d\xe3\x80\x8d\xe3\x81\xa7\xe3\x81\x99\xe3\x80\x82\\n",currentMaxCount ,prime[currentMaxCount - 1]);\n    }\n    \n    printf("%d\xe7\x95\xaa\xe7\x9b\xae\xe3\x81\xae\xe7\xb4\xa0\xe6\x95\xb0\xe3\x81\xbe\xe3\x81\xa7\xe8\xa8\x88\xe7\xae\x97", countMax);\n    printf("\xe8\xa8\x88\xe7\xae\x97\xe7\xb5\x8c\xe9\x81\x8e\xe6\x99\x82\xe9\x96\x93: %d\xe7\xa7\x92\\n", endedTime - startedTime);\n    \n    return 0;\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n
\n**添加**\n
\n我找到了一些原因。\n
for ix in 0..<currentMaxCount - 1 {\n        if primeCandidate % prime[ix] == 0 {\n            flag = 1\n            break\n        }\n    }\n
Run Code Online (Sandbox Code Playgroud)\n
\n我写了一个代码来比较所有数字。那是一个错误。\n但是,我用这个代码修复了,Swift 在 4.7 秒内完成了计算。\n它也比 C lang 慢 4 倍。\n

Ale*_*ica 5

根本原因

\n

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

\n

它们在高层意图上可能相似,但它们的实现方式足够不同,您可以区分它们的性能。

\n

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

\n

一些示例差异

\n

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

\n
    \n
  1. primeC 代码中的数组使用,unsigned int这通常类似于UInt32. 您的 Swift 代码使用Int,通常相当于Int64. 它的大小增加了一倍,从而使内存使用量增加了一倍,并降低了 CPU 缓存的效率。
  2. \n
  3. prime您的 C 代码在堆栈上预分配数组,而您的 Swift 代码以空开始Array,并根据需要重复增长它。
  4. \n
  5. 您的 C 代码不会预先初始化prime。内存中可能残留的任何垃圾仍然可以观察到,而 Swift 代码会在使用前将所有数组内存清零。
  6. \n
  7. 所有 Swift 算术运算都会检查是否溢出。+这在每个单个、等中引入了一个分支。%这对程序安全很有好处(溢出错误永远不会消失,并且总是会被检测到),但在您确定溢出的性能关键型代码中,这不是最佳选择是不可能的。您可以使用所有运算符的未经检查的变体,例如&+&-等。
  8. \n
\n

总体趋势

\n

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

\n

Swift 中通常存在“逃生舱”,可以让您牺牲安全性或便利性来获得类似 C 的性能。这听起来很糟糕,但可以说,你可以认为 C 只是专门使用这些逃生舱口。没有ArrayDictionary、自动引用计数、Sequence算法等。例如 Swift 所说的UnsafePointer只是 C 中的“指针”。“不安全”是固有的。

\n

提高性能

\n

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

\n
    \n
  1. [Array.reserveCapacity(_:)使用]( https://developer.apple.com/documentation/swift/array/reservecapacity(_:) )预分配足够大的数组。请参阅此注释Array documentation

    \n
    \n

    增加数组的大小

    \n

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

    \n

    如果您大约知道需要存储多少个元素,请在附加到数组之前使用 ReserveCapacity(_:) 方法以避免中间重新分配。使用容量和计数属性来确定数组在不分配更大存储空间的情况下还可以存储多少元素。

    \n

    对于大多数 Element 类型的数组,此存储是连续的内存块。对于 Element 类型为类或 @objc 协议类型的数组,此存储可以是连续的内存块或 NSArray 的实例。因为 NSArray 的任意子类都可以成为数组,所以在这种情况下无法保证表示或效率。

    \n
    \n
  2. \n
  3. 使用UInt32Int32代替Int.

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

    \n
  6. \n
  7. 您可以使用未经检查的算术运算符,例如、&+等。显然,只有当您绝对确定不可能发生溢出时才应该这样做。考虑到有成千上万个与无声溢出相关的错误来了又去,这几乎总是一个糟糕的赌注,但如果你坚持的话,上膛的枪就可以为你服务。&-&%

    \n
  8. \n
\n

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

\n

例如,Swift 约定是通常使用的Int,除非您有充分的理由使用其他东西。例如,Array.count返回一个Int,即使它永远不可能为负数,并且不太可能需要大于UInt32.max

\n


Rob*_*ier 5

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

\n
\xe2\x9e\x9c  x swift -O prime.swift\n\xe3\x81\x84\xe3\x81\x8f\xe3\x81\xa4\xe7\x9b\xae\xe3\x81\xbe\xe3\x81\xa7\xef\xbc\x9f(\xe6\x9c\x80\xe5\xb0\x8f2\xe3\x80\x81\xe6\x9c\x80\xe5\xa4\xa7100000\xe3\x81\xbe\xe3\x81\xa7)\n\xe2\x86\x92 40000\n\xe8\xa1\xa8\xe7\xa4\xba\xe6\x96\xb9\xe6\xb3\x95\xe3\x82\x92\xe9\x81\xb8\xe3\x82\x93\xe3\x81\xa7\xe3\x81\x8f\xe3\x81\xa0\xe3\x81\x95\xe3\x81\x84\xe3\x80\x82\xef\xbc\x881\xef\xbc\x9a\xe5\x85\xa8\xe3\x81\xa6\xe9\xa0\x86\xe7\x95\xaa\xe3\x81\xab\xe8\xa1\xa8\xe7\xa4\xba\xe3\x80\x812\xef\xbc\x9a40000\xe7\x95\xaa\xe7\x9b\xae\xe3\x81\xae\xe4\xb8\x80\xe3\x81\xa4\xe3\x81\xa0\xe3\x81\x91\xe8\xa1\xa8\xe7\xa4\xba\xef\xbc\x89\n\xe2\x86\x92 2\n40000\xe7\x95\xaa\xe7\x9b\xae\xe3\x81\xae\xe7\xb4\xa0\xe6\x95\xb0\xe3\x81\xaf479909\xe3\x81\xa7\xe3\x81\x99\xe3\x80\x82\n40000\xe7\x95\xaa\xe7\x9b\xae\xe3\x81\xae\xe7\xb4\xa0\xe6\x95\xb0\xe3\x81\xbe\xe3\x81\xa7\xe8\xa8\x88\xe7\xae\x97\xe3\x80\x82\n\xe8\xa8\x88\xe7\xae\x97\xe7\xb5\x8c\xe9\x81\x8e\xe6\x99\x82\xe9\x96\x93: 5.9\xe7\xa7\x92\n\xe2\x9e\x9c  x clang -O3 prime.c && ./a.out\n\xe3\x81\x84\xe3\x81\x8f\xe3\x81\xa4\xe7\x9b\xae\xe3\x81\xbe\xe3\x81\xa7\xef\xbc\x9f(\xe6\x9c\x80\xe5\xb0\x8f2\xe3\x80\x81\xe6\x9c\x80\xe5\xa4\xa7100000\xe3\x81\xbe\xe3\x81\xa7)\n\xe2\x86\x92 40000\n\xe8\xa1\xa8\xe7\xa4\xba\xe6\x96\xb9\xe6\xb3\x95\xe3\x82\x92\xe9\x81\xb8\xe3\x82\x93\xe3\x81\xa7\xe3\x81\x8f\xe3\x81\xa0\xe3\x81\x95\xe3\x81\x84\xe3\x80\x82\xef\xbc\x881\xef\xbc\x9a\xe5\x85\xa8\xe3\x81\xa6\xe9\xa0\x86\xe7\x95\xaa\xe3\x81\xab\xe8\xa1\xa8\xe7\xa4\xba\xe3\x80\x812\xef\xbc\x9a40000\xe7\x95\xaa\xe7\x9b\xae\xe3\x81\xae\xe4\xb8\x80\xe3\x81\xa4\xe3\x81\xa0\xe3\x81\x91\xe8\xa1\xa8\xe7\xa4\xba\xef\xbc\x89\n\xe2\x86\x92 2\n40000\xe7\x95\xaa\xe7\x9b\xae\xe3\x81\xae\xe7\xb4\xa0\xe6\x95\xb0\xe3\x81\xaf\xe3\x80\x8c479909\xe3\x80\x8d\xe3\x81\xa7\xe3\x81\x99\xe3\x80\x82\n40000\xe7\x95\xaa\xe7\x9b\xae\xe3\x81\xae\xe7\xb4\xa0\xe6\x95\xb0\xe3\x81\xbe\xe3\x81\xa7\xe8\xa8\x88\xe7\xae\x97\xe8\xa8\x88\xe7\xae\x97\xe7\xb5\x8c\xe9\x81\x8e\xe6\x99\x82\xe9\x96\x93: 6\xe7\xa7\x92\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n