Поэтому я пытаюсь оптимизировать некоторый код. У меня есть функция с циклом переменного размера. Однако для эффективности я хочу сделать чехлы с петлями 1, 2 и 3 размера, которые полностью развернуты. Пока что мой подход заключается в том, чтобы объявить размер цикла в качестве параметра const, а затем определить функции-обертки, которые вызывают основную функцию, передавая ее литералу для значения const. Я включил фрагмент кода, иллюстрирующий то, что я имею в виду.
inline void someFunction (const int a)
{
for (int i=0; i<a; i++)
{
// do something with i.
}
}
void specialCase()
{
someFunction (3);
}
void generalCase(int a)
{
someFunction (a);
}
Поэтому мой вопрос: разумно ли ожидать, что мой компилятор (GCC) развернет цикл for внутри specialCase. Я имею в виду, очевидно, что я могу скопировать — вставить содержимое someFunction в specialCase и заменить на 3, но я бы предпочел иметь дело только с одним определением someFunction в моем коде для ясности.
Однако для эффективности я хочу сделать чехлы с петлями 1, 2 и 3 размера, которые полностью развернуты.
Вы измерили, что это на самом деле быстрее? Я сомневаюсь, что это будет (или что компилятор не развернет цикл автоматически).
Пока что мой подход заключается в том, чтобы объявить размер цикла в качестве параметра const, а затем определить функции-обертки, которые вызывают основную функцию, передавая ее литералу для значения const.
const
здесь ничего не значит. Это не повлияет на способность компилятора развернуть цикл. Это просто означает, что a
не может быть изменен внутри тела функции, но это все еще аргумент времени выполнения.
Если вы хотите, чтобы развернуть, а затем принудительно. С C ++ 17 это довольно просто.
template <typename F, std::size_t... Is>
void repeat_unrolled_impl(F&& f, std::index_sequence<Is...>)
{
(f(std::integral_constant<std::size_t, Is>{}), ...);
}
template <std::size_t Iterations, typename F>
void repeat_unrolled(F&& f)
{
repeat_unrolled_impl(std::forward<F>(f),
std::make_index_sequence<Iterations>{});
}
Если вам не нравятся шаблоны и вы не доверяете своему компилятору, всегда есть этот метод, который вдохновлен устаревшим методом ручного развертывания циклов, называемым «устройством Даффа»:
void do_something(int i);
void do_something_n_times(int n)
{
int i = 0;
switch(n)
{
default:
while(n > 3) {
do_something(i++);
--n;
}
case 3: do_something(i++);
case 2: do_something(i++);
case 1: do_something(i++);
}
}
Но я думаю, что стоит сказать, что если вы не доверяете своему компилятору делать что-то настолько простое, как развертывание цикла для вас, возможно, пришло время подумать о новом компиляторе.
Обратите внимание, что устройство Даффа изначально было изобретено в качестве стратегии микрооптимизации для программ, скомпилированных с компиляторами, которые не применяли автоматические оптимизации циклического развертывания.
Он был изобретен Томом Даффом в 1983 году.
https://en.wikipedia.org/wiki/Duff%27s_device
Его использование с современными компиляторами сомнительно.
Я бы предпочел пойти по этому пути, если вы хотите использовать принудительную (нестандартную) функцию всех популярных компиляторов:
__attribute__((always_inline))
void bodyOfLoop(int i) {
// put code here
}
void specialCase() {
bodyOfLoop(0);
bodyOfLoop(1);
bodyOfLoop(2);
}
void generalCase(int a) {
for (int i=0; i<a; i++) {
bodyOfLoop(i);
}
}
Примечание: это решение GCC / Clang. использование __forceinline
для MSVC.