循环两个不同长度的向量时的优化

Optimization when looping through two vectors of different lengths

本文关键字:向量 优化 两个 循环      更新时间:2023-10-16
#include <vector>
#include <iostream>
#include <cmath>
#include <iomanip>
#include <sys/time.h>
using namespace std;
int main()
{
    struct timeval timeStart,
                    timeEnd;

创建随机0和1的向量。我们将用基准时间来总结它们。

    int n1 = 450000000;  // size of vector v1
    int n2 = 500000000;  // size of vector v2
    int i;
    vector<bool> v1(n1);
    vector<bool> v2(n2);
    for (i=0; i < n1 ; i++)
    {
    v1[i] = rand() % 2;
    }
    for (i=0; i < n2 ; i++)
    {
    v2[i] = rand() % 2;
    }

第一个求和技巧。将这些向量与两个完整的(独立的)循环求和

    int sum1 = 0;
    int sum2 = 0;
    gettimeofday(&timeStart, NULL);
    for (i=0; i < n1 ; i++)
    {
      sum1 += v1[i];
    }
    for (i=0; i < n2 ; i++)
    {
      sum2 += v2[i];
    }
    gettimeofday(&timeEnd, NULL);
    cout << "Two complete loops took " << ((timeEnd.tv_sec - timeStart.tv_sec) * 1000000 + timeEnd.tv_usec - timeStart.tv_usec) << " us"  << endl;

第二技术。将这些向量与完全环和部分环求和

    sum1 = 0;
    sum2 = 0;
    gettimeofday(&timeStart, NULL);
    for (i=0; i < n1 ; i++)
    {
      sum1 += v1[i];
      sum2 += v2[i];
    }
    for (i=n1; i < n2 ; i++)
    {
      sum2 += v2[i];
    }
    gettimeofday(&timeEnd, NULL);
    cout << "With a reduced second loop, it took " << ((timeEnd.tv_sec - timeStart.tv_sec) * 1000000 + timeEnd.tv_usec - timeStart.tv_usec) << " us"  << endl;
return 0;
}

我系统地得到了类似于

的输出
Two complete loops took 13291126 us
With a reduced second loop, it took 12758827 us

我本来期望相同的时间(如果编译器像我所期望的那样优化了第一个解决方案),或者我期望完整的两个循环比部分第二个循环花费更多的时间(而不仅仅是5%-10%)。

编译器最有可能在这里做什么?我是否应该考虑在将来循环两个不同长度的向量时使用部分循环?


供参考,我用g++ -std=c++11 -o test test.cpp编译,版本

g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.3.0
Thread model: posix

我试着解释一下在执行时间上的相似之处:

当你这样做的时候:

for (i=0; i < n1 ; i++)
{
  sum1 += v1[i];
}
for (i=0; i < n2 ; i++)
{
  sum2 += v2[i];
}

你执行2个循环,所以更多的指令,但你读取连续的内存在这两种情况下:缓存工作在一个最佳的方式(什么花费大部分时间在"现代"计算机是更多的内存访问/缓存丢失比执行代码)

顺便说一句,我怀疑编译器能否将这两个循环分组。

第二种情况需要较少的控制指令计数,但内存不是连续读取的。

也:优化器用来"展开"循环,从而减少控制指令的负面影响。

所以一边得到了,另一边就失去了。这些优化需要进行基准测试,并且可以根据处理器体系结构进行更大的变化。