亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Swift Beta性能:對數組進行排序

Swift Beta性能:對數組進行排序

喵喔喔 2020-02-03 13:54:52
我在Swift Beta中實現一種算法,發現性能非常差。深入研究后,我意識到瓶頸之一就是對數組進行排序一樣簡單。相關部分在這里:let n = 1000000var x =  [Int](repeating: 0, count: n)for i in 0..<n {    x[i] = random()}// start clock herelet y = sort(x)// stop clock here在C ++中,類似的操作在我的計算機上花費0.06s。在Python中,它花費0.6秒(絕招,僅y =整數列表的sorted(x))。在Swift中,如果使用以下命令進行編譯,則需要6s:xcrun swift -O3 -sdk `xcrun --show-sdk-path --sdk macosx`如果使用以下命令進行編譯,則最多需要88s:xcrun swift -O0 -sdk `xcrun --show-sdk-path --sdk macosx`Xcode中具有“發布”與“調試”構建的時序是相似的。怎么了 與C ++相比,我可以理解一些性能損失,但與純Python相比,卻不能降低10倍。編輯:天氣注意到更改-O3為-Ofast使此代碼運行幾乎與C ++版本一樣快!但是,-Ofast它極大地改變了語言的語義-在我的測試中,它禁用了對整數溢出和數組索引溢出的檢查。例如,使用-Ofast以下Swift代碼,無提示地運行而不會崩潰(并打印出一些垃圾):let n = 10000000print(n*n*n*n*n)let x =  [Int](repeating: 10, count: n)print(x[n])因此,-Ofast這不是我們想要的。Swift的全部要點是我們擁有安全網。當然,安全網會對性能產生一些影響,但是它們不應使程序慢100倍。請記住,Java已經檢查了數組的邊界,在典型情況下,速度下降的幅度遠小于2。在Clang和GCC中,我們必須-ftrapv檢查(有符號的)整數溢出,但也沒有那么慢。因此產生了一個問題:如何在Swift中獲得合理的性能而又不損失安全網?編輯2:我進行了一些基準測試,其中的循環非常簡單for i in 0..<n {    x[i] = x[i] ^ 12345678}(這里有xor操作,以便我可以更容易地在匯編代碼中找到相關的循環。我試圖選擇一個易于發現但又“無害”的操作,因為它不需要任何相關的檢查。到整數溢出。)同樣,-O3和之間的性能存在巨大差異-Ofast。所以我看了一下匯編代碼:有了-Ofast我,我得到了我所期望的。相關部分是一個包含5條機器語言指令的循環。隨著-O3我得到的東西,出乎我的想象。內部循環跨越88行匯編代碼。我沒有嘗試理解所有內容,但是最可疑的部分是“ callq _swift_retain”的13個調用和“ callq _swift_release”的另外13個調用。也就是說,內部循環中有26個子例程調用!編輯3:在評論中,費魯奇奧要求提供不違反內置函數(例如排序)的公平基準。我認為以下程序是一個很好的例子:let n = 10000var x = [Int](repeating: 1, count: n)for i in 0..<n {    for j in 0..<n {        x[i] = x[j]    }}沒有算術運算,因此我們不必擔心整數溢出。我們唯一要做的就是大量數組引用。結果就在這里-與-Ofast相比,Swift -O3損失了將近500倍:C ++ -O3:0.05 sC ++ -O0:0.4秒Java:0.2秒帶有PyPy的Python:0.5秒Python:12秒迅捷-Ofast:0.05 sSwift -O3:23秒迅捷-O0:443秒(如果擔心編譯器可能會完全優化無意義的循環,則可以將其更改為eg x[i] ^= x[j],并添加一個輸出輸出的print語句x[0]。這不會改變任何內容;時序將非常相似。)是的,這里的Python實現是一個愚蠢的純Python實現,帶有一個整數列表和嵌套的for循環。這應該是很多比未優化雨燕慢。Swift和數組索引似乎嚴重破壞了某些東西。
查看完整描述

3 回答

?
江戶川亂折騰

TA貢獻1851條經驗 獲得超5個贊

使用默認的發行優化級別[-O],在此基準測試中,Swift 1.0現在與C一樣快。


這是Swift Beta中的就地快速排序:


func quicksort_swift(inout a:CInt[], start:Int, end:Int) {

    if (end - start < 2){

        return

    }

    var p = a[start + (end - start)/2]

    var l = start

    var r = end - 1

    while (l <= r){

        if (a[l] < p){

            l += 1

            continue

        }

        if (a[r] > p){

            r -= 1

            continue

        }

        var t = a[l]

        a[l] = a[r]

        a[r] = t

        l += 1

        r -= 1

    }

    quicksort_swift(&a, start, r + 1)

    quicksort_swift(&a, r + 1, end)

}

和在C中相同:


void quicksort_c(int *a, int n) {

    if (n < 2)

        return;

    int p = a[n / 2];

    int *l = a;

    int *r = a + n - 1;

    while (l <= r) {

        if (*l < p) {

            l++;

            continue;

        }

        if (*r > p) {

            r--;

            continue;

        }

        int t = *l;

        *l++ = *r;

        *r-- = t;

    }

    quicksort_c(a, r - a + 1);

    quicksort_c(l, a + n - l);

}

