gpt4 book ai didi

c++ - 为什么这个程序片段的自动矢量化版本比简单版本慢

转载 作者:行者123 更新时间:2023-11-30 02:34:09 25 4
gpt4 key购买 nike

在较大的数值计算中,我必须执行将两个 vector 的元素的乘积相加的简单任务。由于此任务需要经常完成,因此我尝试利用编译器 (VC2015) 的自动矢量化功能。我引入了一个临时 vector ,其中乘积保存在第一个循环中,然后在第二个循环中执行求和。优化设置为完整和快速代码是首选。这样,第一个循环就被编译器矢量化了(我从编译器输出中知道这一点)。

结果出乎意料。矢量化代码在我的机器(核心 i5-4570 3.20 GHz)上的执行速度比简单代码慢 3 倍。谁能解释为什么以及什么可以提高性能?我已将两个版本的算法片段放入一个最小的运行示例中,我自己将其用于测试:

#include "stdafx.h"
#include <vector>
#include <Windows.h>
#include <iostream>

using namespace std;

int main()
{
// Prepare timer
LARGE_INTEGER freq,c_start,c_stop;
QueryPerformanceFrequency(&freq);

int size = 20000000; // size of data
double v = 0;

// Some data vectors. The data inside doesn't matter
vector<double> vv(size);
vector<double> tt(size);
vector<float> dd(size);

// Put random values into the vectors
for (int i = 0; i < size; i++)
{
tt[i] = rand();
dd[i] = rand();
}

// The simple version of the algorithm fragment
QueryPerformanceCounter(&c_start); // start timer
for (int p = 0; p < size; p++)
{
v += tt[p] * dd[p];
}
QueryPerformanceCounter(&c_stop); // Stop timer

cout << "Simple version took: " << ((double)(c_stop.QuadPart - c_start.QuadPart)) / ((double)freq.QuadPart) << " s" << endl;
cout << v << endl; // We use v once. This avoids its calculation to be optimized away.

// The version that is auto-vectorized

for (int i = 0; i < size; i++)
{
tt[i] = rand();
dd[i] = rand();
}

v = 0;
QueryPerformanceCounter(&c_start); // start timer
for (int p = 0; p < size; p++) // This loop is vectorized according to compiler output
{
vv[p] = tt[p] * dd[p];
}
for (int p = 0; p < size; p++)
{
v += vv[p];
}
QueryPerformanceCounter(&c_stop); // Stop timer

cout << "Vectorized version took: " << ((double)(c_stop.QuadPart - c_start.QuadPart)) / ((double)freq.QuadPart) << " s" << endl;
cout << v << endl; // We use v once. This avoids its calculation to be optimized away.

cin.ignore();

return 0;
}

最佳答案

您通过将产品存储在临时 vector 中增加了大量工作。

对于如此简单的大数据计算,您期望通过矢量化节省的 CPU 时间并不重要。只有内存引用很重要。

您添加了内存引用,因此运行速度较慢。

我原以为编译器会优化该循环的原始版本。我怀疑优化会影响执行时间(因为无论如何它都是由内存访问决定的)。但它应该在生成的代码中可见。如果你想像那样手动优化代码,临时 vector 总是错误的方法。正确的方向如下(为简单起见,我假设 size 是偶数):

for (int p = 0; p < size; p+=2)
{
v += tt[p] * dd[p];
v1 += tt[p+1] * dd[p+1];
}
v += v1;

请注意,您的数据足够大且操作足够简单,NO 优化应该可以在最简单的版本上进行改进。这包括我的样本手优化。但我认为你的测试并不能完全代表你真正想做或理解的事情。因此,对于较小的数据或更复杂的操作,我展示的方法可能会有所帮助。

另请注意,我的版本依赖于可交换的加法。对于实数,加法是可交换的。但在 float 中,它不是。答案可能相差太小,您无法在意。但这取决于数据。如果奇数/偶数位置的相反符号值很大,在原始序列的早期相互抵消,那么通过分离偶数和奇数位置,我的“优化”将完全破坏答案。 (当然,反之亦然。例如,如果所有偶数位置都很小,而赔率中包含大值并相互抵消,则原始序列产生垃圾,并且更改后的序列会更正确)。

关于c++ - 为什么这个程序片段的自动矢量化版本比简单版本慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34741786/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com