2011-05-26 16 views
6

मैं एक बहु-आयामी वेक्टर (गणितीय वेक्टर) बना रहा हूं जहां मैं मूल गणितीय परिचालन +, -, /, *, = की अनुमति देता हूं। टेम्पलेट दो मानकों में आता है, एक प्रकार (int, float आदि) है जबकि दूसरा वेक्टर का आकार है। वर्तमान में मैं लूप के माध्यम से संचालन लागू कर रहा हूं। अब आकार को संकलित समय पर जाना जाता है, क्या संकलक लूप को अनलॉक करेगा? यदि नहीं, तो क्या इसे (या न्यूनतम) प्रदर्शन दंड के साथ अनलॉक करने का कोई तरीका है?क्या संकलक इस लूप को अनलॉक करेगा?

template <typename T, u32 size> 
class Vector 
{ 
public: 
    // Various functions for mathematical operations. 
    // The functions take in a Vector<T, size>. 
    // Example: 
    void add(const Vector<T, size>& vec) 
    { 
     for (u32 i = 0; i < size; ++i) 
     { 
      values[i] += vec[i]; 
     } 
    } 

private: 
    T values[size]; 
}; 

इससे पहले कि किसी को टिप्पणी Profile then optimize कृपया ध्यान दें कि यह मेरी 3 डी ग्राफिक्स इंजन के लिए आधार है और यह तेजी से होना चाहिए। दूसरा, मैं खुद को शिक्षित करने के लिए जानना चाहता हूं।

+4

नहीं snarky ध्वनि करने के लिए, लेकिन अगर आप अनुकूलन के साथ संकलन और विधानसभा डंप आप काफी पता कर सकते हैं :) – Skurmedel

+1

यहां तक ​​कि अगर यह होना चाहिए वास्तव में तेजी से, रूपरेखा एक अमूल्य उपकरण है। वास्तव में, यह आपका उत्सव मित्र है * विशेष रूप से * यदि वास्तव में तेज़ होना है, क्योंकि यह अनुमान लगाने से कहीं बेहतर काम करता है। (साथ ही, उत्तर संकलक, बड़े पैमाने पर इस्तेमाल किए गए झंडे और शायद और भी अधिक पर निर्भर हो सकता है।) – delnan

+0

'यदि नहीं, तो क्या कोई (या न्यूनतम) प्रदर्शन दंड के साथ इसे अनलॉक करने का कोई तरीका है?' लूप को अनलॉक करने का पूरा बिंदु प्रदर्शन बढ़ाने के लिए है ... यदि आप प्रदर्शन लागत करते हैं तो आप ऐसा क्यों करेंगे? – Chad

उत्तर

9

आप अलग-अलग कोड को संकलित करने के तरीके को देखने के लिए अलग-अलग चाल कर सकते हैं।

Vector<int, 16> a, b; 
    Vector<int, 65536> c, d; 

    asm("xxx"); // marker 
    a.Add(b); 
    asm("yyy"); // marker 
    c.Add(d); 
    asm("zzz"); // marker 

अब संकलन

gcc -O3 1.cc -S -o 1.s 

और disasm

xxx 
# 0 "" 2 
#NO_APP 
    movdqa 524248(%rsp), %xmm0 
    leaq 524248(%rsp), %rsi 
    paddd 524184(%rsp), %xmm0 
    movdqa %xmm0, 524248(%rsp) 
    movdqa 524264(%rsp), %xmm0 
    paddd 524200(%rsp), %xmm0 
    movdqa %xmm0, 524264(%rsp) 
    movdqa 524280(%rsp), %xmm0 
    paddd 524216(%rsp), %xmm0 
    movdqa %xmm0, 524280(%rsp) 
    movdqa 524296(%rsp), %xmm0 
    paddd 524232(%rsp), %xmm0 
    movdqa %xmm0, 524296(%rsp) 
#APP 
# 36 "1.cc" 1 
    yyy 
# 0 "" 2 
#NO_APP 
    leaq 262040(%rsp), %rdx 
    leaq -104(%rsp), %rcx 
    xorl %eax, %eax 
    .p2align 4,,10 
    .p2align 3 
.L2: 
    movdqa (%rcx,%rax), %xmm0 
    paddd (%rdx,%rax), %xmm0 
    movdqa %xmm0, (%rdx,%rax) 
    addq $16, %rax 
    cmpq $262144, %rax 
    jne .L2 
#APP 
# 38 "1.cc" 1 
    zzz 

देखने जैसा कि आप देख, पहली पाश काफी छोटा unrolled पाने के लिए किया गया था। दूसरा लूप है।

+0

ग्रेट चाल, उत्कृष्ट अंतर्दृष्टि। –

0

कई कंपाइलर इस लूप को अनलॉक करेंगे, कोई विचार नहीं कि "कंपाइलर" आप किस संदर्भ का जिक्र कर रहे हैं। दुनिया में सिर्फ एक कंपाइलर नहीं है।

यदि आप गारंटी देना चाहते हैं कि यह अनियंत्रित है, तो टीएमपी (इनलाइनिंग के साथ) ऐसा कर सकता है। (यह वास्तव में टीएमपी के अधिक छोटे अनुप्रयोगों में से एक है, जिसे अक्सर मेटाप्रोग्रामिंग के उदाहरण के रूप में उपयोग किया जाता है)।

