loop unrolling durch inlining hier möglich?
-
Hallo zusammen,
ich habe folgende Klasse
namespace { class rl_list_traverser_t { public: explicit rl_list_traverser_t(rl_val_t traverse_list, std::string func_name) : traverse_list{ traverse_list } , func_name{ func_name } { } auto car() -> rl_list_traverser_t& { try { (*this).traverse_list = traverse_list.car(); } catch(rl_unsupported_operation_t const& e) { throw rl_ill_formed_list_error_t{ (*this).func_name }; } return (*this); } auto cdr() -> rl_list_traverser_t& { try { (*this).traverse_list = traverse_list.cdr(); } catch(rl_unsupported_operation_t const& e) { throw rl_ill_formed_list_error_t{ (*this).func_name }; } return (*this); } auto get_list() -> rl_val_t { return traverse_list; } private: rl_val_t traverse_list; std::string func_name; }; }Diese wird wie folgt verwendet:
auto rl_lisp_cddddr(rl_val_array_t args) -> rl_val_array_t { return { rl_list_traverser_t{ args[0], "cddddr" } .cdr() .cdr() .cdr() .cdr() .get_list(); } }Es stellt sich mir die Frage ob die Chance besteht, dass hier der Code so optimiert wird, dass das Ergebnis leistungstechnisch folgendem entspricht.
rl_val_t tmp{ args[0] }; tmp = tmp.cdr(); tmp = tmp.cdr(); tmp = tmp.cdr(); tmp = tmp.cdr()Oder spricht etwas konkret gegen solch eine Optimierung?
MFG
Martin
-
Wenn wir schon beim Thema loop unrolling sind. Hat es irgend einen Vorteil wenn die Anzahl der Durchläufe einer Zählschleife bereits währen der Kompilierung bekannt ist. Also
template <size_t n> void do_it_n_times() { for(size_t i = 0; i < n; i++) do_it(); }anstatt
void do_it_n_times(size_t n)
{
for(size_t i = 0; i < n; i++)
do_it();
}
[/code]
-
anstatt
void do_it_n_times(size_t n) { for(size_t i = 0; i < n; i++) do_it(); }
-
Einfach mal den erzeugten Assemblercode ansehen. Hier ist die nicht-template Version:
int k = 0; void do_n_times(unsigned n) { for(unsigned i = 0; i < n; ++i) { k += i; } } int main() { do_n_times(5); }ergibt
do_n_times(unsigned int): xor eax, eax test edi, edi mov edx, DWORD PTR k[rip] je .L1 .L5: add edx, eax add eax, 1 cmp edi, eax jne .L5 mov DWORD PTR k[rip], edx .L1: rep ret main: add DWORD PTR k[rip], 10 xor eax, eax ret k: .zero 4Es wird zwar Code für die Funktion generiert, allerdings ist das Ergebnis schon vorberechnet in der main-Funktion und wird einfach nur noch in die Variable geschrieben.
Man kann die Funktion static machen oder in einen anonymen Namespace packen, dann wird auch kein Code mehr dafür generiert.
Die Template-Version
int k = 0; template<unsigned int n> void t_do_n_times() { for(unsigned i = 0; i < n; ++i) { k+= i; } } int main() { t_do_n_times<5>(); }ergibt
main: add DWORD PTR k[rip], 10 xor eax, eax ret k: .zero 4Hier ist das gleiche Ergebnis, wie mit static oder namespace {}.
Fazit: Nein, hat keinen Vorteil, wenn dann kommt es auf den Inhalt von do_it an, ob der Compiler das zur Compilezeit berechnen kann.