兩種工作:


var a_swift:CInt[] = [0,5,2,8,1234,-1,2]

var a_c:CInt[] = [0,5,2,8,1234,-1,2]


quicksort_swift(&a_swift, 0, a_swift.count)

quicksort_c(&a_c, CInt(a_c.count))


// [-1, 0, 2, 2, 5, 8, 1234]

// [-1, 0, 2, 2, 5, 8, 1234]

兩者都在與編寫的相同的程序中調用。


var x_swift = CInt[](count: n, repeatedValue: 0)

var x_c = CInt[](count: n, repeatedValue: 0)

for var i = 0; i < n; ++i {

    x_swift[i] = CInt(random())

    x_c[i] = CInt(random())

}


let swift_start:UInt64 = mach_absolute_time();

quicksort_swift(&x_swift, 0, x_swift.count)

let swift_stop:UInt64 = mach_absolute_time();


let c_start:UInt64 = mach_absolute_time();

quicksort_c(&x_c, CInt(x_c.count))

let c_stop:UInt64 = mach_absolute_time();

這會將絕對時間轉換為秒:


static const uint64_t NANOS_PER_USEC = 1000ULL;

static const uint64_t NANOS_PER_MSEC = 1000ULL * NANOS_PER_USEC;

static const uint64_t NANOS_PER_SEC = 1000ULL * NANOS_PER_MSEC;


mach_timebase_info_data_t timebase_info;


uint64_t abs_to_nanos(uint64_t abs) {

    if ( timebase_info.denom == 0 ) {

        (void)mach_timebase_info(&timebase_info);

    }

    return abs * timebase_info.numer  / timebase_info.denom;

}


double abs_to_seconds(uint64_t abs) {

    return abs_to_nanos(abs) / (double)NANOS_PER_SEC;

}

以下是編譯器優化級別的摘要:


[-Onone] no optimizations, the default for debug.

[-O]     perform optimizations, the default for release.

[-Ofast] perform optimizations and disable runtime overflow checks and runtime type checks.

時間與秒[-Onone]為N = 10_000:


Swift:            0.895296452

C:                0.001223848

這是Swift的內置sort(),其n = 10_000:


Swift_builtin:    0.77865783

這是[-O],n = 10_000:


Swift:            0.045478346

C:                0.000784666

Swift_builtin:    0.032513488

如您所見,Swift的性能提高了20倍。


根據mweathers的回答,設置[-Ofast]會帶來真正的不同,導致這些時間為n = 10_000:


Swift:            0.000706745

C:                0.000742374

Swift_builtin:    0.000603576

對于n = 1_000_000:


Swift:            0.107111846

C:                0.114957179

Swift_sort:       0.092688548

為了比較,這是[-Onone]的n = 1_000_000:


Swift:            142.659763258

C:                0.162065333

Swift_sort:       114.095478272

因此,在開發的這個階段,沒有優化的Swift幾乎比該基準測試中的C慢1000倍。另一方面,將兩個編譯器都設置為[-Ofast],即使實際上不比C稍好,Swift的實際效果也至少一樣好。


已經指出,[-Ofast]更改了語言的語義,使其潛在地不安全。這就是Apple在Xcode 5.0發行說明中指出的內容:


LLVM中提供了新的優化級別-Ofast,可以進行積極的優化。-Ofast放寬了一些保守的限制,大多數情況下對于浮點運算來說是安全的,這對大多數代碼來說都是安全的。它可以從編譯器中獲得明顯的高性能優勢。


他們全都主張。無論是不是明智,我都不能說,但是據我所知,如果您不進行高精度浮點運算并且您確信沒有整數或整數,那么在發行版中使用[-Ofast]似乎足夠合理。程序中可能發生數組溢出。如果您確實需要高性能和溢出檢查/精確算術,請立即選擇另一種語言。


測試版3更新:


n = 10_000,帶有[-O]:


Swift:            0.019697268

C:                0.000718064

Swift_sort:       0.002094721

Swift通常要快一些,看起來Swift的內置排序已經發生了很大變化。


最后更新:


[-Onone]:


Swift:   0.678056695

C:       0.000973914

[-O]:


Swift:   0.001158492

C:       0.001192406

[-Ounchecked]:


Swift:   0.000827764

C:       0.001078914


查看完整回答
反對 回復 2020-02-03
?
慕標5832272

TA貢獻1966條經驗 獲得超4個贊

我決定看看這個很有趣,下面是我得到的時間安排:


Swift 4.0.2           :   0.83s (0.74s with `-Ounchecked`)

C++ (Apple LLVM 8.0.0):   0.74s

迅速

// Swift 4.0 code

import Foundation


func doTest() -> Void {

    let arraySize = 10000000

    var randomNumbers = [UInt32]()


    for _ in 0..<arraySize {

        randomNumbers.append(arc4random_uniform(UInt32(arraySize)))

    }


    let start = Date()

    randomNumbers.sort()

    let end = Date()


    print(randomNumbers[0])

    print("Elapsed time: \(end.timeIntervalSince(start))")

}


