Java 是否可以从JCuda向定义为Union的GPU内存发送数据?

Java 是否可以从JCuda向定义为Union的GPU内存发送数据?,java,cuda,unions,jcuda,Java,Cuda,Unions,Jcuda,我在GPU端(cuda)定义了一种新的数据类型: 在Java中,我们有一个在定义的联合中可用的数组。通常,如果我们有一个int类型的数组,我们可以在Java(JCuda)中执行以下操作,例如: import static jcuda.driver.JCudaDriver.*; int data_size; CUdeviceptr d_array; int[] h_array = new int[data_size]; cuMemAlloc(d_array, data_size * Size

我在GPU端(cuda)定义了一种新的数据类型:

在Java中,我们有一个在定义的联合中可用的数组。通常,如果我们有一个int类型的数组,我们可以在Java(JCuda)中执行以下操作,例如:

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);
但是,如果设备上有一个数组,它的类型是我们的union,该怎么做呢?(假设h_数组仍然是int类型)


我认为人们对什么是工会存在着根本性的误解

让我们考虑一下。是什么使联合不同于结构?它可以在不同的时间存储不同类型的数据

它是如何完成这一壮举的?我们可以使用某种单独的变量来动态地指定类型或者它占用的内存量,但是联合并不能做到这一点,它依赖于程序员确切地知道他们想要检索什么类型以及何时检索。因此,如果在任何给定的时间点上,程序员实际上只知道类型,那么唯一的替代方法就是确保为union变量分配足够的空间,以便始终可以将其用于任何类型

事实上,这就是工会所做的,请看(是的,我知道它是C/C++,但这也适用于CUDA)。这对你意味着什么?这意味着联合数组的大小应该是其最大成员的大小x元素数,因为联合数组的大小是其最大成员的大小

让我们看看你们的工会,看看如何解决这个问题

typedef union {
    int i;
    double d;
    long l;
    char s[16];
} data_unit;
你的工会:

  • inti
    ,我们假设为4字节
  • 双d
    ,即8字节
  • long l
    ,这很令人困惑,因为根据编译器/平台的不同,可以是4字节或8字节,我们现在假设为8字节
  • chars[16]
    ,简单,16字节
因此,任何成员占用的最大字节数是
chars[16]
变量,16字节。这意味着您需要将代码更改为:

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);
注 如果要复制整数,这基本上意味着您需要将每4个整数分配给该索引所需的实际整数


int 0是
h_数组[0]
,int 1是
h_数组[4]
int 2是
h_数组[8]
int n是
h_数组[n*4]
等等。

我用一点脏代码进行了对齐和填充。 另外,注意编译器之间的字节顺序差异也很重要。Java似乎以BIG_ENDIAN格式存储字节。所以在这里,我不得不把它改成LITTLE_ENDIAN来完成它。调试花了我两个小时。 这就是它现在的样子:

int data_size;
int union_size = 16;
// Device Array
CUdeviceptr d_array; 
// Host Array
int[] h_array = new int[data_size];
byte[] h_array_bytes = new byte[data_size * union_size];

// Data allocation on GPU memory
cuMemAlloc(d_array, data_size * union_size);

// Alignment and padding
byte[] tempBytes;

for(int i = 0; i < data_size; i++){
    tempBytes = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN)
                .putInteger(h_array[i]).array();
    int start = i * union_size;
    for(int j = start, k = 0; k < union_size; k++, j++){
        if(k < tempBytes.length){
            h_array_bytes[j] = tempBytes[k];
        } else {
            h_array_bytes[j] = 0;
        }
    }
}
// And then simply do the copy 
cuMemcpyHtoD(d_array, Pointer.to(h_array_bytes), data_size * union_size);
int数据大小;
int union_size=16;
//设备阵列
CUdeviceptr d_阵列;
//主机阵列
int[]h_数组=新int[数据大小];
字节[]h_数组_字节=新字节[数据大小*联合大小];
//GPU内存上的数据分配
CumeAlloc(数据数组,数据大小*联合大小);
//对齐和填充
字节[]临时字节;
对于(int i=0;i
谢谢,Opa,我已经知道什么是Union以及它是如何存储数据的。我在寻找自动对齐和填充。然而,我已经实现了这个想法,经过一点调整,它成功了。我将把代码作为另一个答案。
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);
int data_size;
int union_size = 16;
// Device Array
CUdeviceptr d_array; 
// Host Array
int[] h_array = new int[data_size];
byte[] h_array_bytes = new byte[data_size * union_size];

// Data allocation on GPU memory
cuMemAlloc(d_array, data_size * union_size);

// Alignment and padding
byte[] tempBytes;

for(int i = 0; i < data_size; i++){
    tempBytes = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN)
                .putInteger(h_array[i]).array();
    int start = i * union_size;
    for(int j = start, k = 0; k < union_size; k++, j++){
        if(k < tempBytes.length){
            h_array_bytes[j] = tempBytes[k];
        } else {
            h_array_bytes[j] = 0;
        }
    }
}
// And then simply do the copy 
cuMemcpyHtoD(d_array, Pointer.to(h_array_bytes), data_size * union_size);