2012-11-11 139 views
7

我正在爲由任意數量的char標籤進行參數化的表達式編寫模板。英特爾C++編譯器編譯速度極慢遞歸decltype返回

給定一個參數列表,一個工廠函數根據是否有兩個相同類型的參數或者它們是否唯一,返回不同類型的表達式。

一個具體的例子:假設A是一種「加標籤」對象與其operator()重載以產生?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'>

要做到這一點,我不得不與autodecltype定義?Expression的工廠函數。此外,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; 
} 

上面的代碼如預期工作,並由GCC和Intel編譯器正確編譯。現在,我知道編譯器需要更多時間來執行遞歸模板推演,因爲我調整了使用的標籤數量。

在我的電腦上,如果用一個參數調用build_expression,那麼GCC 4.7.1平均需要大約0.26秒才能編譯。對於五個參數,編譯時間可以擴展到大約0.29秒,對於十個參數,編譯時間可以擴展到0.62秒。這完全合理。

這個故事與英特爾編譯器有很大不同。 ICPC 13.0.1在0.35秒內編譯單參數代碼,編譯時間對於最多四個參數保持不變。在五個參數中,編譯時間長達12秒,而在六個參數上,它在9600秒以上(即超過2小時40分鐘)上升。不用說,我沒有等待足夠長的時間來找出編譯七個參數版本需要多長時間。


兩個問題立刻浮現在腦海:

  • 是英特爾編譯器尤其已知慢編譯遞歸decltype

  • 有沒有什麼辦法來重寫這段代碼,以達到相同的效果,或許對編譯器更友好?

+0

這是旁白:http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-ac-identifier不要使用以下劃線開頭的符號你的代碼。標準庫可以,但你不應該。 – Yakk

+0

do_build是你唯一關心的類型嗎?如果是這樣,請嘗試返回'struct SameType {template operator R()const {return R(); }};' - 如果編譯它會削減大量的複製粘貼樣板,並且可能是編譯時的指數加速。 – Yakk

+0

在英特爾支持論壇上也發佈了這個問題,附近有很多非常有見識的人。 –

回答

3

這是刺傷它。我不是對每個元素進行兩兩比較,而是對類型列表進行排序,然後使用大腦死亡的獨特算法來查看是否有任何重複。

我對類型實現了合併排序,因爲它很有趣。可能是一種天真的泡泡排序會在合理數量的參數上更好地工作。請注意,一些工作將允許我們在長列表上進行合併排序,並專門用於短列表中的冒泡排序(甚至插入排序)。我不想寫一個模板快速排序。

這給了我一個編譯時間布爾值,說明列表中是否有重複項。然後我可以使用enable_if來選擇我要使用的超載。

請注意,您的解決方案涉及n^2層模板遞歸,在每個階段返回類型需要評估1級簡單類的類型,然後返回的類型也需要相同!如果英特爾編譯器記憶失敗,則說明指數量的工作。

我用一些助手增加了一些你的類。我使您的LabelNamestd::integral_constant繼承,所以我可以輕鬆編譯訪問其值。這使得排序代碼更容易一些。我還暴露了enum來自重複和唯一的返回值,所以我可以對結果進行簡單的printf調試。

大部分工作是編寫合併排序 - 是否有一個標準的編譯時間類型排序,我們可以使用?

#include <type_traits> 
#include <iostream> 

template <typename... T> struct TypeList { }; 

// NOTE THIS CHANGE: 
template <char C> struct LabelName:std::integral_constant<char, C> {}; 

template <typename... T> class UniqueExpression 
{ 
    // Contains implementation details in actual code 
public: 
    enum { is_unique = true }; 
}; 

template <typename... T> class RepeatedExpression 
{ 
    // Contains implementation details in actual code 
public: 
    enum { is_unique = false }; 
}; 

