स्विफ्ट बीटा प्रदर्शन: सरणियों को सॉर्ट करना


929

मैं स्विफ्ट बीटा में एक एल्गोरिथ्म लागू कर रहा था और देखा कि प्रदर्शन बहुत खराब था। गहरी खुदाई के बाद मुझे महसूस हुआ कि अड़चनों में से एक छँटाई सरणियों की तरह सरल थी। प्रासंगिक हिस्सा यहाँ है:

let n = 1000000
var x =  [Int](repeating: 0, count: n)
for i in 0..<n {
    x[i] = random()
}
// start clock here
let y = sort(x)
// stop clock here

C ++ में, एक समान ऑपरेशन मेरे कंप्यूटर पर 0.06s लेता है।

पायथन में, यह 0.6 (कोई चाल नहीं, सिर्फ y = सॉर्ट किया गया (x) पूर्णांकों की सूची के लिए) लेता है ।

यदि मैं इसे निम्नलिखित कमांड के साथ संकलित करता हूं तो स्विफ्ट में 6s लगते हैं :

xcrun swift -O3 -sdk `xcrun --show-sdk-path --sdk macosx`

और अगर मुझे इसे निम्नलिखित कमांड के साथ संकलित करने में 88 से अधिक का समय लगता है :

xcrun swift -O0 -sdk `xcrun --show-sdk-path --sdk macosx`

"रिलीज़" बनाम "डीबग" बिल्ड के साथ एक्सकोड में समय समान हैं।

यहाँ क्या गलत है? मैं C ++ की तुलना में कुछ प्रदर्शन हानि को समझ सकता था, लेकिन शुद्ध पायथन की तुलना में 10 गुना मंदी नहीं थी।


संपादित करें: मौसम ने ध्यान दिया कि इस कोड -O3को बदलने से -Ofastसी ++ संस्करण के रूप में लगभग तेजी से चलता है! हालाँकि, -Ofastभाषा के शब्दार्थ को बहुत बदल देता है - मेरे परीक्षण में, इसने पूर्णांक के ओवरले और सरणी अनुक्रमण के लिए चेक को निष्क्रिय कर दिया । उदाहरण के लिए, -Ofastनिम्नलिखित स्विफ्ट कोड के दुर्घटनाग्रस्त होने के बिना चुपचाप चलता है (और कुछ कचरा प्रिंट करता है):

let n = 10000000
print(n*n*n*n*n)
let x =  [Int](repeating: 10, count: n)
print(x[n])

तो -Ofastजैसा हम चाहते हैं वैसा नहीं है; स्विफ्ट की पूरी बात यह है कि हमारे पास सुरक्षा जाल हैं। बेशक, सुरक्षा जाल के प्रदर्शन पर कुछ प्रभाव पड़ता है, लेकिन उन्हें कार्यक्रमों को 100 गुना धीमा नहीं करना चाहिए। याद रखें कि जावा पहले से ही सरणी सीमाओं के लिए जाँच करता है, और विशिष्ट मामलों में, मंदी 2 से बहुत कम कारक है। और क्लैंग और जीसीसी में हमने -ftrapvपूर्णांक ओवरलेप्स की जाँच (हस्ताक्षरित) के लिए प्राप्त किया है, और यह धीमा नहीं है, या तो।

इसलिए सवाल: हम बिना सुरक्षा जाल खोए स्विफ्ट में उचित प्रदर्शन कैसे प्राप्त कर सकते हैं?


संपादित करें 2: मैंने कुछ और बेंचमार्किंग की, जिनकी तर्ज पर बहुत ही सरल छोरों के साथ

for i in 0..<n {
    x[i] = x[i] ^ 12345678
}

(यहां एक्सर ऑपरेशन सिर्फ इतना है कि मैं असेंबली कोड में संबंधित लूप को आसानी से ढूंढ सकता हूं। मैंने एक ऐसा ऑपरेशन चुनने की कोशिश की, जो आसान हो, लेकिन इस अर्थ में "हानिरहित" भी हो कि उसे किसी भी तरह के चेक की आवश्यकता न हो। पूर्णांक तक।)

फिर, के बीच -O3और प्रदर्शन में बहुत अंतर था -Ofast। इसलिए मेरी नजर विधानसभा कोड पर थी:

  • के साथ -Ofastमुझे बहुत उम्मीद है कि मुझे क्या उम्मीद होगी। संबंधित भाग 5 मशीन भाषा निर्देशों के साथ एक लूप है।

  • के साथ -O3मुझे कुछ ऐसा मिला जो मेरी कल्पना से परे था। आंतरिक लूप असेंबली कोड की 88 पंक्तियों को फैलाता है। मैंने यह सब समझने की कोशिश नहीं की, लेकिन सबसे संदिग्ध भाग "callq _swift_retain" के 13 और "callq _swift_release" के 13 अन्य चालान हैं। यानी, इनर लूप में 26 सबरूटीन कॉल !


संपादन 3: टिप्पणियों में, फेर्रुकियो ने उन बेंचमार्क के लिए कहा जो इस अर्थ में उचित हैं कि वे अंतर्निहित कार्यों (जैसे सॉर्ट) पर भरोसा नहीं करते हैं। मुझे लगता है कि निम्नलिखित कार्यक्रम एक अच्छा उदाहरण है:

let n = 10000
var x = [Int](repeating: 1, count: n)
for i in 0..<n {
    for j in 0..<n {
        x[i] = x[j]
    }
}

कोई अंकगणित नहीं है, इसलिए हमें पूर्णांक ओवरफ्लो के बारे में चिंता करने की आवश्यकता नहीं है। केवल एक चीज जो हम करते हैं वह है केवल बहुत सारे सरणी संदर्भ। और परिणाम यहाँ हैं- Swift -O3 एक कारक द्वारा खो देता है -Ofast की तुलना में लगभग 500:

  • सी ++ -ओ 3: 0.05 एस
  • C ++ -O0: 0.4 s
  • जावा: 0.2 एस
  • अजगर अजगर के साथ: 0.5 एस
  • अजगर: 12 एस
  • स्विफ्ट -ऑफ्टर: 0.05 एस
  • स्विफ्ट -ओ 3: 23 एस
  • स्विफ्ट -ओ0: 443 एस

(यदि आप चिंतित हैं कि संकलक पूरी तरह से व्यर्थ छोरों का अनुकूलन कर सकता है, तो आप इसे उदा में बदल सकते हैं x[i] ^= x[j], और एक प्रिंट स्टेटमेंट जोड़ सकते हैं जो आउटपुट x[0]करता है। यह कुछ भी नहीं बदलता है; समय बहुत समान होगा।)

और हां, यहां पायथन कार्यान्वयन एक शुद्ध शुद्ध पायथन कार्यान्वयन था, जिसमें चींटियों की सूची थी और छोरों के लिए नेस्टेड थी। यह बिना किसी बदलाव के स्विफ्ट की तुलना में बहुत धीमा होना चाहिए । ऐसा लगता है कि स्विफ्ट और एरे इंडेक्सिंग के साथ कुछ गंभीर रूप से टूट गया है।


संपादित करें 4: ये मुद्दे (साथ ही कुछ अन्य प्रदर्शन मुद्दे) Xcode 6 बीटा 5 में तय किए गए हैं।

छँटाई के लिए, मेरे पास अब निम्नलिखित समय हैं:

  • clang ++ -O3: 0.06 एस
  • स्विफ्ट-ऑप्शन: 0.1 एस
  • स्विफ्ट-ओ: 0.1 एस
  • स्विफ्ट: 4 एस

नेस्टेड छोरों के लिए:

  • clang ++ -O3: 0.06 एस
  • स्विफ्ट-ऑउटफिट: 0.3 एस
  • स्विफ्ट-ओ: 0.4 एस
  • स्विफ्ट: 540 एस

ऐसा लगता है कि असुरक्षित -Ofast(उर्फ -Ounchecked) का उपयोग करने का कोई कारण नहीं है ; सादा -Oसमान रूप से अच्छा कोड पैदा करता है।


20
यहाँ एक और "स्विफ्ट सी की तुलना में 100 गुना धीमी है" सवाल: stackoverflow.com/questions/24102609/…
Jukka Suomela

16
और यहाँ Apple के विपणन सामग्री पर स्विफ्ट की छँटाई में अच्छे प्रदर्शन से संबंधित चर्चा है: programmers.stackexchange.com/q/242816/913
Jukka Suomela

2
आप के साथ संकलन कर सकते हैं: xcrun --sdk macosx swift -O3। यह छोटा है।
दक्षिणी आतिथ्य

3
यह लिंक ऑब्जेक्टिव-सी की तुलना में कुछ अन्य बुनियादी ऑपरेशन दिखाता है।
वोल्ड

4
बीटा 5 के साथ स्विफ्ट की गति में पर्याप्त सुधार हुआ है - इस पोस्ट को अधिक विवरण के लिए जेसी स्क्वायर्स देखें ।
नैट कुक

जवाबों:


460

tl; ड्रिफ्ट स्विफ्ट 1.0 इस बेंचमार्क द्वारा C के रूप में डिफ़ॉल्ट रिलीज ऑप्टिमाइजेशन स्तर [-ओ] का उपयोग करके अब तेज है।


यहाँ स्विफ्ट बीटा में इन-प्लेस क्विकोर्ट है:

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

और सी में समान:

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] के लिए एन = 10_000 :

Swift:            0.895296452
C:                0.001223848

यहाँ स्विफ्ट का बिलियन प्रकार () n = 10_000 के लिए है :

Swift_builtin:    0.77865783

यहाँ n-10_000 के लिए [-ओ] है :

Swift:            0.045478346
C:                0.000784666
Swift_builtin:    0.032513488

जैसा कि आप देख सकते हैं, स्विफ्ट के प्रदर्शन में 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

तुलना के लिए, यह n = 1_000_000 के लिए [-one] के साथ है :

Swift:            142.659763258
C:                0.162065333
Swift_sort:       114.095478272

इस दिशा में इस स्तर पर स्विफ्ट इस बेंचमार्क में C की तुलना में लगभग 1000x धीमी थी। दूसरी ओर दोनों कंपाइलरों के साथ [-ओफ्ट] पर सेट करें, स्विफ्ट वास्तव में कम से कम और साथ ही सी से बेहतर नहीं है।

यह बताया गया है कि [-ओएफ़टी] भाषा के शब्दार्थ को बदल देता है, जिससे यह संभावित रूप से असुरक्षित हो जाता है। यह वही है जो Apple Xcode 5.0 रिलीज़ नोटों में बताता है:

एलवीवीएम में उपलब्ध एक नया ऑप्टिमाइज़ेशन लेवल-ओफ़ेक्ट, आक्रामक अनुकूलन को सक्षम करता है। -ऑप्ट कुछ रूढ़िवादी प्रतिबंधों को शिथिल करता है, ज्यादातर फ़्लोटिंग-पॉइंट ऑपरेशनों के लिए, जो कि अधिकांश कोड के लिए सुरक्षित हैं। यह संकलक से महत्वपूर्ण उच्च-प्रदर्शन जीत हासिल कर सकता है।

वे सभी इसकी वकालत करते हैं। यह समझदारी है या नहीं, मैं नहीं कह सकता, लेकिन मैं जो बता सकता हूं वह उचित उपयोग करने के लिए पर्याप्त है [-ओफ़ास्ट] एक रिलीज में यदि आप उच्च-सटीक फ़्लोटिंग पॉइंट अंकगणित नहीं कर रहे हैं और आप आश्वस्त हैं कि कोई पूर्णांक या नहीं आपके प्रोग्राम में सरणी ओवरफ्लो संभव है। यदि आपको उच्च प्रदर्शन और अतिप्रवाह चेक / सटीक अंकगणित की आवश्यकता है तो अभी के लिए दूसरी भाषा चुनें।

बीटा 3 अद्यतन:

एन = 10_000 साथ [-O] :

Swift:            0.019697268
C:                0.000718064
Swift_sort:       0.002094721

सामान्य तौर पर स्विफ्ट थोड़ा तेज है और ऐसा लग रहा है कि स्विफ्ट की बिल्ट-इन काफी बदल गई है।

अंतिम अद्यतन:

[-Onone] :

Swift:   0.678056695
C:       0.000973914

[-ओ] :

Swift:   0.001158492
C:       0.001192406

[-Ounchecked] :

Swift:   0.000827764
C:       0.001078914

25
मध्यवर्ती एसआईएल कोड को आउटपुट करने के लिए -मिट-साइल का उपयोग करके पता चलता है कि क्या बरकरार रखा जा रहा है (अर्घ, स्टैक ओवरफ्लो इस प्रारूप को असंभव बना रहा है)। यह ऐरे में एक आंतरिक बफर ऑब्जेक्ट है। यह निश्चित रूप से एक ऑप्टिमाइज़र बग की तरह लगता है, एआरसी ऑप्टिमाइज़र बिना -OIFT के रिटर्न्स को हटाने में सक्षम होना चाहिए।
कैटफ़िश_मैन

'अगर हम टोस्ट ऑप्टिमाइज़ेशन का उपयोग करना चाहते हैं, तो हमें असहमत होना पड़ेगा। सी। जैसी दूसरी भाषा चुनने पर इसे बाउंड चेक और अन्य छोटी समस्याओं के प्रश्न के साथ समान रूप से निपटना होगा। स्विफ्ट ठीक ठाक है क्योंकि यह डिफ़ॉल्ट रूप से सुरक्षित है और यदि आवश्यक हो तो वैकल्पिक रूप से तेज और असुरक्षित है। यह प्रोग्रामर को आपके कोड को डीबग करने की अनुमति देता है, यह सुनिश्चित करने के लिए कि सब कुछ ठीक है और टोस्ट का उपयोग करके संकलित करें। आधुनिक मानकों का उपयोग करने की संभावना और फिर भी सी जैसी "असुरक्षित" भाषा की शक्ति बहुत शांत है।
वाल्सी

2
यदि आप मुझे बता सकते हैं कि यह अवैध कैसे हो सकता है तो कृपया करें। मैं हमेशा अधिक सीखना पसंद करता हूं
जोसेफ मार्क

3
एक अंतिम अपडेट किया गया, स्विफ्ट अब इस बेंचमार्क द्वारा सी के रूप में तेजी से मानक अनुकूलन का उपयोग कर रहा है।
जोसेफ मार्क

4
युक्ति: यदि आपकी छोटी से छोटी पार्टीशन पर सबसे पहले पुनरावृत्ति होती है, तो स्विफ्ट और सी, दोनों ही क्विकॉर्ट के कार्यान्वयन में सुधार किया जा सकता है ! (बाएं विभाजन को हमेशा पहले करने के बजाय।) सबसे खराब स्थिति में एक साधारण धुरी चयन के साथ लागू किया गया क्विकॉर्ट O (n ^ 2) समय लेता है, लेकिन इस सबसे खराब स्थिति में भी आपको केवल O (लॉग एन) स्टैक स्थान की आवश्यकता होती है। पहले छोटे विभाजन पर।
मैकनील शोनल

108

