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

Posted

技术标签:

【中文标题】如何在 C++ 中展开嵌套的 for 循环?【英文标题】:How to unroll nested for loops in c++? 【发布时间】:2021-01-17 08:04:52 【问题描述】:

我想在编译时展开下面的嵌套循环。我在每个“for”循环之后都有一些代码和一个条件,如下面的代码 sn-p 所示。我找到了使用模板元编程在嵌套“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
         
       
     
   

【问题讨论】:

【参考方案1】:

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

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

【讨论】:

It seem that the loop is not unrolled,肯定是因为优化器认为函数太大了。【参考方案2】:

我将支持自定义增量和起始值作为挑战留给您。如果您的条件是运行时的,只需将 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;
            );
        );
    );

【讨论】:

这太好了,谢谢!虽然我有一个问题 - 如果我在我的帖子“//一些代码”中提到的两个嵌套的“for”循环之间有一些逻辑,如何在你的示例中实现它?【参考方案3】:

在简单的情况下,编译器会代替您执行此操作。但是可以使用编译器指令#pragma unroll。这篇文章可能会有所帮助 - What does #pragma unroll do exactly? Does it affect the number of threads?

【讨论】:

【参考方案4】:

为了了解如何做到这一点,下面是一个示例,我生成打印命令以显示 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)

Demo

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

【讨论】:

以上是关于如何在 C++ 中展开嵌套的 for 循环?的主要内容,如果未能解决你的问题,请参考以下文章

c++两个嵌套的for循环使用continue?

如何使用CUDA并行化嵌套for循环以在2D数组上执行计算

如何在c ++中为每个嵌套循环打印新列中的输出?

奇怪的蟒蛇“嵌套” for循环

在 C++ 中读取文件:for 和 while 嵌套循环没有按预期工作:串行?

使用 OpenMP 在 C、C++ 中并行化嵌套 for 循环的几种方法之间的区别