2012-05-25 19 views
8

मैं एक ऐसा एप्लीकेशन विकसित कर रहा हूं जहां प्रदर्शन महत्वपूर्ण है। मैं चाहता हूं कि जीसीसी कुछ विशिष्ट कॉलों को मेमसेट() को दोहराए गए उपसर्ग के साथ निर्देश के रूप में अनुवाद करे, जैसे "रेप स्टॉस क्वॉर्ड पीटीआर एस: [आरडीआई], रैक्स"। जीसीसी यह स्वचालित रूप से करता है जब आकार दोनों ज्ञात और छोटे होते हैं।मेमसेट में दोहराना उपसर्ग का उपयोग करने के लिए फोर्स जीसीसी() कॉल

हालांकि, जीसीसी मैप्स पीएलटी के माध्यम से यादृच्छिक लंबाई के साथ यादृच्छिक लंबाई के साथ मेमसेट() को कॉल करता है, जो शाखा भविष्यवाणी कैश ठंडा होने के कारण शाखा गलतफहमी का कारण बनता है।

क्या जीसीसी को जो कुछ भी मैं चाहता हूं (इनलाइन असेंबली के बाहर) करने के लिए मजबूर करने का कोई तरीका है? ध्यान दें कि मैं इस कार्यक्रम को पूरे कार्यक्रम के लिए नहीं चाहता, केवल कुछ विशिष्ट स्मृति() कॉल के लिए।

एक संबंधित विषय पर, मैं यह भी कहा कि शाखाओं में जब एक cmovcc अनुदेश काम करना होगा से जीसीसी से बचाता है किसी भी हैक के लिए इच्छुक हूँ (& आदि का उपयोग कर, +, के बारे में मुझे पता है। के बजाय & &)।

किसी भी मदद के लिए बहुत बहुत धन्यवाद।

@FrankH:

है वह मूल रूप से मैं क्या कर समाप्त हो गया। यहाँ मेरी कोड है:

static finline void app_zero(void *dst, uint32_t size, uint32_t count) 
{ 
    // Warning: we tell gcc to use 'dst' both as source and destination here. 
    // This does not cause problems because we don't reuse 'dst'. 
    #ifdef APP_ARCH_X86 
    #define STOS(X,Y) do { \ 
     int c = (size/Y)*count; \ 
     __asm__ __volatile__("cld; xor %%eax, %%eax; rep stos"X"\n\n" \ 
          : "+D"(dst), "+c"(c) :: "rax", "flags"); \ 
     } while (0) 
    if (size % 8 == 0)  STOS("q", 8); 
    else if (size % 4 == 0) STOS("l", 4); 
    else if (size % 2 == 0) STOS("w", 2); 
    else     STOS("b", 1); 
    #undef STOS 
    #else 
    memset(dst, 0, size*count); 
    #endif 
} 

ध्यान रखें कि आपके उदाहरण अपने परीक्षण सेटअप में काम करता है, लेकिन यह आम तौर पर काम नहीं करेगा। जीसीसी दिशा ध्वज बदल सकता है, इसलिए cld निर्देश आवश्यक है। इसके अलावा, आप जीसीसी बताना होगा कि %rdi और %rcx stos अनुदेश द्वारा बदला हो जाएगा, और कि एक रजिस्टर दोनों एक इनपुट और clobbered, आप अजीब "+" सिंटैक्स का उपयोग अनिवार्य है निर्दिष्ट के बाद से जीसीसी करने के लिए आप की अनुमति नहीं होगी (जो आपके इनपुट मानों को भी दूषित करेगा)।

यह 'सीएलडी' निर्देश के कारण इष्टतम नहीं है, जिसमें नेहलेम पर 4 चक्र की विलम्ब है। जीसीसी फ्लैग रजिस्टर राज्य को आंतरिक रूप से (AFAICT) ट्रैक करता है, इसलिए इसे हर बार उस निर्देश को जारी करने की आवश्यकता नहीं होती है।

+1

एकमात्र तरीका जिसे मैं कुछ प्राप्त करने का प्रयास करने के बारे में सोच सकता हूं जो कोड के कुछ हिस्सों पर लागू होता है, वह जीसीसी के गुण (फ़ंक्शन, चर और प्रकार विशेषताओं) होगा। हालांकि, एक त्वरित नज़र में उनमें से कोई भी आप जो खोज रहे हैं उसे प्राप्त नहीं करते हैं। – zxcdw

+0

धन्यवाद, मैं पुष्टि करता हूं कि ऐसी कोई विशेषता/प्रज्ञा मौजूद नहीं है। –

उत्तर

2

मुझे जीसीसी के बारे में पता नहीं है, लेकिन एमएसवीसी के नए निर्माण के तहत, सेटिंग/प्रतिलिपि करने के लिए लूप का उपयोग करके REP STOS (और यह अभी भी ज्ञात आकार और ऑटो-वेक्टरेशन के लिए अनुकूलन की अनुमति देता है) जीसीसी के तहत प्रयास करें।