// A compile time merge sort for types 
// Split takes a TypeList<>, and sticks the even 
// index types into Left and odd into Right 
template<typename T> 
struct Split; 
template<> 
struct Split<TypeList<>> 
{ 
    typedef TypeList<> Left; 
    typedef TypeList<> Right; 
}; 
template<typename T> 
struct Split<TypeList<T>> 
{ 
    typedef TypeList<T> Left; 
    typedef TypeList<> Right; 
}; 

// Prepends First into the TypeList List. 
template<typename First, typename List> 
struct Prepend; 
template<typename First, typename... ListContents> 
struct Prepend<First,TypeList<ListContents...>> 
{ 
    typedef TypeList<First, ListContents...> type; 
}; 

template<typename First, typename Second, typename... Tail> 
struct Split<TypeList<First, Second, Tail...>> 
{ 
    typedef typename Prepend< First, typename Split<TypeList<Tail...>>::Left>::type Left; 
    typedef typename Prepend< Second, typename Split<TypeList<Tail...>>::Right>::type Right; 
}; 

// Merges the sorted TypeList<>s Left and Right to the end of TypeList<> MergeList 
template< typename Left, typename Right, typename MergedList=TypeList<> > 
struct Merge; 
template<typename MergedList> 
struct Merge< TypeList<>, TypeList<>, MergedList > 
{ 
    typedef MergedList type; 
}; 
template<typename L1, typename... Left, typename... Merged> 
struct Merge< TypeList<L1, Left...>, TypeList<>, TypeList<Merged... >> 
{ 
    typedef TypeList<Merged..., L1, Left...> type; 
}; 
template<typename R1, typename... Right, typename... Merged> 
struct Merge< TypeList<>, TypeList<R1, Right...>, TypeList<Merged...> > 
{ 
    typedef TypeList<Merged..., R1, Right...> type; 
}; 
template<typename L1, typename... Left, typename R1, typename... Right, typename... Merged> 
struct Merge< TypeList<L1, Left...>, TypeList<R1, Right...>, TypeList<Merged...>> 
{ 
    template<bool LeftIsSmaller, typename LeftList, typename RightList, typename MergedList> 
    struct MergeHelper; 

    template<typename FirstLeft, typename... LeftTail, typename FirstRight, typename... RightTail, typename... MergedElements> 
    struct MergeHelper< true, TypeList<FirstLeft, LeftTail...>, TypeList<FirstRight, RightTail...>, TypeList<MergedElements...> > 
    { 
    typedef typename Merge< TypeList<LeftTail...>, TypeList< FirstRight, RightTail... >, TypeList< MergedElements..., FirstLeft > >::type type; 
    }; 
    template<typename FirstLeft, typename... LeftTail, typename FirstRight, typename... RightTail, typename... MergedElements> 
    struct MergeHelper< false, TypeList<FirstLeft, LeftTail...>, TypeList<FirstRight, RightTail...>, TypeList<MergedElements...> > 
    { 
    typedef typename Merge< TypeList<FirstLeft, LeftTail...>, TypeList<RightTail... >, TypeList< MergedElements..., FirstRight > >::type type; 
    }; 

    typedef typename MergeHelper< (L1::value < R1::value), TypeList<L1, Left...>, TypeList<R1, Right...>, TypeList<Merged...> >::type type; 
}; 

// Takes a TypeList<T...> and sorts it via a merge sort: 
template<typename List> 
struct MergeSort; 
template<> 
struct MergeSort<TypeList<>> 
{ 
    typedef TypeList<> type; 
}; 
template<typename T> 
struct MergeSort<TypeList<T>> 
{ 
    typedef TypeList<T> type; 
}; 
template<typename First, typename Second, typename... T> 
struct MergeSort<TypeList<First, Second, T...>> 
{ 
    typedef Split<TypeList<First, Second, T...>> InitialSplit; 
    typedef typename MergeSort< typename InitialSplit::Left >::type Left; 
    typedef typename MergeSort< typename InitialSplit::Right >::type Right; 
    typedef typename Merge< Left, Right >::type type; 
}; 

