Развертывание для циклов в особом случае функции

Поэтому я пытаюсь оптимизировать некоторый код. У меня есть функция с циклом переменного размера. Однако для эффективности я хочу сделать чехлы с петлями 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 в моем коде для ясности.

3

Решение

Однако для эффективности я хочу сделать чехлы с петлями 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>{});
}

живой пример на Годболт

2

Другие решения

Если вам не нравятся шаблоны и вы не доверяете своему компилятору, всегда есть этот метод, который вдохновлен устаревшим методом ручного развертывания циклов, называемым «устройством Даффа»:

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

Его использование с современными компиляторами сомнительно.

2

Я бы предпочел пойти по этому пути, если вы хотите использовать принудительную (нестандартную) функцию всех популярных компиляторов:

__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.

1
По вопросам рекламы [email protected]