【问题标题】:Cuda, calculate distance matrix between 3d objectsCuda,计算 3d 对象之间的距离矩阵
【发布时间】:2014-01-17 23:58:46
【问题描述】:

我有一个由 3D 连接的 N 个对象(原子)组成的“字符串”(分子)(每个原子都有一个坐标)。我需要计算分子中每对原子之间的距离(见下面的伪代码)。 CUDA怎么能做到呢?我应该传递给内核函数 2 3D 数组吗?还是 3 个带坐标的数组:X[N]、Y[N]、Z[N]?谢谢。

结构原子 { 双 x,y,z; }

int main()
{

//N number of atoms in a molecule
double DistanceMatrix[N][N];
double d;
atom Atoms[N];

for (int i = 0; i < N; i ++)
    for (int j = 0; j < N; j++)
      DistanceMatrix[i][j] = (atoms[i].x -atoms[j].x)*(atoms[i].x -atoms[j].x) +
                     (atoms[i].y -atoms[j].y)* (atoms[i].y -atoms[j].y) + (atoms[i].z -atoms[j].z)* (atoms[i].z -atoms[j].z;

 }

【问题讨论】:

  • 使用 cuda 你想访问彼此相邻的内存。因此,请找到一种数据格式,其中您要检查的所有值都在内存中并排。
  • 分子有多少个原子?
  • 到 portforwardpodcast:谢谢,会按照 Roger Dahl 的建议尝试
  • 致 Roger Dahl:分子将有 40-3000 个原子。但是距离计算将与取决于距离的能量函数计算相结合。从计算的角度来看,能源可能非常昂贵。

标签: c++ c 3d cuda


【解决方案1】:

除非您正在处理非常大的分子,否则可能没有足够的工作来让 GPU 保持忙碌,因此 CPU 的计算速度会更快。

如果您打算计算欧几里得距离,那么您的计算是不正确的。你需要勾股定理的 3D 版本。

我会使用SoA 来存储坐标。

您希望生成一个包含尽可能多的合并读取和写入的内存访问模式。为此,请将每个 warp 中的 32 个线程生成的地址或索引安排得尽可能接近(稍微简化)。

threadIdx 指定块内的线程索引,blockIdx 指定网格内的块索引。 blockIdx 对于 warp 中的所有线程总是相同的。只有threadIdx 在块中的线程内变化。为了可视化如何将threadIdx 的 3 个维度分配给线程,请将它们视为嵌套循环,其中 x 是内循环,z 是外循环。因此,具有相邻 x 值的线程最有可能在同一个 warp 中,如果 x 可以被 32 整除,那么只有共享相同 x / 32 值的线程才在同一个 warp 中。

我在下面为您的算法提供了一个完整的示例。在示例中,i 索引派生自threadIdx.x,因此,为了检查扭曲是否会生成合并读取和写入,我将检查代码,同时为@987654334 插入一些连续值,例如 0、1 和 2 @ 并检查生成的索引是否也是连续的。

从j 索引生成的地址不太重要,因为j 是从threadIdx.y 派生的,因此在warp 内变化的可能性较小(如果threadIdx.x 可被32 整除,则永远不会变化)。

#include  "cuda_runtime.h"
#include <iostream>

using namespace std;

const int N(20);

#define check(ans) { _check((ans), __FILE__, __LINE__); }
inline void _check(cudaError_t code, char *file, int line)
{
  if (code != cudaSuccess) {
    fprintf(stderr,"CUDA Error: %s %s %d\n", cudaGetErrorString(code), file, line);
    exit(code);
  }
}

int div_up(int a, int b) {
  return ((a % b) != 0) ? (a / b + 1) : (a / b);
}

__global__ void calc_distances(double* distances,
  double* atoms_x, double* atoms_y, double* atoms_z);

int main(int argc, char **argv)
{
  double* atoms_x_h;
  check(cudaMallocHost(&atoms_x_h, N * sizeof(double)));

  double* atoms_y_h;
  check(cudaMallocHost(&atoms_y_h, N * sizeof(double)));

  double* atoms_z_h;
  check(cudaMallocHost(&atoms_z_h, N * sizeof(double)));

  for (int i(0); i < N; ++i) {
    atoms_x_h[i] = i;
    atoms_y_h[i] = i;
    atoms_z_h[i] = i;
  }

  double* atoms_x_d;
  check(cudaMalloc(&atoms_x_d, N * sizeof(double)));

  double* atoms_y_d;
  check(cudaMalloc(&atoms_y_d, N * sizeof(double)));

  double* atoms_z_d;
  check(cudaMalloc(&atoms_z_d, N * sizeof(double)));

  check(cudaMemcpy(atoms_x_d, atoms_x_h, N * sizeof(double), cudaMemcpyHostToDevice));
  check(cudaMemcpy(atoms_y_d, atoms_y_h, N * sizeof(double), cudaMemcpyHostToDevice));
  check(cudaMemcpy(atoms_z_d, atoms_z_h, N * sizeof(double), cudaMemcpyHostToDevice));

  double* distances_d;
  check(cudaMalloc(&distances_d, N * N * sizeof(double)));

  const int threads_per_block(256);
  dim3 n_blocks(div_up(N, threads_per_block));

  calc_distances<<<n_blocks, threads_per_block>>>(distances_d, atoms_x_d, atoms_y_d, atoms_z_d);

  check(cudaPeekAtLastError());
  check(cudaDeviceSynchronize());

  double* distances_h;
  check(cudaMallocHost(&distances_h, N * N * sizeof(double)));

  check(cudaMemcpy(distances_h, distances_d, N * N * sizeof(double), cudaMemcpyDeviceToHost));

  for (int i(0); i < N; ++i) {
    for (int j(0); j < N; ++j) {
      cout << "(" << i << "," << j << "): " << distances_h[i + N * j] << endl;
    }
  }

  check(cudaFree(distances_d));
  check(cudaFreeHost(distances_h));
  check(cudaFree(atoms_x_d));
  check(cudaFreeHost(atoms_x_h));
  check(cudaFree(atoms_y_d));
  check(cudaFreeHost(atoms_y_h));
  check(cudaFree(atoms_z_d));
  check(cudaFreeHost(atoms_z_h));

  return 0;
}

__global__ void calc_distances(double* distances,
  double* atoms_x, double* atoms_y, double* atoms_z)
{
  int i(threadIdx.x + blockIdx.x * blockDim.x);
  int j(threadIdx.y + blockIdx.y * blockDim.y);

  if (i >= N || j >= N) {
    return;
  }

  distances[i + N * j] =
    (atoms_x[i] - atoms_x[j]) * (atoms_x[i] - atoms_x[j]) +
    (atoms_y[i] - atoms_y[j]) * (atoms_y[i] - atoms_y[j]) +
    (atoms_z[i] - atoms_z[j]) * (atoms_z[i] - atoms_z[j]);
}

【讨论】:

  • 我是并行编程的初学者。我将首先运行您的代码,并且可能会有更多问题。关于 SoA:分子由一个非常复杂的 C++ 对象呈现。我需要将该对象的原子坐标复制到数组中。非常感谢您的帮助。
  • 致 Roger Dahl:我运行了代码,输出中只有 (i,0) 填充正确,其他元素等于 0。但是,可能我的代码中有错误
  • 我仍然无法弄清楚为什么只有 (i,0) 被填充。以防万一,发布我的 qsub 工作代码:
  • #!/bin/bash #$ -V #$ -cwd #$ -j y # 合并 stdout 和 stderr 流 #$ -N toy2 # 指定可执行文件 #$ -l h_rt=1: 30:00 # 运行时间不超过 1 小时 30 分钟 #$ -o $JOB_NAME.out$JOB_ID # 指定 stdout & stderr 输出 #$ -q gpu # 指定 GPU 队列 #$ -pe 2way 24 # 请求 2 个节点(24) module load cuda set -x ibrun $HOME/GPU_TOY2/toy2
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-13
  • 2018-12-02
  • 2016-11-29
  • 2011-09-21
  • 1970-01-01
  • 2013-12-16
  • 2016-10-30
相关资源
最近更新 更多