// Sorts a TypeList<T..>: 
template<typename List> 
struct Sort: MergeSort<List> {}; 

// Checks sorted TypeList<T...> SortedList for adjacent duplicate types 
// return value is in value 
template<typename SortedList> 
struct Unique; 

template<> struct Unique< TypeList<> >:std::true_type {}; 
template<typename T> struct Unique< TypeList<T> >:std::true_type {}; 

template<typename First, typename Second, typename... Tail> 
struct Unique< TypeList< First, Second, Tail... > > 
{ 
    enum 
    { 
    value = (!std::is_same<First, Second>::value) && 
     Unique< TypeList<Second, Tail...> >::value 
    }; 
}; 

// value is true iff there is a repeated type in Types... 
template<typename... Types> 
struct RepeatedType 
{ 
    typedef TypeList<Types...> MyListOfTypes; 

    typedef typename Sort<MyListOfTypes>::type MyListOfTypesSorted; 
    enum 
    { 
    value = !Unique<MyListOfTypesSorted>::value 
    }; 
}; 

// A struct that creates an rvalue trivial constructed type 
// of any type requested. 
struct ProduceRequestedType 
{ 
    template<typename Result> 
    operator Result() { return Result(); }; 
}; 

struct ExpressionFactory { 
    template<typename... T> 
    typename std::enable_if< 
    !RepeatedType<T...>::value, 
    UniqueExpression<T...> 
    >::type 
    build_expression(T...) const 
    { 
    return ProduceRequestedType(); 
    }; 
    template<typename... T> 
    typename std::enable_if< 
    RepeatedType<T...>::value, 
    RepeatedExpression<T...> 
    >::type 
    build_expression(T...) const 
    { 
    return ProduceRequestedType(); 
    }; 
}; 

// Simple testing code for above: 
int main() 
{ 
    auto foo1 = ExpressionFactory().build_expression(LabelName<'a'>(), LabelName<'b'>(), LabelName<'a'>()); 
    typedef decltype(foo1) foo1Type; 
    if (foo1Type::is_unique) 
    std::cout << "foo1 is unique\n"; 
    else 
    std::cout << "foo1 is repeated\n"; 

    auto foo2 = ExpressionFactory().build_expression(LabelName<'q'>(), LabelName<'a'>(), LabelName<'b'>(), LabelName<'d'>(), LabelName<'t'>(), LabelName<'z'>()); 
    typedef decltype(foo2) foo2Type; 
    if (foo2Type::is_unique) 
    std::cout << "foo2 is unique\n"; 
    else 
    std::cout << "foo2 is repeated\n"; 
} 

另外我想補充你的代碼的批評:模板編程的編程 - 你typenames使用「I1」到「I9」,用於在功能整數變量的等價物。在做一些不重要的事情時給你的類型名稱有意義的名字。

這是如何編譯英特爾的?

+0

對於相當簡潔的代碼抱歉:我發佈的內容實際上是對我的程序進行了快速重寫,以便減少在此處發佈的代碼大小。這與我的示例代碼之間的主要區別在於我實際上需要關於具有相同類型的參數位置的信息(並執行張量收縮,只要有可能,就將其委託給MKL/BLAS)。不幸的是,額外的工作意味着真實的代碼要長得多。我會很好的看​​看你的解決方案,看看我是否可以修改/增強它來做我所需要的。非常感謝你的努力! – Saran

+0

我可以確認您的代碼在英特爾下0.4秒內編譯,即使我將其擴展爲10個參數。 – Saran

+0

在排序之前對類型進行索引並且增強Unique以生成相同索引列表的列表不應該很難。我猜測,在n^2類型的搜索和返回類型和返回語句中的類型的雙重評估之間是英特爾的模板記憶器(假設它有一個)。在記憶被阻止的情況下,您有一個n^2深度的模板評估二叉樹 - O(2^n^2)! – Yakk