繁体   English   中英

如何有效地将向量重复到cuda中的矩阵?

[英]How to efficiently repeat a vector to a matrix in cuda?

我想重复一个向量以在cuda中形成矩阵,避免过多的内存复制。 向量和矩阵均在GPU上分配。

例如:

我有一个向量:

a = [1 2 3 4]

将其扩展为矩阵:

b = [1 2 3 4;
     1 2 3 4;
     .......
     1 2 3 4]

我尝试过的是分配b的每个元素。 但这涉及到大量GPU内存到GPU内存的复制。

我知道这在Matlab中很容易(使用repmat),但是如何在cuda中高效地做到这一点呢? 我没有在方舟上找到任何常规。

根据注释进行编辑 ,我将代码更新为可以处理行级或列级基础存储的版本。

这样的事情应该相当快:

// for row_major, blocks*threads should be a multiple of vlen
// for column_major, blocks should be equal to vlen
template <typename T>
__global__ void expand_kernel(const T* vector, const unsigned vlen, T* matrix, const unsigned mdim, const unsigned col_major=0){
  if (col_major){
    int idx = threadIdx.x+blockIdx.x*mdim;
    T myval = vector[blockIdx.x];
    while (idx < ((blockIdx.x+1)*mdim)){
      matrix[idx] = myval;
      idx += blockDim.x;
      }
    }
  else{
    int idx = threadIdx.x + blockDim.x * blockIdx.x;
    T myval = vector[idx%vlen];
    while (idx < mdim*vlen){
      matrix[idx] = myval;
      idx += gridDim.x*blockDim.x;
      }
    }
}

假设您的矩阵尺寸为mdim行x vlen列(似乎是您在问题中概述的尺寸)。

您可以调整网格和块的尺寸,以找出对特定GPU最快的工作方式。 对于行较大的情况,请从每个块256或512个线程开始,然后将块数设置为等于或大于GPU中SM数的4倍。 选择网格尺寸和块尺寸的乘积,使其等于向量长度vlen的整数倍。 如果这很困难,则选择任意的但“较大”的线程块大小(例如250或500)不会导致效率损失太多。

对于大列情况,请选择每个块256或512个线程,然后选择等于向量长度vlen的块数。 如果vlen > 65535,则需要对其进行编译以实现3.0或更高的计算能力。 如果vlen小,可能小于32,则此方法的效率可能会大大降低。 如果将每个块的线程数增加到GPU的最大数量(512或1024),将会发现某些缓解方法。可能还有其他“扩展”实现可能更适合于列较大的“窄”矩阵情况。 例如,对列主代码的直接修改将允许每个矢量元素2个块,或每个矢量元素4个块,然后总启动块将是2 * vlen或4 * vlen

这里有一个充分的工作例如,带宽测试的得力助手,以证明上面的代码实现〜90%的表示吞吐量的bandwidthTest

$ cat t546.cu
#include <stdio.h>

#define W 512
#define H (512*1024)
// for row_major, blocks*threads should be a multiple of vlen
// for column_major, blocks should be equal to vlen
template <typename T>
__global__ void expand_kernel(const T* vector, const unsigned vlen, T* matrix, const unsigned mdim, const unsigned col_major=0){
  if (col_major){
    int idx = threadIdx.x+blockIdx.x*mdim;
    T myval = vector[blockIdx.x];
    while (idx < ((blockIdx.x+1)*mdim)){
      matrix[idx] = myval;
      idx += blockDim.x;
      }
    }
  else{
    int idx = threadIdx.x + blockDim.x * blockIdx.x;
    T myval = vector[idx%vlen];
    while (idx < mdim*vlen){
      matrix[idx] = myval;
      idx += gridDim.x*blockDim.x;
      }
    }
}

template <typename T>
__global__ void check_kernel(const T* vector, const unsigned vlen, T* matrix, const unsigned mdim, const unsigned col_major=0){
  unsigned i = 0;
  while (i<(vlen*mdim)){
    unsigned idx = (col_major)?(i/mdim):(i%vlen);
    if (matrix[i] != vector[idx]) {printf("mismatch at offset %d\n",i); return;}
    i++;}
}

int main(){

  int *v, *m;
  cudaMalloc(&v, W*sizeof(int));
  cudaMalloc(&m, W*H*sizeof(int));
  int *h_v = (int *)malloc(W*sizeof(int));
  for (int i = 0; i < W; i++)
    h_v[i] = i;
  cudaMemcpy(v, h_v, W*sizeof(int), cudaMemcpyHostToDevice);

  // test row-major

  cudaEvent_t start, stop;
  cudaEventCreate(&start);
  cudaEventCreate(&stop);
  cudaEventRecord(start);
  expand_kernel<<<44, W>>>(v, W, m, H);
  cudaEventRecord(stop);
  float et;
  cudaEventSynchronize(stop);
  cudaEventElapsedTime(&et, start, stop);
  printf("row-majortime: %fms, bandwidth: %.0fMB/s\n", et, W*H*sizeof(int)/(1024*et));
  check_kernel<<<1,1>>>(v, W, m, H);
  cudaDeviceSynchronize();
  // test col-major

  cudaEventRecord(start);
  expand_kernel<<<W, 256>>>(v, W, m, H, 1);
  cudaEventRecord(stop);
  cudaEventSynchronize(stop);
  cudaEventElapsedTime(&et, start, stop);
  printf("col-majortime: %fms, bandwidth: %.0fMB/s\n", et, W*H*sizeof(int)/(1024*et));
  check_kernel<<<1,1>>>(v, W, m, H, 1);
  cudaDeviceSynchronize();
  return 0;
}

$ nvcc -arch=sm_20 -o t546 t546.cu
$ ./t546
row-majortime: 13.066944ms, bandwidth: 80246MB/s
col-majortime: 12.806720ms, bandwidth: 81877MB/s
$ /usr/local/cuda/samples/bin/x86_64/linux/release/bandwidthTest
[CUDA Bandwidth Test] - Starting...
Running on...

 Device 0: Quadro 5000
 Quick Mode

 Host to Device Bandwidth, 1 Device(s)
 PINNED Memory Transfers
   Transfer Size (Bytes)        Bandwidth(MB/s)
   33554432                     5864.2

 Device to Host Bandwidth, 1 Device(s)
 PINNED Memory Transfers
   Transfer Size (Bytes)        Bandwidth(MB/s)
   33554432                     6333.1

 Device to Device Bandwidth, 1 Device(s)
 PINNED Memory Transfers
   Transfer Size (Bytes)        Bandwidth(MB/s)
   33554432                     88178.6

Result = PASS
$

CUDA 6.5,RHEL 5.5

这也可以使用CUBLAS Rank-1更新功能来实现,但是比上述方法要慢得多。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM