我正在使用C ++来解决k耦合迭代方程。例如,对于3耦合情况,
f(n+1) = g(n) + 2*h(n) + c;
g(n+1) = 0.5*f(n+1) - h(n);
h(n+1) = ( f(n+1)+g(n+1) )/2;
其中C是常数。在C / C ++中,实现非常简单
#include <vector>
#include <iostream>
using namespace std;
void main(void)
{
double c= 0.24;
long k=0;
vector<double> f(900000000), g(900000000), h(900000000);
while (k<10000000)
{
f[0] = g[0] = h[0] = rand(); // the initial values of f, g, h are randomly picked
for (long n=1; n<900000000; n++)
{
f[n+1] = g[n] + 2*h[n] + c;
g[n+1] = 0.5*f[n+1] - h[n];
h[n+1] = ( f[n+1]+g[n+1] )/2;
}
//if the final value of f, g, h satisfying some condition then record it and go for next iteration
if (is_good(f[899999999], g[899999999], h[899999999]))
{
// record f[899999999], g[899999999], h[899999999]
k++;
}
}
}
这段代码很慢,因为它进展缓慢,取决于随机初始化。我之前没有编程GPU,但是我读了一些介绍,并且说在某些情况下GPU非常快。我读了几个例子,我觉得GPU只能用于“可分割”的情况(我的意思是任务可以分为子任务,因此可以并行实现)。我想知道它对我的案子有多大帮助。任何想法或建议都将受到高度欢迎。
答案 0 :(得分:2)
您的程序可以在while (k<10000000)
循环中轻松并行化。事实上,由于程序终止条件是未知的迭代次数(达到10M好的集合),您基本上可以删除已在内核中显示的整个代码并按原样运行,只需进行一些小的修改。
#include <curand.h>
#include <curand_kernel.h>
__constant__ double c = 0.24;
__device__ volatile unsigned int k = 0;
#define SCALE 32767.0
#define NUM_GOOD 10000000
__device__ int is_good(double f, double g, double h){
if (....){
...
return 1;
}
return 0;
}
__global__ void initCurand(curandState *state, unsigned long seed){
int idx = threadIdx.x + blockIdx.x*blockDim.x;
curand_init(seed, idx, 0, &state[idx]);
}
__global__ void mykernel(curandState *devStates, double *good_f, double *good_g, double *good_h){
int idx = threadIdx.x + blockDim.x*blockIdx.x;
double f0, g0, h0, f1, g1, h1;
curandState localState = devStates[idx];
while (k<NUM_GOOD){
// assuming you wanted independent starting values for f, g, h
f0 = (double)(curand_uniform(&localState)*SCALE);
g0 = (double)(curand_uniform(&localState)*SCALE);
h0 = (double)(curand_uniform(&localState)*SCALE);
for (int i = 0; i< 450000000; i++){
f1 = g0 + 2*h0 + c;
g1 = 0.5*f1 - h0;
h1 = (f1+g1 )/2;
f0 = g1 + 2*h1 + c;
g0 = 0.5*f0 - h1;
h0 = (f0+g0 )/2;}
if (is_good(f1, g1, h1))
{
unsigned int next = atomicAdd(&k, 1);
if (next<NUM_GOOD){
good_f[next] = f1;
good_g[next] = g1;
good_h[next] = h1;}
}
}
}
上面的代码只是一个大纲,可能存在一些错误,显然并非所有内容都在这里定义。
您可以使用启动的实际线程数来查看运行速度最快的线程。启动的所有线程都将用于填充“好”堆栈,直到它被填满。然后每个线程将检测到堆栈已满并退出。
编辑:回答以下一些问题:
似乎“int idx = threadIdx.x + blockDim.x * blockIdx.x;”是GPU的东西,我认为它与GPU中的线程有关,因此它对GPU编程至关重要吗?
是的,像threadIdx.x
这些变量是CUDA中的“内置”变量,允许每个线程做一些不同的事情(在这种情况下以不同的随机值开始)。
其次,您提供的所有代码看起来都像普通的C ++代码。但是你把“GPU关键部分”,那么我需要在该部分使用的任何特殊语法,或者它就像常规的c ++代码一样?
是的,很多CUDA内核代码都可以是普通的C ++代码,通常类似于你在CPU上做同样的事情。在这种情况下,我提到了一个关键部分并且已经链接了一个示例,但在考虑之后,一个关键部分(在这种情况下用于限制对数据区域的访问,以便GPU线程在更新时不会互相踩踏“好”的价值观在这里是过度的。对于想要填充好值的每个线程,只需要使用atomic操作在堆栈中保留“点”。我已相应地修改了代码。
答案 1 :(得分:1)
根据
while (k<10000000)
您正试图找到10M好{f, h, g}
。
在你的单线程CPU代码中,你逐个找到它们,而在GPU中,很容易启动数千个线程来并行找到满意的结果,直到总数达到10M。
对于耦合迭代部分,您仍然需要以传统方式计算它们。但是你仍然可以通过简化方程式来提高这部分的性能
f(n+1) = 1 *g(n) + 2*h(n) + c;
g(n+1) = 0.5 *g(n) + 0.5*c;
h(n+1) = 0.75*g(n) + 1*h(n) + 0.75*c;
向量A
的变换矩阵[f,g,h,c]'
是(在matlab代码中)
A = [ 0 1 2 1 ; 0 .5 0 .5; 0 .75 1 .75 ; 0 0 0 0];
然后我们有[f,g,h,c]'{n}=A^n * [f,g,h,c]'{0}
。您会发现A^n
会在几次迭代中收敛到[0 3 2 3; 0 0 0 0; 0 1.5 1 1.5; 0 0 0 0]
。