टीएल; डीआर : हां, केवल स्विफ्ट भाषा का कार्यान्वयन धीमा है, अभी । यदि आपको तेज, संख्यात्मक (और अन्य प्रकार के कोड, संभवतः) कोड की आवश्यकता है, तो बस एक दूसरे के साथ जाएं। भविष्य में, आपको अपनी पसंद का पुनर्मूल्यांकन करना चाहिए। यह अधिकांश एप्लिकेशन कोड के लिए पर्याप्त हो सकता है जो उच्च स्तर पर लिखा गया है, हालांकि।

एसआईएल और एलएलवीएम आईआर में जो मैं देख रहा हूं, उससे ऐसा लगता है कि उन्हें रिटेन और रिलीज को हटाने के लिए अनुकूलन का एक समूह चाहिए, जो क्लैंग (ऑब्जेक्टिव-सी) के लिए लागू किया जा सकता है , लेकिन उन्होंने अभी तक उन्हें पोर्ट नहीं किया है। यही वह सिद्धांत है जिसके साथ मैं (अभी के लिए) जा रहा हूं ... मुझे अभी भी यह पुष्टि करने की आवश्यकता है कि क्लैंग इसके बारे में कुछ करता है), क्योंकि इस प्रश्न के अंतिम परीक्षण-मामले पर एक प्रोफाइलर चलता है, यह "सुंदर" परिणाम देता है:

-O3 पर समय रूपरेखा समय-रूपरेखा

जैसा कि कई अन्य लोगों ने कहा था, -Ofastपूरी तरह से असुरक्षित है और भाषा के शब्दार्थ को बदल देता है। मेरे लिए, "यदि आप इसका उपयोग करने जा रहे हैं, तो किसी अन्य भाषा का उपयोग करें" चरण। मैं उस विकल्प का बाद में पुनर्मूल्यांकन करूंगा, अगर यह बदल जाता है।

-O3हमें एक गुच्छा मिलता है swift_retainऔर swift_releaseकॉल करता है, ईमानदारी से, इस उदाहरण के लिए ऐसा नहीं होना चाहिए। ऑप्टिमाइज़र को एएएफआईसीटी को खत्म करना चाहिए (अधिकांश) उन्हें, क्योंकि यह सरणी के बारे में अधिकांश जानकारी जानता है, और जानता है कि इसके पास (कम से कम) इसका एक मजबूत संदर्भ है।

यह अधिक रिटेंशन का उत्सर्जन नहीं करना चाहिए जब यह कॉलिंग फ़ंक्शन भी नहीं है जो वस्तुओं को जारी कर सकता है। मुझे नहीं लगता कि एक सरणी निर्माता एक सरणी वापस कर सकता है जो कि जो पूछा गया था उससे छोटा है, जिसका अर्थ है कि उत्सर्जित किए गए बहुत सारे चेक बेकार हैं। यह भी पता है कि पूर्णांक कभी भी 10k से ऊपर नहीं होगा, इसलिए अतिप्रवाह जांच को अनुकूलित किया जा सकता है ( -Ofastविचित्रता के कारण नहीं , लेकिन भाषा के शब्दार्थ के कारण (और कुछ भी नहीं जो उस संस्करण को बदल रहा है और न ही इसे एक्सेस कर सकता है, और 10k तक जोड़ सकता है) प्रकार के लिए सुरक्षित है Int)।

कंपाइलर सरणी या सरणी तत्वों को अनबॉक्स करने में सक्षम नहीं हो सकता है, हालांकि, चूंकि वे पास हो रहे हैं sort(), जो कि एक बाहरी फ़ंक्शन है और उन तर्कों को प्राप्त करना है जो इसकी अपेक्षा कर रहे हैं। इससे हमें Intअप्रत्यक्ष रूप से मूल्यों का उपयोग करना होगा, जिससे यह थोड़ा धीमा हो जाएगा। यह बदल सकता है यदि sort()जेनेरिक फ़ंक्शन (मल्टी-मेथड तरीके से नहीं) कंपाइलर को उपलब्ध हो और इनलेट हो।

यह एक बहुत ही नई (सार्वजनिक रूप से) भाषा है, और यह मेरे द्वारा की जा रही बदलावों से गुजर रही है, क्योंकि स्विफ्ट भाषा से जुड़े लोग (भारी) फीडबैक मांगते हैं और वे सभी कहते हैं कि भाषा समाप्त नहीं हुई है और इच्छाशक्ति परिवर्तन।

उपयोग किया गया कोड:

import Cocoa

let swift_start = NSDate.timeIntervalSinceReferenceDate();
let n: Int = 10000
let x = Int[](count: n, repeatedValue: 1)
for i in 0..n {
    for j in 0..n {
        let tmp: Int = x[j]
        x[i] = tmp
    }
}
let y: Int[] = sort(x)
let swift_stop = NSDate.timeIntervalSinceReferenceDate();

println("\(swift_stop - swift_start)s")

पुनश्च: मैं ऑब्जेक्ट-सी का विशेषज्ञ नहीं हूं और न ही कोको , ऑब्जेक्टिव-सी या स्विफ्ट रनटाइम की सभी सुविधाएं । मैं कुछ चीजें भी मान सकता हूं जो मैंने नहीं लिखीं।


