Sto scrivendo un modello per espressioni parametrizzate da un numero arbitrario di etichette char
.Il compilatore Intel C++ è estremamente lento per la compilazione dei rendimenti decltype ricorsivi
Dato un elenco di argomenti, una funzione factory restituisce un'espressione di tipi diversi a seconda che esistano due argomenti dello stesso tipo o se siano univoci.
Un esempio concreto: supponiamo che A
sia un oggetto "etichettabile" con il suo operator()
sovraccarico per produrre un ?Expression<...>
. Lascia che sia a, b, ...
dichiarato come etichette LabelName<'a'>, LabelName<'b'>, ...
. Quindi A(a,b,c,d)
produrrebbe uno UniqueExpression<'a','b','c','d'>
, mentre invece A(a,c,b,c)
produrrebbe uno RepeatedExpression<'a','c','b','c'>
.
Per raggiungere questo obiettivo, ho dovuto definire la funzione di fabbrica ?Expression
con auto
e decltype
. Inoltre, lo decltype
deve sovrapporsi a un altro decltype
fino a quando il metaprogramma termina ricorrendo agli argomenti e il tipo restituito viene infine deciso. A titolo illustrativo, ho isolato un codice abbastanza minimale per il metodo factory.
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<>());
}
};
La fabbrica potrebbe essere chiamato nel programma in questo modo: (nel programma vero e proprio c'è un'altra classe con un sovraccarico operator()
che chiama la fabbrica)
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;
}
Il codice precedente funziona come previsto, ed è compilato correttamente sia da GCC che dal compilatore Intel. Ora, capisco che il compilatore impiegherebbe più tempo per eseguire la deduzione dei template ricorsivi mentre aumento il numero di etichette che uso.
Sul mio computer, se build_expression
viene chiamato con un argomento, GCC 4.7.1 richiede circa 0,26 secondi per la compilazione in media. Il tempo di compilazione è scalabile fino a circa 0,29 secondi per cinque argomenti e a 0,62 secondi per dieci argomenti. Questo è tutto perfettamente ragionevole.
La storia è abbastanza diversa con il compilatore Intel. ICPC 13.0.1 compila il codice a un argomento in 0,35 secondi e il tempo di compilazione rimane praticamente costante per un massimo di quattro argomenti. A cinque argomenti il tempo di compilazione sale a 12 secondi, e in sei argomenti spara sopra 9600 secondi (cioè oltre 2 ore e 40 minuti). Inutile dire che non ho aspettato abbastanza per scoprire quanto tempo ci vuole per compilare la versione a sette argomenti.
Due domande vengono subito in mente:
è il compilatore Intel particolarmente conosciuto per essere lento per compilare ricorsiva
decltype
?C'è un modo per riscrivere questo codice per ottenere lo stesso effetto in un modo forse più amichevole con il compilatore?
Questa è una parte: http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-ac-identifier non utilizzare i simboli che iniziano con un carattere di sottolineatura in il tuo codice. La libreria std lo fa, ma non dovresti. – Yakk
È l'unica cosa che ti interessa da do_build del tipo? In tal caso, prova a restituire 'struct SameType {template operator R() const {return R(); }}; '- se questo compila, ridurrebbe un sacco di copie di pasta copia e potrebbe essere un aumento esponenziale nella compilazione. –
Yakk
Posta anche questa domanda sui forum di supporto Intel, ci sono molte persone molto competenti in giro. –