C++ 如何优化Cuda内核性能?
我正在构建一个粒子系统,在计算粒子位置的cuda内核性能方面遇到了困难C++ 如何优化Cuda内核性能?,c++,opengl,cuda,C++,Opengl,Cuda,我正在构建一个粒子系统,在计算粒子位置的cuda内核性能方面遇到了困难 __global__ void updateParticle(const int num_particles, const double time, const double gravity, GLfloat* device_particleCoordinates, GLfloat* device_particleStartCoordinates,
__global__
void updateParticle(const int num_particles, const double time, const double gravity,
GLfloat* device_particleCoordinates, GLfloat* device_particleStartCoordinates,
GLfloat* device_particleAcceleration, GLint* device_particleCreatedTime)
{
int threadId = threadIdx.x + blockIdx.x * blockDim.x;
if (threadId < num_particles)
{
int particleLifetime = (time - device_particleCreatedTime[threadId]) / 1000;
double distanceX = 0.5 * device_particleAcceleration[threadId * 2 + 0] * (particleLifetime * particleLifetime) / 5000.0;
double distanceY = 0.5 * device_particleAcceleration[threadId * 2 + 1] * (particleLifetime * particleLifetime) / 5000.0;
device_particleCoordinates[threadId * 2 + 0] = device_particleStartCoordinates[threadId * 2 + 0] + distanceX;
device_particleCoordinates[threadId * 2 + 1] = device_particleStartCoordinates[threadId * 2 + 1] + distanceY;
}
}
\u全局__
void updateParticle(常量int num_粒子、常量加倍时间、常量加倍重力、,
GLfloat*设备粒子坐标,GLfloat*设备粒子坐标,
GLfloat*设备\粒子过滤加速,GLint*设备\粒子创建时间)
{
int-threadId=threadIdx.x+blockIdx.x*blockDim.x;
if(线程ID<粒子数)
{
int particleLifetime=(时间-设备_particleCreatedTime[threadId])/1000;
双距离X=0.5*设备\粒子加速[threadId*2+0]*(粒子寿命时间*粒子寿命时间)/5000.0;
双距离Y=0.5*设备\粒子加速[threadId*2+1]*(粒子寿命时间*粒子寿命时间)/5000.0;
设备粒子坐标[threadId*2+0]=设备粒子坐标[threadId*2+0]+距离X;
设备粒子坐标[threadId*2+1]=设备粒子坐标[threadId*2+1]+距离;
}
}
内核的调用方式如下:
int blockSize = 32;
int nBlocks = maxParticleCount / 32 + 1;
updateParticle << <nBlocks, blockSize >> >(particles.size(), time, gravity, device_particleCoordinates,
device_particleStartCoordinates, device_particleAcceleration, device_particleCreatedTime);
glDrawArrays(GL_POINTS, 0, particles.size());
HANDLE_ERROR(cudaMemcpy(particleCoordinatesFlat.data(), device_particleCoordinates, particles.size() * 2 * sizeof(GLfloat), cudaMemcpyDeviceToHost));
int blockSize=32;
int nBlocks=maxparticlecont/32+1;
更新粒子>(particles.size()、时间、重力、设备粒子坐标、,
设备\u粒子起始坐标、设备\u粒子加速、设备\u粒子创建时间);
gldrawArray(GL_点,0,particles.size());
处理错误(cudaMemcpy(particleCoordinationsFlat.data(),设备(particleCoordinations,particles.size()*2*sizeof(GLfloat),cudaMemcpyDeviceToHost));
device_particleCoordinates链接到OpenGL缓冲区,以便直接修改坐标
性能不是很好,我认为这是由于内核调用。是否存在任何可能影响性能的明显缺陷?正如评论中所建议的,此内核可能不是您认为的性能限制。至少,你没有提供任何数据支持这个想法。但是,仍然可以提出一些建议,以改进内核的运行时
GLfloat
等同于float
。在这种情况下,特别是由于该内核(设备粒子坐标
)的主要输出是浮点
量,因此以双精度
进行的任何中间计算是否能提供多大的好处值得怀疑。您可以尝试将所有操作转换为float
算术__global__
void updateParticle1(const int num_particles, const double time, const double gravity,
GLfloat* device_particleCoordinates, GLfloat* device_particleStartCoordinates,
GLfloat* device_particleAcceleration, GLint* device_particleCreatedTime)
{
int threadId = threadIdx.x + blockIdx.x * blockDim.x;
if (threadId < num_particles)
{
float particleLifetime = (int)((((float)time) - (float)device_particleCreatedTime[threadId]) * (0.001f));
float2 dpA = *(reinterpret_cast<float2 *>(device_particleAcceleration)+threadId);
float spl2 = 0.0001f * particleLifetime*particleLifetime;
float distanceX = dpA.x * spl2;
float distanceY = dpA.y * spl2;
float2 dpC = *(reinterpret_cast<float2 *>(device_particleStartCoordinates)+threadId);
dpC.x += distanceX;
dpC.y += distanceY;
*(reinterpret_cast<float2 *>(device_particleCoordinates)+threadId) = dpC;
}
}
用常量装饰指针__restrict\uuu
(updateParticle2
)似乎没有为这个测试用例提供任何额外的好处。计算每个线程4个粒子(updateParticle3
)而不是1个粒子似乎对处理时间没有显著影响
特斯拉P100、CUDA 10.0、CentOS 7.5正如评论中已经提到的,该内核可能不是您认为的性能限制。至少,你没有提供任何数据支持这个想法。但是,仍然可以提出一些建议,以改进内核的运行时
GLfloat
等同于float
。在这种情况下,特别是由于该内核(设备粒子坐标
)的主要输出是浮点
量,因此以双精度
进行的任何中间计算是否能提供多大的好处值得怀疑。您可以尝试将所有操作转换为float
算术__global__
void updateParticle1(const int num_particles, const double time, const double gravity,
GLfloat* device_particleCoordinates, GLfloat* device_particleStartCoordinates,
GLfloat* device_particleAcceleration, GLint* device_particleCreatedTime)
{
int threadId = threadIdx.x + blockIdx.x * blockDim.x;
if (threadId < num_particles)
{
float particleLifetime = (int)((((float)time) - (float)device_particleCreatedTime[threadId]) * (0.001f));
float2 dpA = *(reinterpret_cast<float2 *>(device_particleAcceleration)+threadId);
float spl2 = 0.0001f * particleLifetime*particleLifetime;
float distanceX = dpA.x * spl2;
float distanceY = dpA.y * spl2;
float2 dpC = *(reinterpret_cast<float2 *>(device_particleStartCoordinates)+threadId);
dpC.x += distanceX;
dpC.y += distanceY;
*(reinterpret_cast<float2 *>(device_particleCoordinates)+threadId) = dpC;
}
}
用常量装饰指针__restrict\uuu
(updateParticle2
)似乎没有为这个测试用例提供任何额外的好处。计算每个线程4个粒子(updateParticle3
)而不是1个粒子似乎对处理时间没有显著影响
特斯拉P100、CUDA 10.0、CentOS 7.5除了罗伯特·克罗维拉的建议外,还应考虑:
- 每个内核线程处理更多的元素。你看,将线程设置为运行需要一些时间——读取参数、初始化变量(在你的例子中可能没有这么多)等等。当然,不是每个线程都处理连续的元素,而是在扭曲过程中有连续的通道来处理连续的元素
- 在所有指针参数上使用
——假设它们指向不同的内存区域。nvcc支持的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
关键字允许编译器进行各种非常有用的优化,否则它无法进行这些优化。更多关于为什么<>代码>限制符< /COD>是有用的(在C++中一般)参见:< /P>\uuuuu restrict\uuuu
- 每个内核线程处理更多的元素。你看,设置一个线程运行需要一些时间