कंपाइलर सरणी या सरणी तत्वों को अनबॉक्स करने में सक्षम नहीं हो सकता है, हालांकि, चूंकि वे सॉर्ट करने के लिए पारित हो रहे हैं (), जो कि एक बाहरी फ़ंक्शन है और उन तर्कों को प्राप्त करना है जो इसकी अपेक्षा कर रहे हैं। यह अपेक्षाकृत अच्छे संकलक के लिए मायने नहीं रखता। मेटाडेटा को पास करना (सूचक में - 64 बिट्स वास्तविक डेटा के बारे में बहुत सारे लेवी की पेशकश करते हैं) और इसे फ़ंक्शन में कहा जाता है।
बेस्ट

3
क्या वास्तव में -Ofast"पूरी तरह से असुरक्षित" बनाता है ? यह मानते हुए कि आप अपने कोड का परीक्षण करना जानते हैं और ओवरफ्लो पर शासन करना चाहते हैं।
जोसेफ मार्क

@ एसजेओएचपी: यह वास्तव में बहुत कुछ मान रहा है :-) कोड की जाँच करना और ओवरफ्लो को सत्तारूढ़ करना कठिन है। मेरे अनुभव से (मैं संकलक काम करते हैं और कुछ बड़े codebases जाँच की है), और क्या मैं लोग हैं, जो विशाल कंपनियों पर संकलक काम करते हैं, हो रही अतिप्रवाह और अन्य अपरिभाषित व्यवहार सही से सुना है मुश्किल । यूबी को ठीक करने पर भी ऐपल की सलाह (सिर्फ एक उदाहरण) गलत है, कभी-कभी ( randomascii.wordpress.com/2014/04/17/… )। -Ofastभाषा के शब्दार्थ को भी बदलता है, लेकिन मैं इसके लिए किसी भी डॉक्स को निधि नहीं दे सकता। आप कैसे आश्वस्त हो सकते हैं कि आप जानते हैं कि यह क्या कर रहा है?
filabab

@bestsss: यह संभव है, लेकिन यह उपयोगी नहीं हो सकता है। यह एक Int [] के लिए हर एक्सेस पर चेक जोड़ता है। यह निर्भर करता है कि क्या इंट और कुछ अन्य आदिम प्रकार (आपके पास, कम से कम, 3 बिट्स) के सरणियों का उपयोग बहुत किया जाता है (विशेषकर तब जब आप सी को कम कर सकते हैं यदि आपको आवश्यकता है)। यह कुछ बिट्स का भी उपयोग करता है जिन्हें वे उपयोग करना चाहते हैं यदि, अंततः, वे गैर-एआरसी जीसी जोड़ना चाहते हैं। यह एक से अधिक तर्क वाले जेनरिक के पैमाने पर नहीं है। चूंकि उनके पास सभी प्रकार हैं, इसलिए अंतर्विष्ट इंट का उपयोग करने के लिए Int [] (लेकिन Int नहीं? []] को छूने वाले सभी कोड को विशेषज्ञ करना बहुत आसान होगा। लेकिन तब आप के बारे में चिंता करने के लिए ओब्ज-सी इंटरॉप है।
फिल्काब

@ एफिलकैब, नॉन-एआरसी (यानी वास्तविक) जीसी वास्तव में उपयोगी होगा, लेकिन उन्हें कुछ ऐसी चीज की आवश्यकता है जो सी संगत नहीं है यदि वे वास्तव में समवर्ती, गैर-एसटीडब्ल्यू जीसी चाहते हैं। मैं 'हर एक्सेस' के बारे में चिंता नहीं करूंगा Int[]क्योंकि यह कंपाइलर इनलाइन के स्तर पर निर्भर करता है और इसे कुछ दिशानिर्देशों के बाद / साथ तंग छोरों को इनलाइन करने में सक्षम होना चाहिए।
सर्वश्रेष्ठ

53

मैंने मज़े के लिए इस पर एक नज़र रखने का फैसला किया, और यहाँ मुझे जो समय मिलता है:

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

लगता है कि Swift 2.0 से Swift 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

स्विफ्ट 4 प्रदर्शन को फिर से सुधारता है, जबकि -Oऔर के बीच एक अंतर बनाए रखता है -Ounchecked-O -whole-module-optimizationफर्क करने के लिए दिखाई नहीं दिया।

सी ++

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

परिणाम:

Apple Clang 6.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

Apple Clang 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

Apple 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

Apple Clang 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

Apple 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

निर्णय

इस लेखन के समय तक, स्विफ्ट का क्रम तेज़ है, लेकिन अभी तक उतने तेज़ नहीं है जितना C ++ का सॉर्ट है, जब -Oऊपर संकलक और पुस्तकालयों के साथ संकलित किया गया है। इसके साथ -Ounchecked, यह स्विफ्ट 4.0.2 और Apple LLVM 9.0.0 में C ++ जितना तेज़ दिखाई देता है।