+0

मैंने प्रासंगिक टैग जोड़ा है। यह एमएसवीसी संकलक है। – Samaursa

+0

@ सैमुरुसा: कोई "कंपाइलर" नहीं है। आप जानते हैं कि एमएसवीसी के कितने स्वाद हैं? अपने प्रश्न में संस्करण स्ट्रिंग जोड़ें (जब आप 'cl.exe/version' चलाते हैं तो संकलक द्वारा मुद्रित)। –

4

पहला: आधुनिक सीपीयू शाखाओं की भविष्यवाणी करने के बारे में बहुत ही स्मार्ट हैं, इसलिए लूप को अनलॉक करने से मदद नहीं मिल सकती है (और यहां तक ​​कि चोट भी हो सकती है)।

दूसरा: हाँ, आधुनिक कंपाइलर्स जानते हैं कि इस तरह के लूप को कैसे अनलॉक करना है, यदि यह आपके लक्षित CPU के लिए एक अच्छा विचार है।

तीसरा: आधुनिक कंपाइलर लूप को ऑटो-वेक्टरिज़ भी कर सकते हैं, जो अनलॉकिंग से भी बेहतर है।

नीचे पंक्ति: आपको लगता है कि आप अपने कंपाइलर से अधिक स्मार्ट नहीं हैं जब तक कि आप बहुत सीपीयू आर्किटेक्चर के बारे में जानते हैं। अपना कोड सरल, सीधा तरीके से लिखें, और माइक्रो-ऑप्टिमाइज़ेशन के बारे में चिंता न करें जब तक कि आपका प्रोफाइलर आपको न बताए।

1

सबसे पहले, यह बिल्कुल निश्चित नहीं है कि लूप को अनलॉक करना फायदेमंद होगा।

आपके प्रश्न का एकमात्र संभावित उत्तर "यह निर्भर करता है" (संकलक झंडे पर, size, आदि के मूल्य पर)।

यदि आप वास्तव में जानना चाहते हैं, तो अपने कंपाइलर से पूछें: size के विशिष्ट मानों के साथ असेंबली कोड में संकलित करें और अनुकूलन झंडे के साथ आप वास्तविक रूप से उपयोग करेंगे, और परिणाम की जांच करें।

1

इसे समझने का एकमात्र तरीका यह है कि इसे अपने स्वयं के अनुकूलन पैरामीटर के साथ अपने स्वयं के कंपाइलर पर आज़माएं। अपने "इसे उतारना है" कोड के साथ एक परीक्षण फ़ाइल बनाने, test.cpp:

#include "myclass.hpp" 

void doSomething(Vector<double, 3>& a, Vector<double, 3>& b) { 
    a.add(b); 
} 

तो एक संदर्भ कोड स्निपेट reference.cpp:

#include "myclass.hpp" 

void doSomething(Vector<double, 3>& a, Vector<double, 3>& b) { 
    a[0] += b[0]; 
    a[1] += b[1]; 
    a[2] += b[2]; 
} 

और अब उन्हें संकलन और केवल विधानसभा थूक से बाहर करने के लिए जीसीसी का उपयोग करें:

for x in *.cpp; do g++ -c "$x" -Wall -Wextra -O2 -S -o "out/$x.s"; done 

मेरे अनुभव में, जीसीसी 3 या डिफ़ॉल्ट रूप से कम जब छोरों अवधि जिसका संकलन समय पर जाना जाता है का उपयोग कर के छोरों उतारना होगा; -funroll-loops का उपयोग करके इसे और भी अनलॉक कर दिया जाएगा।

1

लूप को रिकर्सिव टेम्पलेट इंस्टेंटेशन का उपयोग करके अनलॉक किया जा सकता है। यह आपके सी ++ कार्यान्वयन पर तेज़ हो सकता है या नहीं भी हो सकता है।

मैंने आपके उदाहरण को थोड़ा समायोजित किया, ताकि यह संकलित हो सके।

typedef unsigned u32; // or something similar 

template <typename T, u32 size> 
class Vector 
{ 
    // need to use an inner class, because member templates of an 
    // unspecialized template cannot be explicitly specialized. 
    template<typename Vec, u32 index> 
    struct Inner 
    { 
    static void add(const Vec& a, const Vec& b) 
    { 
     a.values[index] = b.values[index]; 
     // triggers recursive instantiation of Inner 
     Inner<Vec, index-1>::add(a,b); 
    } 
    }; 
    // this specialization terminates the recursion 
    template<typename Vec> 
    struct Inner<Vec, 0> 
    { 
    static void add(const Vec& a, const Vec& b) 
    { 
     a.values[0] = b.values[0]; 
    } 
    }; 

public: 

    // PS! this function should probably take a 
    // _const_ Vector, because the argument is not modified 
    // Various functions for mathematical operations. 
    // The functions take in a Vector<T, size>. 
    // Example: 
    void add(Vector<T, size>& vec) 
    { 
     Inner<Vector, size-1>::add(*this, vec); 
    } 

    T values[size]; 
}; 
संबंधित मुद्दे