2009-06-23 14 views
22

मैं एक सी ++ एक रन-टाइम for पाश के साथ नीचे स्निपेटसी ++ में 'फॉर' लूप को टेम्पलेट करना?

for(int i = 0; i < I; i++) 
    for (int j = 0; j < J; j++) 
    A(row(i,j), column(i,j)) = f(i,j); 

टुकड़ा बार-बार कहा जाता है। लूप सीमा 'I' और 'J' संकलन समय पर ज्ञात हैं (I/J 2 से 10 का क्रम है)। मैं किसी भी तरह टेम्पलेट का उपयोग कर लूप को अनलॉक करना चाहता हूं। मुख्य बाधा पंक्ति() और कॉलम() और एफ() फ़ंक्शंस है। मैं उन्हें row<i,j>::enum चाल का उपयोग करके संकलित समय पर मूल्यांकन किए गए समकक्ष मेटाप्रोग्राम के साथ प्रतिस्थापित करना चाहता हूं। - बहुत ज्यादा for संरचना

A(12,37) = 0.5; 
A(15,23) = 0.25; 
A(14,45) = 0.25; 

लेकिन मैं for नष्ट किए बिना ऐसा करना चाहते हैं:

क्या मैं सच में अच्छा लगेगा कुछ है कि अंततः जैसे बयानों के अनुक्रम में पाश का समाधान करता है। की भावना में कुछ:

TEMPLATE_FOR<i,0,I> 
    TEMPLATE_FOR<j,0,J> 
    A(row<i,j>::value, column<i,j>::value) = f<i,j>::value 

बढ़ा सकता है :: लैम्ब्डा (या कुछ और) इसे बनाने में मेरी सहायता करें?

उत्तर

4

Template Metaprograms और bubble sort कार्यान्वयन देखें।

+0

लिंक टूट गया है (डोमेन * ubiety.uwaterloo

यहाँ variadic टेम्पलेट्स कि हार्ड कोडित मैं और जम्मू की आवश्यकता नहीं है के साथ एक तरीका है। सीए * मौजूद नहीं है)। –

+0

@ पीटर मॉर्टेंसन, ठीक है, मैंने इसे ठीक किया है। –

12

एक अच्छा कंपाइलर आपके लिए अनोल करना चाहिए। उदाहरण के लिए, -ओ 2 विकल्प के साथ संकलित जीसीसी में लूप अनोलिंग चालू हो जाता है।

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

+0

[ओपी] यह उन लूपों का ऊपरी भाग नहीं है जिन्हें मैं दूर करने की कोशिश कर रहा हूं, यह पंक्ति(), कॉलम() और एफ() का मूल्यांकन अधिक है। वे वर्तमान में नियमित, नि: शुल्क कार्य हैं। मैं गारंटी देना चाहता हूं कि रन के बजाए संकलन समय पर उनका मूल्यांकन किया जाता है। – RAC

+2

यदि आप उन दिनचर्या को "इनलाइन" बनाते हैं, जो ऑप्टिमाइज़र को स्वयं को करने के लिए पर्याप्त अतिरिक्त जानकारी दे सकता है। –

+2

कोई कारण नहीं है कि यह उन लोगों को इनलाइन करने में सक्षम नहीं होना चाहिए, जब तक कॉल फ़ंक्शन पर पूर्ण फ़ंक्शन परिभाषा दिखाई दे। – jalf

4

आप को रोकने के लिए सक्षम हैं Boost MPL का उपयोग कर सकते हैं।

लूप अनोलिंग का एक उदाहरण इस mpl::for_each page पर है।

for_each< range_c<int,0,10> >(value_printer()); 

ऐसा प्रतीत नहीं होता कि यह संकलन समय पर सभी का मूल्यांकन किया गया है, लेकिन यह एक अच्छा प्रारंभिक बिंदु हो सकता है।

4

मैं कहूंगा कि यह एक झूठा अच्छा विचार है।

C++ में इस: row<i,j>::value
का मतलब है की तुलना में आप मैं j * आप के रूप में कई विभिन्न row<>() कार्यों होगा। आप इसे नहीं चाहते हैं क्योंकि यह कोड के आकार को बढ़ाएगा और बहुत सारे निर्देश कैश मिस करेगा।

मैंने यह देखा कि जब मैं एक एकल बूलियन चेक से बचने के लिए टेम्पलेट फ़ंक्शन कर रहा था।

यदि कोई छोटा कार्य है तो इसे केवल इनलाइन करें।

+0

विचार था कि संकलक गणित करने दें, प्रोसेसर नहीं । 10 से 10 मैट्रिक्स के लिए, 200 कार्यों का संकलन समय पर मूल्यांकन किया जा रहा है, और अंतिम बाइनरी में एड्रेस द्वारा प्रतिस्थापित किया गया है। यह अभी भी कैश फिट होना चाहिए, है ना? – xtofl

+0