2
वास्तव में आपको दस लाख तत्वों को सम्मिलित करने से पहले कभी भी वेक्टर :: रिजर्व () नहीं कहना चाहिए ।
BJovke

शायद! फिलहाल केवल छंटनी की जा रही है।
OpenGL ES

34

से The Swift Programming Language:

सॉर्ट फ़ंक्शन स्विफ्ट की मानक लाइब्रेरी सॉर्ट नामक एक फ़ंक्शन प्रदान करती है, जो आपके द्वारा प्रदान किए गए सॉर्टिंग क्लोजर के आउटपुट के आधार पर एक ज्ञात प्रकार के मूल्यों की एक सरणी को सॉर्ट करती है। एक बार जब यह छंटनी की प्रक्रिया पूरी कर लेता है, तो सॉर्ट फ़ंक्शन सही प्रकार से क्रम में अपने तत्वों के साथ पुराने प्रकार के समान आकार और आकार का एक नया सरणी देता है।

sortसमारोह दो घोषणाओं है।

डिफ़ॉल्ट घोषणा जो आपको एक तुलना बंद करने की अनुमति देती है:

func sort<T>(array: T[], pred: (T, T) -> Bool) -> T[]

और एक दूसरी घोषणा जो केवल एक पैरामीटर (सरणी) लेती है और "तुलनात्मक रूप से कम-से-कम उपयोग करने के लिए हार्डकोड" है।

func sort<T : Comparable>(array: T[]) -> T[]

Example:
sort( _arrayToSort_ ) { $0 > $1 }

मैंने अपने कोड के एक संशोधित संस्करण को खेल के मैदान में बंद किए गए खेल के साथ जोड़ा, ताकि मैं फ़ंक्शन को थोड़ा और बारीकी से देख सकूं, और मैंने पाया कि n सेट के साथ 1000, क्लोजर को लगभग 11,000 बार कॉल किया जा रहा था।

let n = 1000
let x = Int[](count: n, repeatedValue: 0)
for i in 0..n {
    x[i] = random()
}
let y = sort(x) { $0 > $1 }

यह एक कुशल कार्य नहीं है, मैं एक बेहतर छँटाई समारोह कार्यान्वयन का उपयोग करने की सलाह दूंगा।

संपादित करें:

मैंने क्विकसॉर्ट विकिपीडिया पृष्ठ पर एक नज़र डाली और इसके लिए एक स्विफ्ट कार्यान्वयन लिखा। यहाँ पूरा कार्यक्रम है जो मैंने इस्तेमाल किया है (एक खेल के मैदान में)

import Foundation

func quickSort(inout array: Int[], begin: Int, end: Int) {
    if (begin < end) {
        let p = partition(&array, begin, end)
        quickSort(&array, begin, p - 1)
        quickSort(&array, p + 1, end)
    }
}

func partition(inout array: Int[], left: Int, right: Int) -> Int {
    let numElements = right - left + 1
    let pivotIndex = left + numElements / 2
    let pivotValue = array[pivotIndex]
    swap(&array[pivotIndex], &array[right])
    var storeIndex = left
    for i in left..right {
        let a = 1 // <- Used to see how many comparisons are made
        if array[i] <= pivotValue {
            swap(&array[i], &array[storeIndex])
            storeIndex++
        }
    }
    swap(&array[storeIndex], &array[right]) // Move pivot to its final place
    return storeIndex
}

let n = 1000
var x = Int[](count: n, repeatedValue: 0)
for i in 0..n {
    x[i] = Int(arc4random())
}

quickSort(&x, 0, x.count - 1) // <- Does the sorting

for i in 0..n {
    x[i] // <- Used by the playground to display the results
}

N = 1000 के साथ इसका उपयोग करते हुए, मैंने पाया कि

  1. quickSort () को लगभग 650 बार कॉल किया गया,
  2. लगभग 6000 स्वैप बनाए गए,
  3. और लगभग 10,000 तुलनाएँ हैं

ऐसा लगता है कि अंतर्निहित सॉर्ट विधि त्वरित सॉर्ट करने के लिए (या करीब है), और वास्तव में धीमी है ...


17
शायद मैं पूरी तरह से गलत हूं, लेकिन en.wikipedia.org/wiki/Quicksort के अनुसार क्विकॉर्ट में तुलनाओं की औसत संख्या है 2*n*log(n)। यह n = 1000 तत्वों को छांटने के लिए 13815 तुलना है, इसलिए यदि तुलनात्मक फ़ंक्शन को लगभग 11000 बार कहा जाता है जो इतना बुरा नहीं लगता है।
मार्टिन आर

6
साथ ही Apple ने दावा किया कि पायथन की तुलना में स्विफ्ट में "जटिल ऑब्जेक्ट सॉर्ट" (जो भी हो) 3.9 गुना तेज है। इसलिए "बेहतर सॉर्टिंग फ़ंक्शन" को खोजने के लिए आवश्यक नहीं होना चाहिए। - लेकिन स्विफ्ट अभी भी विकास में है ...
मार्टिन आर

6
यह प्राकृतिक लघुगणक का उल्लेख करता है
मार्टिन आर

