web-dev-qa-db-ja.com

cudamalloc()の使用。なぜダブルポインターなのですか?

現在、CUDAを学ぶために http://code.google.com/p/stanford-cs193g-sp2010/ のチュートリアルの例に目を通しています。 ___global___関数をデモするコードを以下に示します。 CPUに1つ、GPUに1つの2つのアレイを作成し、GPUアレイに番号7を入力し、GPUアレイデータをCPUアレイにコピーします。

_#include <stdlib.h>
#include <stdio.h>

__global__ void kernel(int *array)
{
  int index = blockIdx.x * blockDim.x + threadIdx.x;

  array[index] = 7;
}

int main(void)
{
  int num_elements = 256;

  int num_bytes = num_elements * sizeof(int);

  // pointers to Host & device arrays
  int *device_array = 0;
  int *Host_array = 0;

  // malloc a Host array
  Host_array = (int*)malloc(num_bytes);

  // cudaMalloc a device array
  cudaMalloc((void**)&device_array, num_bytes);

  int block_size = 128;
  int grid_size = num_elements / block_size;

  kernel<<<grid_size,block_size>>>(device_array);

  // download and inspect the result on the Host:
  cudaMemcpy(Host_array, device_array, num_bytes, cudaMemcpyDeviceToHost);

  // print out the result element by element
  for(int i=0; i < num_elements; ++i)
  {
    printf("%d ", Host_array[i]);
  }

  // deallocate memory
  free(Host_array);
  cudaFree(device_array);
} 
_

私の質問は、なぜ彼らがダブルポインターでcudaMalloc((void**)&device_array, num_bytes);ステートメントを記述したのですか? here のcudamalloc()の定義は、最初の引数がダブルポインターであることを示しています。

CPUでmalloc関数が行うように、GPUで割り当てられたメモリの先頭へのポインタを単に返さないのはなぜですか?

36
smilingbuddha

すべてのCUDA API関数はエラーコードを返します(エラーが発生しなかった場合はcudaSuccess)。他のすべてのパラメーターは参照渡しされます。ただし、プレーンCでは参照を使用できないため、戻り情報を保存する変数のアドレスを渡す必要があります。ポインターを返すため、ダブルポインターを渡す必要があります。

同じ理由でアドレスを操作するもう1つの有名な関数は、scanf関数です。これを書くのを何回忘れましたか_&値を保存する変数の前に? ;)

int i;
scanf("%d",&i);
20
CygnusX1

これは単純に恐ろしい、恐ろしいAPI設計です。抽象(void *)メモリを取得する割り当て関数にダブルポインタを渡す際の問題は、結果を保持するためにvoid *型の一時変数を作成し、それを実際のポインタに割り当てる必要があることです。使用したい正しいタイプの。 (void**)&device_arrayのようなキャストは無効なCであり、未定義の動作になります。次のように、通常のmallocのように動作し、ポインターを返すラッパー関数を単に作成する必要があります。

void *fixed_cudaMalloc(size_t len)
{
    void *p;
    if (cudaMalloc(&p, len) == success_code) return p;
    return 0;
}
18
R..

ポインターへのポインターであるため、ダブルポインターにキャストします。 GPUメモリのポインターを指す必要があります。 cudaMalloc()が行うことは、GPU上にメモリポインタ(スペース付き)を割り当て、それが最初の引数で指定されることです。

7
jwdmsd

C/C++では、malloc関数を呼び出すことにより、実行時にメモリブロックを動的に割り当てることができます。

int * h_array
h_array = malloc(sizeof(int))

malloc関数は、割り当てられたメモリブロックのアドレスを返します。このメモリブロックは、ある種のポインタの変数に格納できます。
CUDAのメモリ割り当ては、2つの点で少し異なります。

  1. cudamallocは、メモリブロックへのポインタの代わりにエラーコードとして整数を返します。
  2. 割り当てられるバイトサイズに加えて、cudamallocは最初のパラメーターとしてdouble voidポインターも必要とします。

    int * d_array cudamalloc((void **)&d_array、sizeof(int))

最初の違いの背後にある理由は、すべてのCUDA API関数が整数エラーコードを返すという規則に従っていることです。したがって、一貫性を保つために、cudamalloc AP​​Iも整数を返します。

関数の最初の引数は2つのステップで理解できるため、ダブルポインターの要件があります。

まず、cudamallocが整数値を返すように既に決めているため、割り当てられたメモリのアドレスを返すために使用できなくなりました。 Cでは、関数が通信する他の唯一の方法は、ポインターまたはアドレスを関数に渡すことです。この関数は、アドレスまたはポインターが指しているアドレスに格納されている値を変更できます。これらの値への変更は、同じメモリアドレスを使用して、関数スコープの外部で後で取得できます。

ダブルポインタの仕組み

次の図は、ダブルポインターでの動作を示しています。

int cudamalloc((void **) &d_array, int type_size) {
  *d_array = malloc(type_size)
  return return_code
}

enter image description here

なぜダブルポインターが必要なのですか?なぜこれが機能するのか

私は通常python world.

int cudamalloc((void *) d_array, int type_size) {
  d_array = malloc(type_size)
  ...
  return error_status
}

それではなぜ機能しないのでしょうか? Cでは、cudamallocが呼び出されると、d_arrayという名前のローカル変数が作成され、最初の関数引数の値が割り当てられます。関数のスコープ外のローカル変数の値を取得する方法はありません。ここで、ポインタへのポインタが必要な理由。

int cudamalloc((void *) d_array, int type_size) {
  *d_array = malloc(type_size)
  ...
  return return_code
}

enter image description here

2
Louis T

問題:2つの値を返す必要があります:戻りコードとメモリへのポインター(戻りコードが成功を示す場合)。そのため、そのうちの1つを戻り値の型へのポインタにする必要があります。また、戻り値の型として、intへの戻りポインター(エラーコードの場合)またはポインターへの戻りポインター(メモリアドレスの場合)を選択できます。ある解決策は他の解決策と同じです(そしてその1つはポインターへのポインターを生成します(double pointerの代わりにこの用語を使用することを好みます。数))。

Mallocには、エラーを示すためにnullポインタを持つことができるNiceプロパティがありますので、基本的に戻り値は1つだけ必要です。デバイスメモリへのポインタでこれが可能かどうかはわかりません。 noまたは間違ったnull値(これはCUDAおよび[〜#〜] not [〜#〜] Ansi Cであることに注意してください)。ホストシステムのヌルポインターがデバイスに使用されるヌルとはまったく異なる可能性があり、そのため、エラーを示すヌルポインターの戻りが機能しないため、この方法でAPIを作成する必要があります(これも意味します)両方のデバイスに共通のNULLがないこと)。

1
flolo