Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/149.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为了提高循环效率:合并循环_C++_Performance_Loops_Benchmarking - Fatal编程技术网

C++ 为了提高循环效率:合并循环

C++ 为了提高循环效率:合并循环,c++,performance,loops,benchmarking,C++,Performance,Loops,Benchmarking,我一直认为减少迭代次数是提高程序效率的方法。因为我从未真正证实过这一点,所以我开始测试这一点 我做了以下C++程序,测量两个不同函数的时间: 第一个函数执行单个大循环并使用一组变量 第二个函数执行多个同样大的循环,但每个变量只有一个循环 完整的测试代码: #include <iostream> #include <chrono> using namespace std; int* list1; int* list2; int

我一直认为减少迭代次数是提高程序效率的方法。因为我从未真正证实过这一点,所以我开始测试这一点

我做了以下C++程序,测量两个不同函数的时间:

  • 第一个函数执行单个大循环并使用一组变量
  • 第二个函数执行多个同样大的循环,但每个变量只有一个循环
完整的测试代码:

    #include <iostream>
    #include <chrono>

    using namespace std;

    int* list1; int* list2;
    int* list3; int* list4;
    int* list5; int* list6;
    int* list7; int* list8;
    int* list9; int* list10;

    const int n = 1e7;

    // **************************************
    void myFunc1()
    {
        for (int i = 0; i < n; i++)
        {
            list1[i] = 2;
            list2[i] = 4;
            list3[i] = 8;
            list4[i] = 16;
            list5[i] = 32;
            list6[i] = 64;
            list7[i] = 128;
            list8[i] = 256;
            list9[i] = 512;
            list10[i] = 1024;
        }

        return;
    }

    // **************************************
    void myFunc2()
    {

        for (int i = 0; i < n; i++)
        {
            list1[i] = 2;
        }
        for (int i = 0; i < n; i++)
        {
            list2[i] = 4;
        }
        for (int i = 0; i < n; i++)
        {
            list3[i] = 8;
        }
        for (int i = 0; i < n; i++)
        {
            list4[i] = 16;
        }
        for (int i = 0; i < n; i++)
        {
            list5[i] = 32;
        }
        for (int i = 0; i < n; i++)
        {
            list6[i] = 64;
        }
        for (int i = 0; i < n; i++)
        {
            list7[i] = 128;
        }
        for (int i = 0; i < n; i++)
        {
            list8[i] = 256;
        }

        for (int i = 0; i < n; i++)
        {
            list9[i] = 512;
        }
        for (int i = 0; i < n; i++)
        {
            list10[i] = 1024;
        }

        return;
    }


    // **************************************
    int main()
    {
        list1 = new int[n]; list2 = new int[n];
        list3 = new int[n]; list4 = new int[n];
        list5 = new int[n]; list6 = new int[n];
        list7 = new int[n]; list8 = new int[n];
        list9 = new int[n]; list10 = new int[n];

        auto start = chrono::high_resolution_clock::now();

        myFunc1();

        auto elapsed = chrono::high_resolution_clock::now() - start;

        long long microseconds = chrono::duration_cast<chrono::microseconds>(elapsed).count();

        cout << "Time taken by func1 (micro s):" << microseconds << endl << endl;

        //

        start = chrono::high_resolution_clock::now();

        myFunc2();

        elapsed = chrono::high_resolution_clock::now() - start;

        microseconds = chrono::duration_cast<chrono::microseconds>(elapsed).count();

        cout << "Time taken by func2 (micro s):" << microseconds << endl << endl;

        delete[] list1; delete[] list2; delete[] list3; delete[] list4;
        delete[] list5; delete[] list6; delete[] list7; delete[] list8;
        delete[] list9; delete[] list10;

        return 0;
    }