24
log(n)एल्गोरिथम जटिलता के लिए पारंपरिक रूप से लॉग -2 बेस को संदर्भित करता है। आधार न बताने का कारण यह है कि लॉगरिदम के लिए परिवर्तन का आधार कानून केवल एक निरंतर गुणक का परिचय देता है, जिसे ओ-नोटेशन के प्रयोजनों के लिए छोड़ दिया जाता है।
minuteman3

3
प्राकृतिक लघुगणक बनाम आधार 2 लघुगणक के बारे में चर्चा के बारे में: विकिपीडिया पृष्ठ से सटीक कथन यह है कि एन तत्वों के लिए आवश्यक तुलना की औसत संख्या है C(n) = 2n ln n ≈ 1.39n log₂ n। एन = 1000 के लिए इस सी (एन) = 13815 देता है, और यह है नहीं एक "बड़ा-ओ संकेत"।
मार्टिन आर

18

Xcode 7 के रूप में आप चालू कर सकते हैं Fast, Whole Module Optimization। इससे आपका प्रदर्शन तुरंत बढ़ जाना चाहिए।

यहां छवि विवरण दर्ज करें


12

स्विफ्ट एरे का प्रदर्शन फिर से शुरू:

मैंने अपने स्वयं के बेंचमार्क की तुलना स्विफ्ट की सी / ऑब्जेक्टिव-सी के साथ की। मेरा बेंचमार्क प्रमुख संख्याओं की गणना करता है। यह प्रत्येक नए उम्मीदवार में प्रमुख कारकों को देखने के लिए पिछली अभाज्य संख्याओं के सरणी का उपयोग करता है, इसलिए यह काफी तेज है। हालाँकि, यह सरणी पढ़ने के TONS, और सरणियों के लिए कम लेखन करता है।

मैंने मूल रूप से स्विफ्ट 1.2 के खिलाफ यह बेंचमार्क किया था। मैंने प्रोजेक्ट को अपडेट करने और स्विफ्ट 2.0 के खिलाफ इसे चलाने का फैसला किया।

प्रोजेक्ट आपको सामान्य स्विफ्ट सरणियों का उपयोग करने और सरणी सेमेंटिक्स का उपयोग करके स्विफ्ट असुरक्षित मेमोरी बफ़र्स का उपयोग करने के बीच चयन करने देता है।

C / Objective-C के लिए, आप NSArrays, या C Malloc'ed सरणियों का उपयोग करने का विकल्प चुन सकते हैं।

परीक्षा परिणाम सबसे तेज़, सबसे छोटे कोड अनुकूलन ([-0s]) या सबसे तेज़, आक्रामक ([-0fast]) अनुकूलन के साथ समान प्रतीत होते हैं।

स्विफ्ट 2.0 का प्रदर्शन अभी भी कोड ऑप्टिमाइज़ेशन बंद होने के साथ भयानक है, जबकि सी / ऑब्जेक्टिव-सी प्रदर्शन केवल मामूली धीमा है।

लब्बोलुआब यह है कि सी मॉलॉक सरणी सरणी आधारित गणना एक मामूली मार्जिन द्वारा सबसे तेज है

असुरक्षित बफ़र्स के साथ स्विफ्ट, सबसे तेज़, सबसे छोटे कोड ऑप्टिमाइज़ेशन का उपयोग करते समय C Malloc'd सरणियों की तुलना में लगभग 1.19X - 1.20X लंबा होता है। अंतर तेज, आक्रामक अनुकूलन के साथ थोड़ा कम लगता है (स्विफ्ट सी की तुलना में 1.18x से 1.16 गुना अधिक लेता है।

यदि आप नियमित स्विफ्ट सरणियों का उपयोग करते हैं, तो सी के साथ अंतर थोड़ा है अधिक है। (स्विफ्ट को ~ 1.22 से 1.23 लंबा समय लगता है।)

नियमित स्विफ्ट सरणियाँ हैं DRAMATICALLY स्विफ्ट 1.2 / एक्सकोड 6. में तेजी से होती उनका प्रदर्शन स्विफ्ट असुरक्षित बफर आधारित सरणियों के इतना करीब है कि असुरक्षित मेमोरी बफ़र्स का उपयोग करना वास्तव में किसी भी अधिक परेशानी के लायक नहीं लगता है, जो कि बड़ा है।

BTW, उद्देश्य-सी NSArray प्रदर्शन बदबू आ रही है। यदि आप दोनों भाषाओं में देशी कंटेनर वस्तुओं का उपयोग करने जा रहे हैं, तो स्विफ्ट नाटकीय रूप से तेज है।

आप स्विफ्टपिरफॉर्मेंसबेन्चमार्क में जीथब पर मेरी परियोजना की जांच कर सकते हैं

इसमें एक साधारण यूआई है जो आंकड़े एकत्र करना बहुत आसान बनाता है।

यह दिलचस्प है कि अब स्विफ्ट सी की तुलना में स्विफ्ट में थोड़ा तेज है, लेकिन यह है कि यह प्राइम नंबर एल्गोरिथ्म स्विफ्ट में अभी भी तेज है।


8