doTest()

結果:


斯威夫特1.1


xcrun swiftc --version

Swift version 1.1 (swift-600.0.54.20)

Target: x86_64-apple-darwin14.0.0


xcrun swiftc -O SwiftSort.swift

./SwiftSort     

Elapsed time: 1.02204304933548

斯威夫特1.2


xcrun swiftc --version

Apple Swift version 1.2 (swiftlang-602.0.49.6 clang-602.0.49)

Target: x86_64-apple-darwin14.3.0


xcrun -sdk macosx swiftc -O SwiftSort.swift

./SwiftSort     

Elapsed time: 0.738763988018036

雨燕2.0


xcrun swiftc --version

Apple Swift version 2.0 (swiftlang-700.0.59 clang-700.0.72)

Target: x86_64-apple-darwin15.0.0


xcrun -sdk macosx swiftc -O SwiftSort.swift

./SwiftSort     

Elapsed time: 0.767306983470917

如果我用編譯,性能似乎相同-Ounchecked。


斯威夫特3.0


xcrun swiftc --version

Apple Swift version 3.0 (swiftlang-800.0.46.2 clang-800.0.38)

Target: x86_64-apple-macosx10.9


xcrun -sdk macosx swiftc -O SwiftSort.swift

./SwiftSort     

Elapsed time: 0.939633965492249


xcrun -sdk macosx swiftc -Ounchecked SwiftSort.swift

./SwiftSort     

Elapsed time: 0.866258025169373

似乎是一個性能回歸從雨燕2.0至3.0雨燕,而且我也看到之間的差異-O,并-Ounchecked首次。


迅捷4.0


xcrun swiftc --version

Apple Swift version 4.0.2 (swiftlang-900.0.69.2 clang-900.0.38)

Target: x86_64-apple-macosx10.9


xcrun -sdk macosx swiftc -O SwiftSort.swift

./SwiftSort     

Elapsed time: 0.834299981594086


xcrun -sdk macosx swiftc -Ounchecked SwiftSort.swift

./SwiftSort     

Elapsed time: 0.742045998573303

Swift 4再次提高了性能,同時保持-O和之間的差距-Ounchecked。-O -whole-module-optimization似乎沒有什么不同。


C ++

#include <chrono>

#include <iostream>

#include <vector>

#include <cstdint>

#include <stdlib.h>


using namespace std;

using namespace std::chrono;


int main(int argc, const char * argv[]) {

    const auto arraySize = 10000000;

    vector<uint32_t> randomNumbers;


    for (int i = 0; i < arraySize; ++i) {

        randomNumbers.emplace_back(arc4random_uniform(arraySize));

    }


    const auto start = high_resolution_clock::now();

    sort(begin(randomNumbers), end(randomNumbers));

    const auto end = high_resolution_clock::now();


    cout << randomNumbers[0] << "\n";

    cout << "Elapsed time: " << duration_cast<duration<double>>(end - start).count() << "\n";


    return 0;

}

結果:


蘋果C6.0


clang++ --version

Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn)

Target: x86_64-apple-darwin14.0.0

Thread model: posix


clang++ -O3 -std=c++11 CppSort.cpp -o CppSort

./CppSort     

Elapsed time: 0.688969

蘋果C 6.1.0


clang++ --version

Apple LLVM version 6.1.0 (clang-602.0.49) (based on LLVM 3.6.0svn)

Target: x86_64-apple-darwin14.3.0

Thread model: posix


clang++ -O3 -std=c++11 CppSort.cpp -o CppSort

./CppSort     

Elapsed time: 0.670652

蘋果Clang 7.0.0


clang++ --version

Apple LLVM version 7.0.0 (clang-700.0.72)

Target: x86_64-apple-darwin15.0.0

Thread model: posix


clang++ -O3 -std=c++11 CppSort.cpp -o CppSort

./CppSort     

Elapsed time: 0.690152

蘋果鐺8.0.0


clang++ --version

Apple LLVM version 8.0.0 (clang-800.0.38)

Target: x86_64-apple-darwin15.6.0

Thread model: posix


clang++ -O3 -std=c++11 CppSort.cpp -o CppSort

./CppSort     

Elapsed time: 0.68253

蘋果Clang 9.0.0


clang++ --version

Apple LLVM version 9.0.0 (clang-900.0.38)

Target: x86_64-apple-darwin16.7.0

Thread model: posix


clang++ -O3 -std=c++11 CppSort.cpp -o CppSort

./CppSort     

Elapsed time: 0.736784

判決

在撰寫本文時,Swift的排序速度很快,但-O與使用上述編譯器和庫進行編譯時,C ++的排序還不及C ++的排序。使用-Ounchecked,它似乎與Swift 4.0.2和Apple LLVM 9.0.0中的C ++一樣快。


查看完整回答
反對 回復 2020-02-03
  • 3 回答
  • 0 關注
  • 777 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號