#包括
#包括
使用名称空间std;
int*list1;int*list2;
int*list3;int*list4;
int*清单5;int*清单6;
int*清单7;int*list8;
int*list9;int*清单10;
常数int n=1e7;
// **************************************
void myFunc1()
{
对于(int i=0;i你从一个循环中得到的东西是,你失去了循环变量的递增。因此,在这种情况下,循环的内容非常简单,赋值(和测试)会产生很大的不同

您的示例也没有考虑到的是,连续内存访问通常比随机访问更快

在一个循环需要更长时间的函数中(试着加入睡眠而不是赋值),你会发现差异并不是很大


提高性能的方法是从数学开始——正确的算法总是能买到最大的改进。理想情况下,这是在手指敲击键盘之前完成的。

如果我不得不冒险猜测,我会说,您看到的是第一个函数中更频繁的内存缓存未命中的结果

myFunc1()
基本上是以随机访问方式执行10e8内存写入

myFunc2()
正在执行10e7个字的10x顺序内存写入


在现代内存体系结构上,我希望第二种更有效。

此代码创建变量:

    list1 = new int[n]; list2 = new int[n];
    list3 = new int[n]; list4 = new int[n];
    list5 = new int[n]; list6 = new int[n];
    list7 = new int[n]; list8 = new int[n];
    list9 = new int[n]; list10 = new int[n];
但它几乎肯定不会创建实际的物理页映射,直到实际修改内存

因此,您的
func1()
必须等待RAM的实际物理页的创建,而
func2()
则没有。更改顺序,映射时间将归因于
func2()
性能

对于发布的代码,最简单的解决方案是在执行定时运行之前运行
func1()
func2()


如果在进行任何基准测试之前未确保实际物理内存已映射,则该映射将是您首次修改内存时测量的时间的一部分。

尝试对代码进行基准测试时,您需要:

  • 在启用优化标志的情况下编译
  • 多次运行每个测试,以收集平均值
  • 您没有同时执行这两项操作。例如,您可以使用
    -O3
    ,对于平均值,我这样做了(我让函数从列表中返回一个元素):

    这证实了你所看到的,但区别是一个数量级(这是一个非常大的问题)


    在单for循环中,您执行一次内务处理,循环的计数器增加一次。在多个for循环中,这是扩展的(您需要执行的次数与for循环的次数相同)。当循环体有点琐碎时,就像在您的例子中一样,那么它可以产生不同


    另一个问题是数据局部性。第二个函数的循环一次将填充一个列表(意味着内存将以连续方式访问)。在第一个函数的大循环中,您将一次填充列表中的一个元素,这归结为内存的随机访问(例如,
    list1
    将被带到缓存中,因为您填充了其中的一个元素,因此在代码的下一行中,您将请求
    list2
    ,这意味着
    list1
    现在是无用的。但是,在第二个函数中,一旦您将
    list1
    带到缓存中,您将从
    for(int i = 0; i < 100; ++i)        
        dummy = myFunc1();
    
    Time taken by func1 (micro s):206693
    
    Time taken by func2 (micro s):37898
    
    // global modifier allows auto-vec of myFunc1
    #define GLOBAL_MODIFIER  __restrict
    #define LOCAL_MODIFIER  __restrict  // inside myFunc1
    
    static int *GLOBAL_MODIFIER list1, *GLOBAL_MODIFIER list2,
           *GLOBAL_MODIFIER list3, *GLOBAL_MODIFIER list4,
           *GLOBAL_MODIFIER list5, *GLOBAL_MODIFIER list6,
           *GLOBAL_MODIFIER list7, *GLOBAL_MODIFIER list8,
           *GLOBAL_MODIFIER list9, *GLOBAL_MODIFIER list10;
    
    .L12:    # myFunc1 inner loop from gcc8.1 -O3  with __restrict pointers
        movups  XMMWORD PTR [rbp+0+rax], xmm9       # MEM[base: l1_16, index: ivtmp.87_52, offset: 0B], tmp108
        movups  XMMWORD PTR [rbx+rax], xmm8 # MEM[base: l2_17, index: ivtmp.87_52, offset: 0B], tmp109
        movups  XMMWORD PTR [r11+rax], xmm7 # MEM[base: l3_18, index: ivtmp.87_52, offset: 0B], tmp110
        movups  XMMWORD PTR [r10+rax], xmm6 # MEM[base: l4_19, index: ivtmp.87_52, offset: 0B], tmp111
        movups  XMMWORD PTR [r9+rax], xmm5  # MEM[base: l5_20, index: ivtmp.87_52, offset: 0B], tmp112
        movups  XMMWORD PTR [r8+rax], xmm4  # MEM[base: l6_21, index: ivtmp.87_52, offset: 0B], tmp113
        movups  XMMWORD PTR [rdi+rax], xmm3 # MEM[base: l7_22, index: ivtmp.87_52, offset: 0B], tmp114
        movups  XMMWORD PTR [rsi+rax], xmm2 # MEM[base: l8_23, index: ivtmp.87_52, offset: 0B], tmp115
        movups  XMMWORD PTR [rcx+rax], xmm1 # MEM[base: l9_24, index: ivtmp.87_52, offset: 0B], tmp116
        movups  XMMWORD PTR [rdx+rax], xmm0 # MEM[base: l10_25, index: ivtmp.87_52, offset: 0B], tmp117
        add     rax, 16   # ivtmp.87,
        cmp     rax, 40000000     # ivtmp.87,
        jne     .L12      #,