मुख्य मुद्दा जो दूसरों द्वारा उल्लिखित है, लेकिन पर्याप्त रूप से बाहर नहीं बुलाया गया है, वह -O3स्विफ्ट (और कभी नहीं) में कुछ भी नहीं करता है, इसलिए जब इसके साथ संकलित किया जाता है तो यह प्रभावी रूप से गैर-अनुकूलित ( -Onone) है।

विकल्प के नाम समय के साथ बदल गए हैं इसलिए कुछ अन्य उत्तरों में बिल्ड विकल्पों के लिए अप्रचलित झंडे हैं। सही वर्तमान विकल्प (स्विफ्ट 2.2) हैं:

-Onone // Debug - slow
-O     // Optimised
-O -whole-module-optimization //Optimised across files

पूरे मॉड्यूल ऑप्टिमाइज़ेशन का एक धीमा संकलन है लेकिन मॉड्यूल के भीतर या प्रत्येक फ्रेम के भीतर और वास्तविक एप्लिकेशन कोड के भीतर फ़ाइलों के बीच अनुकूलन कर सकते हैं लेकिन उनके बीच नहीं। आपको इसे किसी भी प्रदर्शन के लिए महत्वपूर्ण उपयोग करना चाहिए)

आप सुरक्षा जांच को और भी अधिक गति के लिए अक्षम कर सकते हैं लेकिन सभी दावे और पूर्व शर्त के साथ न केवल अक्षम बल्कि इस आधार पर अनुकूलित किया जाता है कि वे सही हैं। यदि आप कभी जोर मारते हैं तो इसका मतलब है कि आप अपरिभाषित व्यवहार में हैं। अत्यधिक सावधानी के साथ उपयोग करें और केवल तभी जब आप यह निर्धारित करते हैं कि गति में वृद्धि आपके लिए सार्थक है (परीक्षण करके)। यदि आपको यह कुछ कोड के लिए मूल्यवान लगता है, तो मैं उस कोड को एक अलग फ्रेमवर्क में अलग करने की सलाह देता हूं और केवल उस मॉड्यूल के लिए सुरक्षा जांच को अक्षम करता हूं।


यह उत्तर अब पुराना हो चुका है। स्विफ्ट 4.1 के रूप में पूरे मॉड्यूल ऑप्टिमाइज़ेशन विकल्प एक अलग बूलियन है जिसे अन्य सेटिंग्स के साथ जोड़ा जा सकता है और आकार के लिए अनुकूलन करने के लिए अब -ओ है। जब मेरे पास सटीक विकल्प झंडे की जांच करने का समय हो तो मैं अपडेट कर सकता हूं।
जोसफ लॉर्ड

7
func partition(inout list : [Int], low: Int, high : Int) -> Int {
    let pivot = list[high]
    var j = low
    var i = j - 1
    while j < high {
        if list[j] <= pivot{
            i += 1
            (list[i], list[j]) = (list[j], list[i])
        }
        j += 1
    }
    (list[i+1], list[high]) = (list[high], list[i+1])
    return i+1
}

func quikcSort(inout list : [Int] , low : Int , high : Int) {

    if low < high {
        let pIndex = partition(&list, low: low, high: high)
        quikcSort(&list, low: low, high: pIndex-1)
        quikcSort(&list, low: pIndex + 1, high: high)
    }
}

var list = [7,3,15,10,0,8,2,4]
quikcSort(&list, low: 0, high: list.count-1)

var list2 = [ 10, 0, 3, 9, 2, 14, 26, 27, 1, 5, 8, -1, 8 ]
quikcSort(&list2, low: 0, high: list2.count-1)

var list3 = [1,3,9,8,2,7,5]
quikcSort(&list3, low: 0, high: list3.count-1) 

क्विक सॉर्ट- जीथब नमूना क्विक-सॉर्ट के बारे में यह मेरा ब्लॉग है

आप सूची विभाजन में लोमुटो के विभाजन एल्गोरिथ्म पर एक नज़र डाल सकते हैं। स्विफ्ट में लिखा है।


4

स्विफ्ट 4.1 नए -Osizeअनुकूलन मोड का परिचय देता है।

स्विफ्ट 4.1 में कंपाइलर अब एक नए अनुकूलन मोड का समर्थन करता है जो कोड आकार को कम करने के लिए समर्पित अनुकूलन को सक्षम करता है।

स्विफ्ट कंपाइलर शक्तिशाली अनुकूलन के साथ आता है। -O के साथ संकलित करते समय संकलक कोड को बदलने की कोशिश करता है ताकि यह अधिकतम प्रदर्शन के साथ निष्पादित हो। हालाँकि, रनटाइम प्रदर्शन में यह सुधार कभी-कभी बढ़े हुए कोड आकार के ट्रेडऑफ़ के साथ आ सकता है। नए -Oize ऑप्टिमाइज़ेशन मोड के साथ उपयोगकर्ता के पास अधिकतम गति के बजाय न्यूनतम कोड आकार के लिए संकलन करने का विकल्प है।

कमांड लाइन पर आकार अनुकूलन मोड को सक्षम करने के लिए, -O के बजाय -Oize का उपयोग करें।

आगे पढ़े: https://swift.org/blog/osize/

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.