अहस्ताक्षरित और हस्ताक्षरित पूर्णांक के बीच प्रदर्शन अंतर क्या हैं? [बन्द है]


42

फ्लोट के साथ साइन इन्ट्स को मिक्स करते समय मुझे प्रदर्शन हिट की जानकारी है।

क्या फ्लोट्स के साथ अहस्ताक्षरित मिश्रणों को मिलाना बदतर है?

क्या फ्लोट के बिना हस्ताक्षरित / अहस्ताक्षरित होने पर कोई हिट है?

क्या विभिन्न आकारों (u32, u16, u8, i32, i16, i8) के प्रदर्शन पर कोई प्रभाव पड़ता है? किन प्लेटफार्मों पर?


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

जवाबों:


36

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

विभिन्न आकारों या हस्ताक्षरित-नेस के बीच जाना सब कुछ एक ही रजिस्टर सेट में रखता है, इसलिए आप बड़े दंड से बचते हैं। साइन-एक्सटेंशन आदि के कारण छोटे दंड हो सकते हैं, लेकिन ये लोड-हिट-स्टोर की तुलना में बहुत छोटे होते हैं।


आपके द्वारा लिंक किए गए लेख में कहा गया है कि PS3 सेल प्रोसेसर इसका एक अपवाद है क्योंकि जाहिरा तौर पर सब कुछ रजिस्टरों के एक ही सेट में संग्रहीत किया जाता है (लेख के बीच में लगभग पाया जा सकता है या "सेल" की खोज कर सकता है)।
बंमज़ैक

4
@ बम्ज़ैक: यह केवल एसपीई पर लागू होता है, पीपीई पर नहीं; एसपीईएस में एक बहुत, उह, विशेष, फ्लोटिंग पॉइंट वातावरण होता है, और कास्ट अभी भी अपेक्षाकृत महंगा है। इसके अलावा, हस्ताक्षरित बनाम अहस्ताक्षरित पूर्णांकों के लिए लागतें अभी भी समान हैं।

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

1
@ उपर - मैं इस पर कुछ सार्वजनिक दस्तावेज खोजने की कोशिश कर रहा था, लेकिन फिलहाल इसे नहीं पा रहा हूं। यदि आपके पास Xbox360 प्रलेखन तक पहुंच है, तो ब्रूस डॉसन द्वारा एक अच्छा श्वेतपत्र है जो इस में से कुछ को कवर करता है (और सामान्य रूप से यह बहुत अच्छा है)।
celion

@ उपसर्ग: मैंने नीचे एक विश्लेषण पोस्ट किया है, लेकिन अगर यह आपको संतुष्ट करता है, तो कृपया सीलिएक को जवाब दें - उसने जो कुछ भी कहा वह सही है, मैंने जो कुछ किया है वह जीसीसी द्वारा कुछ बार चलाया जाता है।

12

मुझे संदेह है कि Xbox 360 और PS3 के बारे में जानकारी विशेष रूप से लाइसेंस-डेवलपर-केवल दीवारों के पीछे होने वाली है, जैसे अधिकांश निम्न-स्तरीय विवरण। हालांकि, हम एक समान x86 प्रोग्राम का निर्माण कर सकते हैं और इसे एक सामान्य विचार प्राप्त करने के लिए अलग कर सकते हैं।

सबसे पहले, आइए देखते हैं कि क्या बिना लागत वाली चौड़ीकरण लागत है:

unsigned char x = 1;
unsigned int y = 1;
unsigned int z;
z = x;
z = y;

संबंधित भाग (GCC 4.4.5 का उपयोग करते हुए)

    z = x;
  27:   0f b6 45 ff             movzbl -0x1(%ebp),%eax
  2b:   89 45 f4                mov    %eax,-0xc(%ebp)
    z = y;
  2e:   8b 45 f8                mov    -0x8(%ebp),%eax
  31:   89 45 f4                mov    %eax,-0xc(%ebp)

