2012-09-28 6 views
50

मान लें कि हम प्रदर्शन निगरानी के लिए टीएससी का उपयोग करने की कोशिश कर रहे हैं और हम निर्देश पुनर्निर्माण को रोकना चाहते हैं।rdtscp, rdtsc के बीच अंतर: स्मृति और cpuid/rdtsc?

1:rdtscp एक serializing कॉल है

ये हमारे विकल्प हैं। यह rdtscp पर कॉल के चारों ओर पुनरावृत्ति रोकता है।

__asm__ __volatile__("rdtscp; "   // serializing read of tsc 
        "shl $32,%%rdx; " // shift higher 32 bits stored in rdx up 
        "or %%rdx,%%rax" // and or onto rax 
        : "=a"(tsc)  // output to tsc variable 
        : 
        : "%rcx", "%rdx"); // rcx and rdx are clobbered 

हालांकि, rdtscp नए CPUs पर ही उपलब्ध है। तो इस मामले में हमें rdtsc का उपयोग करना होगा। लेकिन rdtsc गैर-क्रमबद्ध है, इसलिए अकेले इसका उपयोग करने से सीपीयू इसे पुन: व्यवस्थित करने से नहीं रोकेगा।

2:

तो हम पुनर्व्यवस्था को रोकने के लिए इन दो विकल्पों में से किसी का उपयोग कर सकते यह cpuid और फिर rdtsc के लिए एक कॉल है। cpuid एक धारावाहिक कॉल है।

volatile int dont_remove __attribute__((unused)); // volatile to stop optimizing 
unsigned tmp; 
__cpuid(0, tmp, tmp, tmp, tmp);     // cpuid is a serialising call 
dont_remove = tmp;        // prevent optimizing out cpuid 

__asm__ __volatile__("rdtsc; "   // read of tsc 
        "shl $32,%%rdx; " // shift higher 32 bits stored in rdx up 
        "or %%rdx,%%rax" // and or onto rax 
        : "=a"(tsc)  // output to tsc 
        : 
        : "%rcx", "%rdx"); // rcx and rdx are clobbered 

3: इस मार-पीट की सूची में memory साथ rdtsc के लिए एक कॉल, जो पुन: क्रम रोकता है

__asm__ __volatile__("rdtsc; "   // read of tsc 
        "shl $32,%%rdx; " // shift higher 32 bits stored in rdx up 
        "or %%rdx,%%rax" // and or onto rax 
        : "=a"(tsc)  // output to tsc 
        : 
        : "%rcx", "%rdx", "memory"); // rcx and rdx are clobbered 
                // memory to prevent reordering 

3 विकल्प के लिए मेरे समझ इस प्रकार है:

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

बताएं कि कंपाइलर मेमोरी को गिरफ्तार किया जा रहा है: : "memory")"memory" क्लॉबर का अर्थ है कि जीसीसी एएमएम में समान स्मृति सामग्री के बारे में कोई धारणा नहीं कर सकता है, और इस प्रकार इसके आसपास पुन: व्यवस्थित नहीं होगा।

तो मेरी प्रश्न हैं:

  • 1: __volatile__ और "memory" सही की मेरी समझ है?
  • 2: क्या दूसरी दो कॉल एक ही काम करते हैं?
  • 3: "memory" का उपयोग करना एक और धारावाहिक निर्देश का उपयोग करने से कहीं अधिक सरल दिखता है। दूसरे विकल्प पर तीसरे विकल्प का उपयोग क्यों करेंगे?
+9

आप कंपाइलर द्वारा उत्पन्न निर्देशों की पुनरावृत्ति को भ्रमित करने लगते हैं, जिसे आप 'अस्थिर' और 'मेमोरी' का उपयोग करके और प्रोसेसर द्वारा निष्पादित निर्देशों की पुनरावृत्ति (आदेश निष्पादन_ का उर्फ ​​_out) का उपयोग करके टालना कर सकते हैं, जिसे आप ' cpuid'। – hirschhornsalz

+0

@hirschhornsalz लेकिन क्लॉबर सूची में 'मेमोरी' नहीं होगी प्रोसेसर को निर्देशों को फिर से चलाने से रोकें? मेमोरी बाड़ की तरह 'स्मृति' कार्य नहीं करता है? –

+0

या शायद क्लॉबर सूची में 'मेमोरी' केवल जीसीसी को उत्सर्जित है, और परिणामस्वरूप मशीन कोड प्रोसेसर को इसका खुलासा नहीं करता है? –

उत्तर

35

एक टिप्पणी में उल्लेख किया है, वहाँ एक संकलक बाधा और एक प्रोसेसर बाधा के बीच एक अंतर है। एएसएम कथन अधिनियम में संकलक बाधा के रूप में volatile और memory, लेकिन प्रोसेसर अभी भी निर्देशों को पुन: व्यवस्थित करने के लिए स्वतंत्र है।

प्रोसेसर बाधा विशेष निर्देश हैं जिन्हें स्पष्ट रूप से दिया जाना चाहिए, उदा। rdtscp, cpuid, मेमोरी बाड़ निर्देश (mfence, lfence, ...) आदि

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

