为了加快库中的计算速度,我决定使用std::valarray
类。 documentation说:
std :: valarray和helper类被定义为没有某些 别名的形式,因此允许对这些类进行操作 优化,类似于在C中使用关键字limit的效果 编程语言。此外,采取的功能和运营商 valarray参数允许返回代理对象 编译器可以优化表达式,例如v1 = a * v2 + v3;作为执行v1 [i] = a * v2 [i] + v3 [i]的单个循环;避免任何临时或多次通过。
这正是我所需要的。当我使用g ++编译器时,它的工作方式如文档中所述。我开发了一个简单的示例来测试std::valarray
的效果:
void check(std::valarray<float>& a)
{
for (int i = 0; i < a.size(); i++)
if (a[i] != 7)
std::cout << "Error" << std::endl;
}
int main()
{
const int N = 100000000;
std::valarray<float> a(1, N);
std::valarray<float> c(2, N);
std::valarray<float> b(3, N);
std::valarray<float> d(N);
auto start = std::chrono::system_clock::now();
d = a + b * c;
auto end = std::chrono::system_clock::now();
std::cout << "Valarr optimized case: "
<< (end - start).count() << std::endl;
check(d);
// Optimal single loop case
start = std::chrono::system_clock::now();
for (int i = 0; i < N; i++)
d[i] = a[i] + b[i] * c[i];
end = std::chrono::system_clock::now();
std::cout << "Optimal case: " << (end - start).count() << std::endl;
check(d);
return 0;
}
在g ++上,我得到了:
Valarr optimized case: 1484215
Optimal case: 1472202
似乎所有操作d = a + b * c;
实际上都放在一个周期中,这在保持性能的同时简化了代码。但是,当我使用Visual Studio 2015时这不起作用。对于相同的代码,我得到:
Valarr optimized case: 6652402
Optimal case: 1766699
差异几乎是四倍;没有优化!为什么std::valarray
在Visual Studio 2015上无法按需工作?我做对了吗?如何在不放弃std::valarray
的情况下解决问题?
答案 0 :(得分:20)
我做对了吗?
您所做的一切正确。问题出在Visual Studio std::valarray
的实现中。
为什么
std::valarray
不能在Visual Studio 2015上正常工作?
只需打开任何valarray
运算符的实现,例如operator+
。您会看到类似的内容(在宏扩展之后):
template<class _Ty> inline
valarray<_Ty> operator+(const valarray<_Ty>& _Left,
const valarray<_Ty>& _Right)
{
valarray<TYPE> _Ans(_Left.size());
for (size_t _Idx = 0; _Idx < _Ans.size(); ++_Idx)
_Ans[_Idx] = _Left[_Idx] + _Right[_Idx];
return (_Ans)
}
如您所见,将创建一个新对象,在该对象中复制操作结果。确实没有优化。我不知道为什么,但这是事实。在Visual Studio中,std::valarray
的添加仅出于兼容性考虑。
为进行比较,考虑使用GNU implementation。如您所见,每个operator返回模板类_Expr,该模板类仅包含operation,但不包含数据。实际计算在assignment operator中执行,更具体地说,在__valarray_copy函数中执行。因此,在执行分配之前,所有操作都在代理对象_Expr
上执行。只有调用operator=
一次后,存储在_Expr
中的操作才会在单个循环中执行。这就是为什么您使用g ++获得如此出色的结果的原因。
我该如何解决问题?
您需要在互联网上找到合适的std::valarray
实现,也可以编写自己的实现。您可以使用GNU实现作为示例。