हां यह होगा, लेकिन आपको 200 बार एक बार उपयोग करने के बजाय कैश के अंदर सभी थोज़ फ़ंक्शन को एक ही उपयोग के लिए लोड करना होगा। और दौड़ के बाद i-cache बेकार कोड से भरा जाएगा। @ आरएसी हालांकि मैं गलत हो सकता हूं कि कैश मुद्दे हमेशा मुश्किल होते हैं अगर आपको कोई सुधार मिल सके तो हमें बताएं। – Ben

+7

पंक्ति कक्षा प्रकार होने के नाते, और स्थिर स्थिरांक int होने का मान, मैं नहीं देखता कि यह निष्पादन योग्य कैसे होगा। यदि डीबग छीन लिया गया है, तो पंक्ति :: मान के लिए कोई कोड उत्पन्न नहीं होना चाहिए, मुझे –

3

आप बूस्ट.एमपीएल का उपयोग संकलन-समय पर पूरी चीज़ को लागू करने के लिए कर सकते हैं, लेकिन मुझे यकीन नहीं है कि यह तेज़ होगा।(एमपीएल अनिवार्य रूप से फिर से लागू सभी एसटीएल एल्गोरिदम संकलन समय metaprogramming टेम्पलेट के रूप में)

कि दृष्टिकोण के साथ समस्या यह है कि आप unrolling और इनलाइन किए जाने वाले एक बहुत कोड के, अनुदेश कैश पिटाई और खा सकता है जो अंत है स्मृति बैंडविड्थ जो सहेजा जा सकता था। यह विशाल, सूजन और धीमी कोड उत्पन्न कर सकता है।

शायद मैं संकल्प को समझने के लिए संकलक पर भरोसा करता हूं। जब तक row और column फ़ंक्शन परिभाषाएं लूप से दिखाई देती हैं, तो संकलक कॉल को इनलाइन कर सकते हैं और कई पुनरावृत्तियों को अनलोल कर सकते हैं क्योंकि यह फायदेमंद लगता है।

0

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

  1. मेरी for पाश एक टोंटी है?
  2. क्या यह कोई फर्क नहीं पड़ता है? हाथ से अनियंत्रित और मापने से आसानी से परीक्षण किया जाता है।

कई कंपाइलर्स/सीपीयू में, "looped" संस्करण कैश प्रभावों के कारण बेहतर प्रदर्शन दे सकता है।

याद रखें: पहले मापें, बाद में अनुकूलित करें - अगर बिल्कुल।

+0

अनलोल करना होगा, मुझे पता था कि आप इस क्यू के आसपास कहीं होंगे। – Ben

+4

टेड की प्रतिक्रिया के तहत कुछ प्रासंगिक चर्चा है - अनोलिंग नहीं है वास्तविक लक्ष्य, वास्तविक लक्ष्य समतुल्य टीएमपी के साथ पंक्ति() और कॉलम() फ़ंक्शंस को समाप्त कर रहा है। लूप अनोलिंग एक माध्यमिक आवश्यकता बन जाती है। लूप, मनमाना आदेश कोड प्रोफाइल किया गया है और विरासत (अनलॉप्ड, निश्चित आदेश) कोड से निश्चित रूप से धीमा है। – RAC

1

f को double वापस करने की आवश्यकता होगी - जो संकलन समय पर नहीं किया जा सकता है।

+1

मान लीजिए यह एक ऐसा हिस्सा है जो अभी भी मेरे लिए थोड़ा अस्पष्ट है। हालांकि, एफ() हमेशा एक अंश होता है और मैं टीएमपी को इसके न्यूमेरेटर और डिमॉमिनेटर के लिए स्वतंत्र रूप से कर सकता हूं, फिर static_cast को युगल करने और फ़्लोटिंग पीटी में अनुपात करने के लिए कर सकता है। (मुझे उम्मीद है)। लेकिन बहुत अच्छा मुद्दा है। – RAC

1

आप वाक्य रचना को संशोधित करने के इच्छुक हैं, तो थोड़ा तुम कुछ इस तरह कर सकते हैं:

template <int i, int ubound> 
struct OuterFor { 
    void operator()() { 
     InnerFor<i, 0, J>()(); 
     OuterFor<i + 1, ubound>()(); 
    } 
}; 

template <int ubound> 
struct OuterFor <ubound, ubound> { 
    void operator()() { 
    } 
}; 

InnerFor में, मैं बाहरी छोरों काउंटर है (समय निरंतर संकलन), जे भीतरी छोरों काउंटर है (प्रारंभ में 0 - समय स्थिर भी संकलित करें), ताकि आप एक संकलन समय टेम्पलेट के रूप में पंक्ति का मूल्यांकन कर सकें।

यह थोड़ा और जटिल है, लेकिन जैसा कि आप कहते हैं, पंक्ति(), col(), और f() आपके जटिल भाग हैं। कम से कम इसे आज़माएं और देखें कि प्रदर्शन इसके लायक है या नहीं। अपनी पंक्ति(), आदि कार्यों को सरल बनाने के लिए अन्य विकल्पों की जांच करना इसके लायक हो सकता है।