यह जांचने का विकल्प है कि क्या जीसीसी के पास __stosq के समान बनाया गया है, अन्यथा आपको शायद इनलाइन असेंबली में जाना होगा, लेकिन जीसीसी (और शायद यह सबसे सरल और सबसे तेज़ तरीका) के तहत यह बुरा नहीं है।

अपने दूसरे प्रश्न, सामान्य करने के लिए जिस तरह से वास्तव में एक अच्छा जवाब पाने के लिए है कारण यह हाथ में मामले पर निर्भर करता है, तथापि, जीसीसी काफी अच्छी तरह से विशिष्ट कोने मामलों को छोड़कर शाखाओं बाहर के अनुकूलन में क्या करना चाहिए (का उपयोग कर SETCC/MOVCC/FMOVCC)।

+0

मैंने मेमसेट() करने के लिए एक सरल पाश का उपयोग करने का प्रयास किया है। कोई भाग्य नहीं, जीसीसी अभी भी शाखाओं का उपयोग करने पर जोर देती है। अब मैं 'stos' और' movs' के आस-पास इनलाइन असेंबली रैपर का उपयोग कर रहा हूं। यह इष्टतम से बहुत दूर है क्योंकि जीसीसी ने दिशा ध्वज सेट करने के मामले में मुझे 'cld' निर्देश डालना होगा। शाखाओं को अनुकूलित करने के लिए, मेरा अनुभव यह रहा है कि जीसीसी को 'MIN' और 'MAX' जैसे साधारण मामलों के लिए भी शाखाओं का उपयोग करना पसंद है। उन मामलों के लिए इनलाइन असेंबली का उपयोग करना इष्टतम नहीं है क्योंकि यह कंपाइलर के रजिस्टर उपयोग को बाधित करता है। –

4

यदि आप इसे मजबूर करना चाहते हैं, तो इनलाइन असेंबली को एक विकल्प के रूप में क्यों बाहर रखा जाए?

#define my_forced_inline_memset(dst, c, N) \ 
    __asm__ __volatile__(     \ 
     "rep stosq %%rax, (%%rdi)\n\t" 
     : : "D"((dst)), "a"((c)), "c"((N)) : "memory"); 

की तरह एक डेमो कार्यक्रम में इस का उपयोग करते हुए:

int main(int argc, char **argv) 
{ 
    my_forced_inline_memset(argv[0], 0, argc); 
    return 0; 
} 

मुझे इस विधानसभा बनाता है:

00000000004004b0 <main>: 
    4004b0:  89 f9     mov %edi,%ecx 
    4004b2:  31 c0     xor %eax,%eax 
    4004b4:  48 8b 3e    mov (%rsi),%rdi 
    4004b7:  f3 ab     repz stos %rax,%es:(%rdi) 
    4004b9:  c3      retq 

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

नोट:repz stos %rax,(%rdi) (या इंटेल वाक्य रचना QWORD PTR समतुल्य) नहींmemset() रूप में ही है क्योंकि memset() के लिए विवरण के स्तर को एक एकल बाइट है। इसके बजाय उपरोक्त memset(..., c, N * 8) जैसा ही है। इसे ध्यान में रखो।

संपादित करें: के रूप में आप कोड लिखते हैं:

#include <stdint.h>      // for uintptr_t 
#define my_forced_inline_memset(dst, c, N)       \ 
    __asm__ __volatile__(            \ 
     "rep stos %1, (%0)\n\t"          \ 
     :: "D"((dst)), "a"((uintptr_t)(c)), "c"((N)/sizeof(uintptr_t)) \ 
     : "memory"); 

यह दोनों 32bit और 64bit के लिए संकलित करता है।

+0

सहायता के लिए धन्यवाद, ऊपर मेरा जवाब देखें। –

+0

दिशा ध्वज और एबीआई के बारे में, जीसीसी मेलिंग सूची, http://gcc.gnu.org/ml/gcc/2008-03/msg00330.html पर इसके बारे में एक लंबी और घुमावदार चर्चा है - उस संदेश का तात्पर्य है कि कंपाइलर इनलाइन असेंबली से पहले _assure_ 'DF = 0' होगा, यानी इसे स्पष्ट रूप से 'cld' करने के लिए आवश्यक नहीं होना चाहिए, यदि आवश्यक हो तो gcc माना जाता है कि यह आपके लिए करता है। साथ ही, पीएलटी उपयोग के कारण शाखा गलतफहमी को फिर से करें, क्या आप फ़ंक्शन पॉइंटर के माध्यम से 'memset()' को कॉल करके उस पर काम नहीं कर सकते? –

+0

जोड़ा जाना चाहिए था: https://lkml.org/lkml/2008/3/5/306 जीसीसी मेलिंग सूची थ्रेड वार्ता के गलत 'डीएफ' मानों के लिए लिनक्स कर्नेल फिक्स है। यह फ़ंक्शन कॉल से पहले 'डीएफ = 0' पर एबीआई आवश्यकता का भी उल्लेख करता है। जीसीसी थ्रेड, इसके कई संदेशों में से एक में यह भी कहता है कि 'memmove() 'std' हो सकता है लेकिन वापसी से पहले' cld' करने की गारंटी है। –

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