लिनक्स कर्नेल एएमडी प्लेटफॉर्म पर mfence;rdtsc और इंटेल पर lfence;rdtsc का उपयोग करता है। यदि आप इनके बीच अंतर करने के साथ परेशान नहीं करना चाहते हैं, तो mfence;rdtsc दोनों पर काम करता है हालांकि यह mfence के रूप में थोड़ा धीमा है lfence से अधिक मजबूत बाधा है।

+5

'cpuid; rdtsc' मेमोरी बाड़ के बारे में नहीं है, यह निर्देश धारा क्रमबद्ध करने के बारे में है। आमतौर पर इसका उपयोग यह सुनिश्चित करने के लिए बेंचमार्किंग उद्देश्यों के लिए किया जाता है कि रीडर बफर/आरक्षण स्टेशन में कोई "पुराना" निर्देश न रहे। 'Cpuid' का निष्पादन समय (जो काफी लंबा है, मुझे याद है> 200 चक्र) तब घटाया जाना चाहिए। यदि परिणाम अधिक "सटीक" है, तो यह मेरे लिए बिल्कुल स्पष्ट नहीं है, मैंने प्रयोग किया है और बिना और अंतर भिन्नता की प्राकृतिक त्रुटि को कम करता है, यहां तक ​​कि एकल उपयोगकर्ता मोड में भी कुछ भी नहीं चल रहा है। – hirschhornsalz

+0

मुझे यकीन नहीं है, लेकिन संभवतया मैं कर्नेल में इस तरह से उपयोग की जाने वाली बाड़ निर्देश सभी ^^ – hirschhornsalz

+4

@hirschhornsalz पर उपयोगी नहीं है: गिट प्रतिबद्ध लॉग के अनुसार, एएमडी और इंटेल ने पुष्टि की है कि एम/लेंस वर्तमान में rdtsc को क्रमबद्ध करेगा उपलब्ध सीपीयू। मुझे लगता है कि यदि आप रुचि रखते हैं और उससे पूछें तो एंडी क्लेन वास्तव में क्या कहा गया था, इसके बारे में अधिक जानकारी प्रदान कर सकते हैं। – janneb

5

आप इसे नीचे दिखाया गया है पसंद का उपयोग कर सकते हैं:

asm volatile (
"CPUID\n\t"/*serialize*/ 
"RDTSC\n\t"/*read the clock*/ 
"mov %%edx, %0\n\t" 
"mov %%eax, %1\n\t": "=r" (cycles_high), "=r" 
(cycles_low):: "%rax", "%rbx", "%rcx", "%rdx"); 
/* 
Call the function to benchmark 
*/ 
asm volatile (
"RDTSCP\n\t"/*read the clock*/ 
"mov %%edx, %0\n\t" 
"mov %%eax, %1\n\t" 
"CPUID\n\t": "=r" (cycles_high1), "=r" 
(cycles_low1):: "%rax", "%rbx", "%rcx", "%rdx"); 

उपरोक्त कोड में, पहले CPUID कॉल एक बाधा को लागू करता है के ऊपर और नीचे RDTSC अनुदेश निर्देश के बाहर के आदेश निष्पादन से बचने के लिए। इस विधि के साथ हम रीयल-टाइम रजिस्ट्रार

पहले आरडीटीएससी टाइमस्टैम्प रजिस्टर को पढ़ते हैं और मान मेमोरी में संग्रहीत किया जाता है। फिर जिस कोड को हम मापना चाहते हैं उसे निष्पादित किया जाता है। आरडीटीएससीपी निर्देश दूसरी बार टाइमस्टैंप रजिस्टर पढ़ता है और गारंटी देता है कि हम जिस कोड को मापना चाहते हैं उसका निष्पादन पूरा हो गया है। बाद में आने वाले दो "mov" निर्देश edx और eax रजिस्टरों के मान स्मृति में संग्रहीत करते हैं। अंततः एक सीपीयूआईडी कॉल गारंटी देता है कि एक बाधा फिर से कार्यान्वित की जाती है ताकि यह असंभव हो कि बाद में आने वाले किसी भी निर्देश को CPUID से पहले निष्पादित किया जाता है।

+12

हाय, ऐसा प्रतीत होता है कि आपने गैब्रिएल पाओलिनिस श्वेत पत्र "इंटेल® आईए -32 और आईए -64 निर्देश सेट आर्किटेक्चर पर कोड निष्पादन टाइम्स कैसे बेंचमार्क करें" से इस जवाब की प्रतिलिपि बनाई है (हालांकि आप एक लाइन ब्रेक चूक गए हैं)। आप लेखक क्रेडिट देने के बिना किसी और के काम का उपयोग कर रहे हैं। एक विशेषता क्यों नहीं जोड़ें? –

+0

हां, वास्तव में, यह मुकाबला है। मैं यह भी सोच रहा हूं कि प्रारंभिक समय पढ़ने में दो mov आवश्यक हैं: http://stackoverflow.com/questions/38994549/is-intels-timestamp-reading-asm-code-example-using-two-more-registers -थान - –

+0

क्या दो चर उच्च और निम्न होने का एक विशिष्ट कारण है? – ExOfDe

संबंधित मुद्दे