Tenho definido um novo tipo de dados como este no lado GPU (CUDA):
typedef union {
int i;
double d;
long l;
char s[16];
} data_unit;
data_unit *d_array;
E em Java, temos um conjunto de um dos tipos disponíveis na união definido. Normalmente, podemos fazer o seguinte em Java (JCuda) se temos uma matriz do tipo int por exemplo:
import static jcuda.driver.JCudaDriver.*;
int data_size;
CUdeviceptr d_array;
int[] h_array = new int[data_size];
cuMemAlloc(d_array, data_size * Sizeof.INT);
cuMemcpyHtoD(d_array, Pointer.to(h_array), data_size * Sizeof.INT);
Mas como isso pode ser feito se houver um conjunto de dispositivo que seu tipo é a nossa união? (Supor que ainda o h_array é do tipo int)
int data_size;
CUdeviceptr d_array;
int[] h_array = new int[data_size];
cuMemAlloc(d_array, data_size * Sizeof.?);
// Here we should have some type of alignment (?)
cuMemcpyHtoD(d_array, Pointer.to(h_array), data_size * Sizeof.?);
Eu acredito que há um mal-entendido fundamental do que uma união é.
Vamos pensar sobre isso. O que torna uma pessoa diferente união de um struct? Ele pode armazenar diferentes tipos de dados em momentos diferentes.
Como ele se realizar esta façanha? Bem pode-se usar algum tipo de variável separada para dinamicamente especificar o tipo ou a quantidade de memória que ocupa, mas não a União não fizer isso, ele conta com o programador saber exatamente o tipo que deseja recuperar e quando. Assim, a única alternativa, se o tipo só é realmente conhecido pelo programador em qualquer ponto no tempo, é simplesmente certificar-se de que existe espaço suficiente alocado para a variável de união que sempre se pode usá-lo para que seja o tipo.
Na verdade, é isso que a união faz, veja aqui (sim, eu sei que é C / C ++, mas isso também se aplica a CUDA também). O que isso significa para você? Isso significa que o tamanho de sua matriz união deve ser o tamanho de seu principal membro x o número de elementos, como o tamanho de uma união é o tamanho de seu principal membro.
Vamos olhar para o seu sindicato para ver como descobrir isso.
typedef union {
int i;
double d;
long l;
char s[16];
} data_unit;
Seu sindicato tem:
int i
, Que supomos ser 4 bytesdouble d
, Que é de 8 byteslong l
, O que é confuso, porque dependendo do compilador / plataforma pode ser 4 ou 8 bytes, assumimos 8 bytes para agora.char s[16]
, fácil, 16 bytes
Assim, o maior número de bytes qualquer membro ocupa é a sua char s[16]
variável, 16 bytes. Isso significa que você terá que alterar seu código para:
int data_size;
int union_size = 16;
CUdeviceptr d_array;
// copying this to the device will not result in what you expect with out over allocating
// if you just copy over integers, which occupy 4 bytes each, your integers will fill less space than the number of unions
// we need to make sure that there is a "stride" here if we want to actually copy real data from host to device.
// union_size / Sizeof.INT = 4, so there will be 4 x as many ints, 4 for each union.
int[] h_array = new int[data_size * (union_size / Sizeof.INT)];
// here we aren't looking for size of int to allocate, but the size of our union.
cuMemAlloc(d_array, data_size * union_size);
// we are copying, again, data_size * union_size bytes
cuMemcpyHtoD(d_array, Pointer.to(h_array), data_size * union_size);
NOTA
Se você deseja copiar ints longo, isso basicamente significa que você terá de atribuir a cada 4 int à int real que você deseja para esse índice.
int 0 é h_array[0]
, int 1 é h_array[4]
int 2 é h_array[8]
int n é h_array[n * 4]
etc ..