मैं char
लेबलों की मनमानी संख्या द्वारा पैरामीट्रिज्ड अभिव्यक्तियों के लिए एक टेम्पलेट लिख रहा हूं।इंटेल सी ++ कंपाइलर रिकर्सिव decltype रिटर्न संकलित करने के लिए बेहद धीमा है
एक तर्क सूची को देखते हुए, फैक्ट्री फ़ंक्शन अलग-अलग प्रकार की अभिव्यक्ति देता है कि इस प्रकार के दो तर्क हैं या वे अद्वितीय हैं या नहीं।
एक ठोस उदाहरण: मान लीजिए कि A
?Expression<...>
का उत्पादन करने के लिए अधिभारित "लेबल योग्य" ऑब्जेक्ट है। a, b, ...
को लेबल LabelName<'a'>, LabelName<'b'>, ...
के रूप में घोषित किया जाना चाहिए। फिर A(a,b,c,d)
UniqueExpression<'a','b','c','d'>
का उत्पादन करेगा, जबकि A(a,c,b,c)
इसके बजाय RepeatedExpression<'a','c','b','c'>
का उत्पादन करेगा।
इसे प्राप्त करने के लिए, मुझे ?Expression
के फैक्टरी फ़ंक्शन को auto
और decltype
के साथ परिभाषित करना पड़ा। इसके अलावा, decltype
को decltype
तक कैस्केड करना है जब तक मेट्रोग्रामोग तर्कों के माध्यम से रिकर्सिंग समाप्त नहीं कर लेता है और अंततः वापसी का प्रकार तय किया जाता है। एक उदाहरण के रूप में, मैंने फैक्ट्री विधि के लिए काफी कम कोड अलग कर दिया है।
template <typename... T> struct TypeList { };
template <char C> struct LabelName { };
template <typename... T> class UniqueExpression
{
// Contains implementation details in actual code
};
template <typename... T> class RepeatedExpression
{
// Contains implementation details in actual code
};
class ExpressionFactory {
private:
template <char _C, typename... T, typename... _T>
static UniqueExpression<T...>
_do_build(TypeList<T...>,
TypeList<LabelName<_C>>,
TypeList<>,
TypeList<_T...>)
{
return UniqueExpression<T...>();
}
template <char _C, typename... T, typename... _T1, typename... _T2, typename... _T3>
static RepeatedExpression<T...>
_do_build(TypeList<T...>,
TypeList<LabelName<_C>, _T1...>,
TypeList<LabelName<_C>, _T2...>,
TypeList<_T3...>)
{
return RepeatedExpression<T...>();
}
template <char _C1, char _C2, typename... T, typename... _T1, typename... _T2, typename... _T3>
static auto
_do_build(TypeList<T...>,
TypeList<LabelName<_C1>, _T1...>,
TypeList<LabelName<_C2>, _T2...>,
TypeList<_T3...>)
-> decltype(_do_build(TypeList<T...>(),
TypeList<LabelName<_C1>, _T1...>(),
TypeList<_T2...>(),
TypeList<_T3..., LabelName<_C2>>()))
{
return _do_build(TypeList<T...>(),
TypeList<LabelName<_C1>, _T1...>(),
TypeList<_T2...>(),
TypeList<_T3..., LabelName<_C2>>());
}
template <char _C1, char _C2, typename... T, typename... _T1, typename... _T2>
static auto
_do_build(TypeList<T...>,
TypeList<LabelName<_C1>, LabelName<_C2>, _T1...>,
TypeList<>,
TypeList<LabelName<_C2>, _T2...>)
-> decltype(_do_build(TypeList<T...>(),
TypeList<LabelName<_C2>, _T1...>(),
TypeList<_T2...>(),
TypeList<>()))
{
return _do_build(TypeList<T...>(),
TypeList<LabelName<_C2>, _T1...>(),
TypeList<_T2...>(),
TypeList<>());
}
public:
template <char C, typename... T>
static auto
build_expression(LabelName<C>, T...)
-> decltype(_do_build(TypeList<LabelName<C>, T...>(),
TypeList<LabelName<C>, T...>(),
TypeList<T...>(),
TypeList<>()))
{
return _do_build(TypeList<LabelName<C>, T...>(),
TypeList<LabelName<C>, T...>(),
TypeList<T...>(),
TypeList<>());
}
};
कारखाने कार्यक्रम में कहा जा सकता है ताकि तरह: (वास्तविक कार्यक्रम में वहाँ एक ओवरलोड operator()
जो कारखाने कॉल के साथ एक और वर्ग है)
int main()
{
LabelName<'a'> a;
LabelName<'b'> b;
...
LabelName<'j'> j;
auto expr = ExpressionFactory::build_expression(a,b,c,d,e,f,g,h,i,j);
// Perhaps do some cool stuff with expr
return 0;
}
ऊपर कोड अपेक्षित तरीके से काम, और जीसीसी और इंटेल कंपाइलर दोनों द्वारा सही ढंग से संकलित किया गया है। अब, मैं समझता हूं कि संकलक को रिकर्सिव टेम्पलेट कटौती करने में अधिक समय लगेगा क्योंकि मैं उपयोग किए जाने वाले लेबलों की संख्या को क्रैंक करता हूं।
मेरे कंप्यूटर पर, यदि build_expression
को एक तर्क के साथ बुलाया जाता है, तो औसत पर संकलित करने के लिए जीसीसी 4.7.1 लगभग 0.26 सेकेंड लेता है। संकलन समय पांच तर्कों के लिए लगभग 0.2 9 सेकेंड तक और दस तर्कों के लिए 0.62 सेकेंड तक स्केल करता है। यह सब बिल्कुल उचित है।
कहानी इंटेल कंपाइलर के साथ काफी अलग है। आईसीपीसी 13.0.1 0.35 सेकंड में एक-तर्क कोड संकलित करता है, और संकलन समय चार तर्कों के लिए काफी स्थिर रहता है। पांच तर्कों पर संकलन समय 12 सेकंड तक चला जाता है, और छह तर्कों पर यह 9600 सेकंड (यानी, 2 घंटे और 40 मिनट से अधिक) तक चलता है। कहने की जरूरत नहीं है, मैंने यह पता लगाने के लिए काफी देर तक इंतजार नहीं किया है कि सात-तर्क संस्करण को संकलित करने में कितना समय लगता है।
दो सवालों के तुरंत दिमाग में आते हैं:
पुनरावर्ती
decltype
संकलित करने के लिए इंटेल संकलक विशेष रूप से धीमी गति से हो जाता है?क्या इस कोड को फिर से लिखने का कोई तरीका है जो एक ही प्रभाव को प्राप्त करने के लिए संभवतः संकलक के लिए मित्रवत है?
यह एक तरफ है: http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-ac-identifier अंडरस्कोर से शुरू होने वाले प्रतीकों का उपयोग नहीं करते हैं तुम्हारा कोड। एसडीडी लाइब्रेरी करता है, लेकिन आपको नहीं करना चाहिए। – Yakk
क्या आप केवल एक चीज की देखभाल करते हैं जो कि do_build प्रकार से है? यदि ऐसा है तो 'संरचना समान प्रकार {टेम्पलेट ऑपरेटर आर() कॉन्स {रिटर्न आर() लौटने का प्रयास करें; }}; '- अगर वह संकलित करता है तो यह बहुत से कॉपी पेस्ट बॉयलरप्लेट को काट देगा और संकलन में एक घातीय गति हो सकता है। –
Yakk
इंटेल समर्थन फ़ोरम पर भी इस प्रश्न को पोस्ट करें, वहां बहुत सारे जानकार लोग हैं। –