मैं स्विफ्ट बीटा में एक एल्गोरिथ्म लागू कर रहा था और देखा कि प्रदर्शन बहुत खराब था। गहरी खुदाई के बाद मुझे महसूस हुआ कि अड़चनों में से एक छँटाई सरणियों की तरह सरल थी। प्रासंगिक हिस्सा यहाँ है:
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
समान रूप से अच्छा कोड पैदा करता है।
xcrun --sdk macosx swift -O3
। यह छोटा है।