繁体   English   中英

如何在 C++ 中展开嵌套的 for 循环?

[英]How to unroll nested for loops in c++?

我想在编译时展开下面的嵌套循环。 我在每个“for”循环之后都有一些代码和条件,如下面的代码片段所示。 我找到了使用模板元编程在嵌套的“for”循环之间没有任何代码(和条件)的情况下展开它的方法,但这对我的用例没有帮助。 我正在为下面的示例寻找一种方法。 我将衷心感谢您的帮助!

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
         }
       }
     }
   }
}

编译优化(例如-O3 -march=native ),编译器不仅会展开,还会转置、矢量化或有时完全消除循环。

为确保代码质量,请定期检查关键代码的生成程序集,例如在https://gcc.godbolt.org/ 上

我将支持自定义增量和起始值作为挑战留给您。 如果您的条件是运行时,只需将 N 传递给 F 并在 lambda 中实现该条件。

这更像是一个模板演示,我同意 rustyx。 让编译器为您优化它。

#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;
            });
        });
    });
}

在简单的情况下,编译器会代替你去做。 但是可以使用编译器指令#pragma unroll 这篇文章可能会有所帮助 - #pragma unroll 究竟做了什么? 它会影响线程数吗?

要了解如何执行此操作,以下是我生成打印命令以显示 2d 矩阵的示例:

#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>();
}

输出

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

演示

通过更改索引序列的大小或数量,您可以控制每个循环的大小和循环数。 此外,通过用您的函数替换printf调用,您可以更改展开的功能,甚至可以将其抽象为作为参数传递给展开程序。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM