coder
coder

Reputation: 123

How to unroll nested for loops in c++?

I want to unroll the below nested loop at compile time. I have some code and a condition after every 'for' loop as shown in the code snippet below. I found ways to unroll it without any code (and conditions) between nested 'for' loops using template metaprogramming, but that did not help for my use case. I am looking for a way for my below example. I would really appreciate your help!

for (i=0;i<2;i++)
{
  //some code
  if (some condition using i)
  { 
    for(j=0;j<12;j++)
    {
       //some code
       if (another condition using j)
       {
         for(k=0;k<10;k++)
         {
           //some code
         }
       }
     }
   }
}

Upvotes: 0

Views: 762

Answers (4)

Nikos Athanasiou
Nikos Athanasiou

Reputation: 31549

To get an idea of how you can do this, here's an example where I generate the print commands to display a 2d matrix:

#include <utility>
#include <iostream>

template <std::size_t... Xs, std::size_t... Ys>
void unroll_cartesian_impl(
    std::index_sequence<Xs...> const&, 
    std::index_sequence<Ys...> const&)
{
    auto print_row = [](std::size_t row, auto... cols) {
        (std::printf("(%lu, %lu)\n", row, cols), ...);
    };
    
    (print_row(Xs, Ys...), ...);
}

template <std::size_t X, std::size_t Y>
void unroll_cartesian()
{
    unroll_cartesian_impl(
        std::make_index_sequence<X>{},
        std::make_index_sequence<Y>{});
}

int main ()
{
    unroll_cartesian<3, 3>();
}

Output

(0, 0) (0, 1) (0, 2) (1, 0) (1, 1) (1, 2) (2, 0) (2, 1) (2, 2)

Demo

By changing the size or number of index sequences you control the size of each loop and the number of loops. Also by replacing the printf call with your function you change the unrolled functionality and can even abstract it to be passed as a parameter to your unroller.

Upvotes: 0

lars
lars

Reputation: 475

I leave it as a challenge to you to support custom increment and start value. If your conditions are runtime, just pass N to F and implement the condition in the lambda.

This is more a template demonstration, i agree with rustyx. Let the compiler optimize it for you.

#include <iostream>

template<unsigned N>
struct IsOdd
{
    static constexpr bool value = N % 2 == 0; 
};    

template<unsigned N, typename F, template <unsigned> typename Condition>
struct RepeatIfHelper
{
    void operator()(F f)
    {
        if constexpr(Condition<N>::value)
        {
            f();
        }        
        RepeatIfHelper<N-1, F, Condition>()(f);
    }
};

template<typename F, template <unsigned> typename Condition>
struct RepeatIfHelper<0, F, Condition>
{
    void operator()(F f)
    {
        if constexpr(Condition<0>::value)
        {
            f();
        }
    }    
};

template<unsigned N, template <unsigned> typename Condition, typename F>
void RepeatIf(F f)
{
    RepeatIfHelper<N, F, Condition>()(f);
}

int main()
{
    RepeatIf<7, IsOdd>([](){ 
        RepeatIf<5, IsOdd>([](){
            RepeatIf<3, IsOdd>([](){
                std::cout << "Hi" << std::endl;
            });
        });
    });
}

Upvotes: 1

rustyx
rustyx

Reputation: 85462

Compile with optimization on (e.g. -O3 -march=native), the compiler will not only unroll but transpose, vectorize or sometimes entirely eliminate loops for you.

To ensure code quality, regularly check the generated assembly of critical code, e.g. on https://gcc.godbolt.org/.

Upvotes: 3

spiridon_the_sun_rotator
spiridon_the_sun_rotator

Reputation: 1044

In simple cases, the compiler will do it instead of you. But one may use a compiler directive #pragma unroll. This post may be helpful - What does #pragma unroll do exactly? Does it affect the number of threads?

Upvotes: 0

Related Questions