तो मूल रूप से एक ही - एक मामले में हम एक बाइट को स्थानांतरित करते हैं, दूसरे में हम एक शब्द को स्थानांतरित करते हैं। आगे:

signed char x = 1;
signed int y = 1;
signed int z;
z = x;
z = y;

में बदल जाता है:

   z = x;
  11:   0f be 45 ff             movsbl -0x1(%ebp),%eax
  15:   89 45 f4                mov    %eax,-0xc(%ebp)
    z = y;
  18:   8b 45 f8                mov    -0x8(%ebp),%eax
  1b:   89 45 f4                mov    %eax,-0xc(%ebp)

इसलिए साइन एक्सटेंशन की लागत जो भी है, उसके movsblबजाय लागत movzbl- उप-अनुदेश स्तर है। जिस तरह से आधुनिक प्रोसेसर काम करते हैं, उसके कारण आधुनिक प्रोसेसर पर मात्रा निर्धारित करना असंभव है। बाकी सब कुछ, मेमोरी स्पीड से लेकर कैशिंग तक जो पहले से पाइपलाइन में थी, रनटाइम पर हावी होने वाली है।

~ 10 मिनट में मुझे इन परीक्षणों को लिखने में लग गया, मुझे आसानी से एक वास्तविक प्रदर्शन बग मिल सकता था, और जैसे ही मैं कंपाइलर ऑप्टिमाइज़ेशन के किसी भी स्तर को चालू करता हूं, कोड ऐसे सरल कार्यों के लिए अपरिचित हो जाता है।

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


