我希望升级我的显卡,以便能够并行处理以下任务。
由于我没有GPU计算的经验,这项任务是否合适,是否有可能在我购买之前估算处理的速度?
我的项目是公共资金,但预算有限,所以我需要做出正确的选择。
我有一个内部构建的相机芯片,以100fps的速度生成4x 256x256图像。通过调用c函数访问数据,将指针传递给unsigned short类型的数组数据。我可以快速地将数据读出到内存缓冲区中。
目前,原始数据已保存到磁盘,然后在以后离线处理,但是对于此相机的未来实验室实验,我希望在实验运行时访问从图像派生的数据。
我使用valarray编写了用c ++编写的方法来计算派生数据,但是在我当前的硬件上,每帧大约40ms的速度太慢了。 (我已经尝试过优化,并且我已经将时间从> 100ms减少了很多)
如果帧由S表示,则四个子帧(在时间上)是S1,S2,S3,S4。
我必须计算以下图像和图像平均值,(S1 + S2 + S3 + S4)/ 4,
Sqrt((S3-S1)^ 2 +(S4-S2)^ 2),
arctan(S3-S1 / S2-S4)
答案 0 :(得分:2)
这似乎非常适合由GPU执行的操作。 GPU比CPU更适合执行大量相对简单的计算。当存在逻辑或“线程”之间的相互依赖性时,它们不那么有效。虽然这种徘徊在“意见”领域,但我会尝试用一些数字来支持我的回答。
作为对您可以预期的性能的快速估计,我制作了一个快速的HLSL像素着色器来完成您的建议操作(未经测试 - 无法保证功能!):
Texture2D S[4] : register(t0);
SamplerState mySampler : register(s0);
struct PS_OUT
{
float4 average : SV_Target0;
float4 sqrt : SV_Target1;
float4 arctan : SV_Target2;
};
PS_OUT main(float2 UV: TEXCOORD0)
{
PS_OUT output;
float4 SSamples[4];
int i;
for (i = 0; i < 4; i++)
{
SSamples[i] = S[i].Sample(mySampler, UV);
}
float4 s3ms1 = SSamples[2] - SSamples[0];
float4 s4ms2 = SSamples[3] - SSamples[1];
output.average = (SSamples[0] + SSamples[1] + SSamples[2] + SSamples[3]) / 4.0;
output.sqrt = sqrt(s3ms1*s3ms1 + s4ms2*s4ms2);
output.arctan = atan(s3ms1 / s4ms2);
return output;
}
编译时(fxc /T ps_4_0 example.ps
),它给出了估计:使用了大约32个指令槽。
如果你每帧处理256x256(64k像素),那么大约2.1m /帧,或210m / s,100fps。看一下GPU性能图表(例如Nvidia:http://en.wikipedia.org/wiki/List_of_Nvidia_graphics_processing_units),他们所有的GPU都经过Geforce 4(大约2005年),有足够的速度来实现这一目标。
请注意,此着色器性能仅是一种估计值,列出的速率是理论上的最大值,而我只考虑像素单位工作(尽管它将完成大部分工作)。但是,对于任何最新的视频卡,FLOPS将远远超出您的需求,因此您应该能够在100fps的GPU上轻松完成此操作。假设您的PC比2005年更新,那么您可能已经拥有足够强大的视频卡了。
答案 1 :(得分:0)
除了@MuertoExcobito已经编写的内容之外,您还必须考虑将数据复制到GPU或从GPU复制数据,但在您的情况下,这不是太多数据。
我创建了一个基于推力的简单实现,可以使用CUDA 7编译和运行,如下所示:
nvcc -std=c++11 main.cu && ./a.out
平均超过10000次运行一次迭代,包括复制到GPU,计算三个结果图像并从GPU复制结果在我的计算机上需要 1.79 ms (Ubuntu 14.04 x64,Intel Xeon @ 3.6 Ghz,Geforce GTX 680)。
文件“helper_math.h”改编自CUDA SDK,可在此处找到:
https://gist.github.com/dachziegel/70e008dee7e3f0c18656
#include <thrust/device_vector.h>
#include <thrust/iterator/zip_iterator.h>
#include <thrust/transform.h>
#include <vector_types.h>
#include <iostream>
#include <chrono>
#include "helper_math.h"
template<typename T>
struct QuadVec
{
T S1, S2, S3, S4;
QuadVec(const int N) : S1(N), S2(N), S3(N), S4(N){}
};
template<typename T>
struct Result
{
T average, sqrt, arctan;
Result(const int N) : average(N), sqrt(N), arctan(N){}
};
typedef thrust::tuple<float4,float4,float4,float4> QuadInput;
typedef thrust::tuple<float4,float4,float4> TripleOutput;
struct CalcResult : public thrust::unary_function<QuadInput,TripleOutput>
{
__host__ __device__
TripleOutput operator()(const QuadInput& f) const
{
const float4 s3ms1 = thrust::get<2>(f) - thrust::get<0>(f);
const float4 s4ms2 = thrust::get<3>(f) - thrust::get<1>(f);
const float4 sqrtArg = s3ms1*s3ms1 + s4ms2*s4ms2;
const float4 atanArg = s3ms1 / s4ms2;
return thrust::make_tuple((thrust::get<0>(f) + thrust::get<1>(f) + thrust::get<2>(f) + thrust::get<3>(f)) / 4.0f,
make_float4(sqrtf(sqrtArg.x), sqrtf(sqrtArg.y), sqrtf(sqrtArg.z), sqrtf(sqrtArg.w)),
make_float4(atanf(atanArg.x), atanf(atanArg.y), atanf(atanArg.z), atanf(atanArg.w))
);
}
};
int main()
{
typedef thrust::host_vector<float4> HostVec;
typedef thrust::device_vector<float4> DevVec;
const int N = 256;
QuadVec<HostVec> hostFrame(N*N);
QuadVec<DevVec> devFrame(N*N);
Result<HostVec> hostResult(N*N);
Result<DevVec> devResult(N*N);
const int runs = 10000;
int accumulatedDuration = 0;
for (int i = 0; i < runs; ++i)
{
auto start = std::chrono::system_clock::now();
thrust::copy(hostFrame.S1.begin(), hostFrame.S1.end(), devFrame.S1.begin());
thrust::copy(hostFrame.S2.begin(), hostFrame.S2.end(), devFrame.S2.begin());
thrust::copy(hostFrame.S3.begin(), hostFrame.S3.end(), devFrame.S3.begin());
thrust::copy(hostFrame.S4.begin(), hostFrame.S4.end(), devFrame.S4.begin());
thrust::transform(thrust::make_zip_iterator(make_tuple(devFrame.S1.begin(), devFrame.S2.begin(), devFrame.S3.begin(), devFrame.S4.begin())),
thrust::make_zip_iterator(make_tuple(devFrame.S1.end(), devFrame.S2.end(), devFrame.S3.end(), devFrame.S4.end())),
thrust::make_zip_iterator(make_tuple(devResult.average.begin(), devResult.sqrt.begin(), devResult.arctan.begin())),
CalcResult() );
thrust::copy(devResult.average.begin(), devResult.average.end(), hostResult.average.begin());
thrust::copy(devResult.sqrt.begin(), devResult.sqrt.end(), hostResult.sqrt.begin());
thrust::copy(devResult.arctan.begin(), devResult.arctan.end(), hostResult.arctan.begin());
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - start);
accumulatedDuration += duration.count();
}
std::cout << accumulatedDuration/runs << std::endl;
return 0;
}