函数调用批处理模式的C++优化
Posted
技术标签:
【中文标题】函数调用批处理模式的C++优化【英文标题】:C++ optimization of function call batch mode 【发布时间】:2015-08-11 02:30:18 【问题描述】:编辑:找到了一个好的解决方案(-25%)valarray,如果我应该以某种方式更改问题状态,请发表评论
假设我有一个数组或任何其他具有 >E6 浮点/双精度值的容器
对于容器内的每个元素,我都想进行函数调用
例如。 sin(x) 或者更复杂的东西。
代码:C++11
代码是一个基准示例
#include <random>
#include <iostream>
#include <cmath>
#include <chrono>
#include <algorithm>
#include <valarray>
int main()
std::cout<<"start\n";
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<> dis(-1000, 1000);
int N=100;
while(N--)
std::cout<<"\nN: "<<N;
int T=1E7,T0=T;
std::vector<float> array;
while(T--)
array.push_back(dis(gen));
auto start_time = std::chrono::high_resolution_clock::now();
while(T0--)
array[T0]=sin(array[T0]);
auto time2=std::chrono::high_resolution_clock::now()-start_time;
std::cout<<"\nno: "<<std::chrono::duration_cast<std::chrono::microseconds>(time2).count();
int T=1E7;
std::vector<float> array;
while(T--)
array.push_back(dis(gen));
auto start_time = std::chrono::high_resolution_clock::now();
for_each (array.begin(), array.end(), sin);
auto time2=std::chrono::high_resolution_clock::now()-start_time;
std::cout<<"\nit: "<<std::chrono::duration_cast<std::chrono::microseconds>(time2).count();
int T=1E7;
std::valarray<float> array(T);
while(T--)
array[T]=dis(gen);
auto start_time = std::chrono::high_resolution_clock::now();
array=sin(array);
auto time2=std::chrono::high_resolution_clock::now()-start_time;
std::cout<<"\nvalarray: "<<std::chrono::duration_cast<std::chrono::microseconds>(time2).count();
std::cout<<"\nfin\n";
return 0;
有没有像这样快速又好用的方法:
sin(&array)
或
array=sin(array)
编辑:
for_each(it) seems to be 5-10% slower;
no: 1014219
it: 1060500
valarray: 742929
in microsec;
编辑 2: Sinus 是一个相当复杂的算法,问题更多的是如果有人知道一个库可能在 boost 中优化了一些数学函数以直接与容器一起工作,而不是每次都“启动”这个漏洞。
【问题讨论】:
基于基准,它让人相信您正在对未优化的构建进行基准测试,因为您的std::for_each
案例基本上什么都不做(gcc 甚至 eliminates the call to it),但该案例的时间仍然显示达到你的基准。这似乎是 SIMD 操作的完美候选,但如果您甚至不费心调整优化开关,编译器能够生成 SIMD 指令的机会可能会大大降低。
【参考方案1】:
我觉得没有什么神奇的代码可以让它变快或变好。尽可能简单。
我看到你可以采取两个行动。 1)将两个循环合并为一个。你可以使用任何你可以使用的循环。 2)使用并行计算,如 OpenMP 或 C++11 中的线程。
另外一件事是,如果你可以先使用uniform_int_distribution,如果T真的很大,也许你可以使用hashmap来存储sin值。
【讨论】:
随机输入只是为了让容器有一些值。我不认为让多个线程同时使用一个容器是个好主意 因为你没有在容器中添加/删除任何东西,你应该可以使用并行。哦,你改变了你的代码。在您的情况下,大小是预定义的,您可以先保留该大小并并行更改值。或者在你的情况下,你可以简单地使用数组(使用新的,而不是堆栈上的数组) 另外,你可以通过预定义你需要的内存来尽量避免在超大循环中的内存操作,在你的情况下,内存操作也是一个繁重的时间消耗。【参考方案2】:您可以使用 std::for_each 对容器中的每个元素(如向量)执行操作。
这很快。
http://www.cplusplus.com/reference/algorithm/for_each/
【讨论】:
我将 for_each 添加到基准测试中,似乎更慢【参考方案3】:如果您真的只对sin(float)
感兴趣,那么 AVX 就是您的最佳选择。它可以并行计算8个32位float
s。
【讨论】:
高级矢量扩展 (AVX)?你的意思是在汇编程序中构建我的函数? 对于像这样的简单任务,使用内部函数就可以了。但我不清楚sin()
是您真正感兴趣的有效载荷,还是只是您用于演示目的的东西。以上是关于函数调用批处理模式的C++优化的主要内容,如果未能解决你的问题,请参考以下文章