+0

मुझे वास्तव में यह पसंद है कि इस समाधान का नेतृत्व किया जा सकता है - क्या आप एक ऐसा उदाहरण प्रदान कर सकते हैं जो वास्तव में लूप को कॉल/आवेदक/तत्काल करता है? इसके अलावा, इसे गहरा घोंसला वाले लूप तक कितनी आसानी से बढ़ाया जा सकता है? लूप के लिए चार गहरे घोंसले सवाल से बाहर नहीं है। (ध्रुवीकरण राज्य, एक्स, वाई और जेड पर लूप)। – RAC

1

मुझे क्या करना यह इतना को एक अलग नज़रिए के साथ इस विचार लेने की कोशिश की कभी नहीं किया है ...

यह आप Boost.Preprocessor इस्तेमाल कर सकते हैं जैसे पाश unrolling (विशेष रूप से BOOST_PP_FOR और BOOST_PP_FOR_r मैक्रो) कर रहा है और उसके बाद वास्तविक निरंतर अभिव्यक्ति उत्पन्न करने के लिए टेम्पलेट का उपयोग करें।

template <int i, int j> 
struct inner 
{ 
    static void value() 
    { 
    A(row<i,j>::value, column<i,j>::value) = f<i,j>::value; 
    inner<i, j+1>::value(); 
    } 
}; 

template <int i> struct inner<i, J> { static void value() {} }; 

template <int i> 
struct outer 
{ 
    static void value() 
    { 
    inner<i, 0>::value(); 
    outer<i+1>::value(); 
    } 
}; 

template <> struct outer<I> { static void value() {} }; 

void test() 
{ 
    outer<0>::value(); 
} 

आप के माध्यम से पारित कर सकते हैं Avalue रों यदि आवश्यक हो तो से प्रत्येक के लिए एक पैरामीटर के रूप:

+1

कुछ विचारों के बाद, मुझे नहीं लगता कि यह काम करेगा। पकड़ यह है कि लूप को समाप्त करने के लिए उपयोग किए जाने वाले I और J वास्तव में स्थिरांक नहीं हैं, बल्कि उन्हें टेम्पलेट तर्कों से (दिखाया नहीं गया) से हटाया जाता है। स्निपेट जिसे मैंने पहले पोस्ट किया था वास्तव में टेम्पलेट {...} ब्लॉक के अंदर लपेटा गया है, और I और J अन्य टीएमपी का उपयोग करके ORDER से गणना की जाती है। आईआईआरसी प्रीप्रोसेसर को टेम्पलेट विस्तार होने से पहले बुलाया जाता है, जो मुझे लगता है कि गड़बड़ चीजों पर जा रहा है। हालांकि मैं गलत हो सकता था। – RAC

6

इस तरह यह सीधे करना है।

#include <utility> 

template <int j, class Columns> 
struct Inner; 

template <class Columns, class Rows> 
struct Outer; 

template <int j, int... i> 
struct Inner<j, std::index_sequence<i...>> 
{ 
    static void value() { (A(column<i, j>::value, row<i, j>::value), ...); } 
}; 


template <int... j, class Columns> 
struct Outer<std::index_sequence<j...>, Columns> 
{ 
    static void value() { (Inner<j, Columns>::value(), ...); } 
}; 

template <int I, int J> 
void expand() 
{ 
    Outer<std::make_index_sequence<I>, std::make_index_sequence<J>>::value(); 
} 

void test() 
{ 
    expand<3, 5>(); 
} 

(उत्पन्न विधानसभा के साथ झलकी: https://godbolt.org/g/DlgmEl)

+0

उदाहरण के लिए धन्यवाद - कोड काफी संकलित नहीं है। मुझे लगता है कि दोनों विशेषज्ञता गलत हैं, संभवतः 'टेम्पलेट <> संरचना बाहरी {स्थिर शून्य मान() {}};' 'टेम्पलेट <> संरचना बाहरी <0> {स्थिर शून्य मान() {}} होना चाहिए;'। मैं दूसरे के बारे में निश्चित नहीं हूं, शायद 'टेम्पलेट संरचना आंतरिक {स्थिर शून्य मान() {}}; '? (मुझे लगता है कि वे रिकर्सन को समाप्त करने के लिए हैं, उन्हें पूरी तरह से निकालना और संकलन हमेशा के लिए लेना बहुत मजेदार है!) – pjcard

+0

बस चेक किया गया - यह संकलित करता है और अपेक्षित काम करता है (प्रश्न द्वारा निहित कुछ मूल्यों के आधार पर)। यहां एक पूर्ण काम करने वाला स्निपेट है: https://godbolt.org/g/bvngt9 –

+0

इन दिनों यह विविधता के साथ बहुत आसान होगा। –

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