2
(CW क्योंकि यह वास्तव में celion के उत्तर पर एक टिप्पणी है, और क्योंकि मैं उत्सुक हूं कि कोड परिवर्तन लोगों को इसे और अधिक

PS3 CPU की जानकारी आसानी से और कानूनी रूप से उपलब्ध है, इसलिए PS3 से संबंधित CPU सामान की चर्चा कोई समस्या नहीं है। जब तक सोनी ने अन्यओएसओएस समर्थन को हटा दिया, तब तक कोई भी PS3 पर लिनक्स को चिपका सकता है और इसे प्रोग्राम कर सकता है। GPU सीमा से बाहर था, लेकिन CPU (SPEs सहित) ठीक हैं। अन्यओएसओएस समर्थन के बिना भी आप आसानी से उपयुक्त जीसीसी को पकड़ सकते हैं और देख सकते हैं कि कोड-जीन क्या है।
जेसन

@ जेसन: मैंने सीडब्ल्यू के रूप में अपनी पोस्ट को हरी झंडी दिखाई है, अगर कोई ऐसा करता है तो वे जानकारी प्रदान कर सकते हैं। हालांकि, सोनी के आधिकारिक गेमओएस कंपाइलर तक पहुंच रखने वाला कोई भी व्यक्ति - जो वास्तव में एकमात्र ऐसा है जो मायने रखता है - शायद ऐसा करने से रोक दिया गया है।

वास्तव में हस्ताक्षरित पूर्णांक PPC IIRC पर अधिक महंगा है। यह एक छोटे से प्रदर्शन हिट है, लेकिन यह वहाँ है ... PS3 PPU / SPU विवरण का एक बहुत यहाँ हैं: jheriko-rtw.blogspot.co.uk/2011/07/ps3-ppuspu-docs.html और यहाँ: jheriko-rtw.blogspot.co.uk/2011/03/ppc-instruction-set.html । जिज्ञासु यह GameOS संकलक हालांकि क्या है? क्या वह जीसीसी कम्पेयर या एसएनसी एक है? जब पहले से ही हस्ताक्षर किए गए तुलनाओं का उल्लेख किया गया है, तो इसके अलावा, जब अंतरतम छोरों को अनुकूलित करने के बारे में बात की जाती है, तो एक ओवरहेड होता है। मेरा यह वर्णन करते हुए डॉक्स तक पहुँच नहीं है - और भले ही मैंने किया हो ...
jheriko

4

लगभग सभी आर्किटेक्चर पर हस्ताक्षर किए गए पूर्णांक ऑपरेशन अधिक महंगे हो सकते हैं। उदाहरण के लिए, अहस्ताक्षरित होने पर एक स्थिर से विभाजन तेज होता है, जैसे:

unsigned foo(unsigned a) { return a / 1024U; }

के लिए अनुकूलित किया जा रहा है:

unsigned foo(unsigned a) { return a >> 10; }

परंतु...

int foo(int a) { return a / 1024; }

के लिए अनुकूलित करेंगे:

int foo(int a) {
  return (a + 1023 * (a < 0)) >> 10;
}

या उन प्रणालियों पर जहां ब्रांचिंग सस्ती है,

int foo(int a) {
  if (a >= 0) return a >> 10;
  else return (a + 1023) >> 10;
}

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

सामान्य तौर पर, संकलक को यह बताना कि नकारात्मक संख्याएँ परिणाम का अनुकूलन नहीं कर सकती हैं, विशेष रूप से लूप समाप्ति और अन्य सशर्तों के लिए उपयोग किए जाने वाले।

विभिन्न आकार के ints के लिए, हाँ थोड़ा सा प्रभाव है, लेकिन आपको उस बनाम को कम स्मृति के आसपास घूमना होगा। इन दिनों आप संभवतः कम मेमोरी तक पहुँचने से अधिक प्राप्त करते हैं, क्योंकि आप आकार के विस्तार से कम हो जाते हैं। आप उस बिंदु पर सूक्ष्म अनुकूलन में बहुत दूर हैं।


मैंने आपके अनुकूलित कोड को और अधिक प्रतिबिंबित होने के लिए संपादित किया जो कि जीसीसी वास्तव में उत्पन्न करता है, यहां तक ​​कि -0 पर भी। एक शाखा होने पर भ्रामक था जब एक परीक्षण + पत्र आपको शाखाहीन करने देता है।

2
X86 पर, हो सकता है। ARMv7 पर इसे सिर्फ सशर्त निष्पादित किया जाता है।
जॉन रिप्ले

3

हस्ताक्षरित या अहस्ताक्षरित इंट के साथ संचालन वर्तमान प्रोसेसर (x86_64, x86, powerpc, arm) पर समान लागत है। 32 बिट्स प्रोसेसर पर, u32, u16, u8 s32, s16, s8 समान होने चाहिए। आप खराब संरेखण के साथ जुर्माना लगा सकते हैं।

लेकिन इंट को फ्लोट में बदलना या फ्लोट को इंट में बदलना एक महंगा ऑपरेशन है। आप आसानी से अनुकूलित कार्यान्वयन (SSE2, नियॉन ...) पा सकते हैं।

सबसे महत्वपूर्ण बिंदु संभवतः मेमोरी एक्सेस है। यदि आपका डेटा L1 / L2 कैश में फिट नहीं है, तो आप रूपांतरण से अधिक चक्र को ढीला कर देंगे।


2

जॉन प्यूरी ऊपर कहता है (मैं टिप्पणी नहीं कर सकता) कि अहस्ताक्षरित धीमा हो सकता है क्योंकि यह अतिप्रवाह नहीं कर सकता है। मैं असहमत हूं, अहस्ताक्षरित अंकगणित शब्द में बिट की संख्या के लिए सरल मौलर अंकगणितीय मोडुलो 2 है। सिद्धांत रूप में हस्ताक्षरित ऑपरेशन ओवरफ्लो को पीड़ित कर सकते हैं, लेकिन वे आमतौर पर बंद हो जाते हैं।

कभी-कभी आप चतुर (लेकिन बहुत पठनीय चीजें नहीं) कर सकते हैं जैसे एक इंट में दो या अधिक डेटा आइटम पैक करते हैं, और प्रति निर्देश कई ऑपरेशन प्राप्त करते हैं (पॉकेट अंकगणित)। लेकिन आप समझ सकते हैं कि आप क्या कर रहे हैं। बेशक MMX आपको स्वाभाविक रूप से ऐसा करने की अनुमति देता है। लेकिन कभी-कभी सबसे बड़ा HW समर्थित शब्द आकार का उपयोग करना और डेटा को मैन्युअल रूप से पैक करना आपको सबसे तेज़ कार्यान्वयन देता है।

डेटा संरेखण के बारे में सावधान रहें। अधिकांश HW क्रियान्वयनों पर गैर-लोड किए गए लोड और स्टोर धीमे होते हैं। प्राकृतिक संरेखण, का अर्थ है कि एक 4byte शब्द कहने के लिए, पता चार का एक बहु है, और आठ बाइट शब्द पते आठ बाइट्स के गुणक होने चाहिए। यह SSE (128bit 16byte संरेखण) का समर्थन करता है। AVX जल्द ही इन "वेक्टर" रजिस्टर साइज़ को 256bit और फिर 512bit तक बढ़ा देगा। और संरेखित भार / भंडार अनछुए लोगों की तुलना में तेज़ होंगे। HW geeks के लिए, अन-असाइन किए गए मेमोरी ऑपरेशन में कैशलाइन और यहां तक ​​कि पेज बाउंड्रीज जैसी चीजें हो सकती हैं, जिसके लिए HW के बारे में सावधान रहना होगा।


1

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

इससे परे, x86 बेहतर काम करता है यदि आप प्रकारों को नहीं मिलाते हैं, क्योंकि यह मेमोरी ऑपरेंड का उपयोग कर सकता है। यदि उसे प्रकार (संकेत या शून्य एक्सटेंशन) को बदलना है जिसका अर्थ है स्पष्ट लोड और रजिस्टर का उपयोग।

स्थानीय चर के लिए int के साथ छड़ी और इस के अधिकांश डिफ़ॉल्ट रूप से होगा।


0

जैसा कि cionion बताता है, चींटियों और फ़्लोट्स के बीच रूपांतरण के ओवरहेड को बड़े पैमाने पर रजिस्टरों के बीच मूल्यों की नकल और रूपांतरण के साथ करना पड़ता है। अपने आप में और इन-लेस अहस्ताक्षरित ओवरहेड केवल उनके गारंटीकृत रैपराउंड व्यवहार से आता है, जो संकलित कोड में एक निश्चित मात्रा में अतिप्रवाह जाँच की आवश्यकता होती है।

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

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


क्या अतिप्रवाह जाँच आप की बात कर रहे हैं? जब तक आप कोडांतरक की तुलना में कम स्तर का मतलब है, दो ints जोड़ने के लिए कोड अधिकांश प्रणालियों पर समान है, और वास्तव में कुछ पर लंबे समय तक नहीं है कि जैसे साइन-परिमाण का उपयोग करें। बिल्कुल अलग।

@JoeWreschnig: लानत है। मैं इसे खोजने के लिए प्रतीत नहीं कर सकता, लेकिन मुझे पता है कि मैंने अलग-अलग कोडांतरक आउटपुट के उदाहरणों को परिभाषित रैपपराउंड व्यवहार के लिए देखा है, कम से कम कुछ प्लेटफार्मों पर। एकमात्र संबंधित पोस्ट जो मुझे मिल सकती है: stackoverflow.com/questions/4712315/…
जॉन पूर्डी

विभिन्न आवरण व्यवहार के लिए अलग-अलग कोडांतरक आउटपुट है क्योंकि संकलक हस्ताक्षरित मामले में अनुकूलन कर सकता है, जैसे कि यदि b> 0 तो a + b> a, क्योंकि हस्ताक्षरित अतिप्रवाह अपरिभाषित है (और इस प्रकार उस पर भरोसा नहीं किया जा सकता है)। यह वास्तव में एक पूरी तरह से अलग स्थिति है।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.