Результаты добавления 2D сетки и блока CUDA 0 [дублировать]

Java всегда проходит по значению, а не по ссылке

. Прежде всего, нам нужно понять, что проходит по значению и передает по ссылке.

Передача по значению означает, что вы делаете копию в памяти переданного значения фактического параметра. Это копия содержимого фактического параметра.

Pass by ссылка (также называемая pass by address) означает, что копия адреса фактического параметра сохраняется.

Иногда Java может дать иллюзию прохождения по ссылке. Давайте посмотрим, как это работает, используя следующий пример:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeValue(t);
        System.out.println(t.name);
    }

    public void changeValue(Test f) {
        f.name = "changevalue";
    }
}

class Test {
    String name;
}

Выход этой программы:

changevalue

Давайте поэтапно поймем :

Test t = new Test();

Как мы все знаем, он создаст объект в куче и вернет исходное значение обратно в t. Например, предположим, что значение t равно 0x100234 (мы не знаем фактическое внутреннее значение JVM, это просто пример).

first illustration [/g0]

new PassByValue().changeValue(t);

При переходе ссылочного т к функции, она непосредственно не передать фактическое значение задания теста объекта, но это создаст копию т, а затем передать его в функцию. Поскольку он передается по значению, он передает копию переменной, а не фактическую ссылку на нее. Поскольку мы сказали, что значение t было 0x100234, оба t и f будут иметь одинаковое значение и, следовательно, они будут указывать на один и тот же объект.

second illustration [/g1]

Если вы измените что-либо в функции с помощью ссылки f, оно изменит существующее содержимое объекта. Вот почему мы получили выход changevalue, который обновляется в функции.

Чтобы понять это более четко, рассмотрим следующий пример:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeRefence(t);
        System.out.println(t.name);
    }

    public void changeRefence(Test f) {
        f = null;
    }
}

class Test {
    String name;
}

Будет ли это NullPointerException? Нет, потому что он только передает копию справки. В случае прохождения по ссылке он мог бы выбросить NullPointerException, как показано ниже:

third illustration [/g2]

Надеюсь, это поможет.

79
задан Community 23 May 2017 в 10:31
поделиться

2 ответа

Блокировка обычно выбирается для максимизации «занятости». Поиск на CUDA Occupancy для получения дополнительной информации. В частности, см. Таблицу электронных калькуляций CUDA.

10
ответ дан Roger Dahl 16 August 2018 в 01:25
поделиться

В приведенных выше ответах указывается, как размер блока может повлиять на производительность и предложить общую эвристику для ее выбора, основанную на максимизации занятости. Не желая предоставлять критерий для выбора размера блока, стоит упомянуть, что CUDA 6.5 (теперь в версии Release Candidate) включает несколько новых функций времени выполнения, которые помогают вычислениям занятости и конфигурации запуска, см.

CUDA Pro Совет: API занятости упрощает конфигурацию запуска

. Одна из полезных функций - cudaOccupancyMaxPotentialBlockSize, которая эвристически вычисляет размер блока, который достигает максимальной занятости. Значения, предоставляемые этой функцией, могут быть затем использованы в качестве отправной точки для ручной оптимизации параметров запуска. Ниже приведен небольшой пример.

#include <stdio.h>

/************************/
/* TEST KERNEL FUNCTION */
/************************/
__global__ void MyKernel(int *a, int *b, int *c, int N) 
{ 
    int idx = threadIdx.x + blockIdx.x * blockDim.x; 

    if (idx < N) { c[idx] = a[idx] + b[idx]; } 
} 

/********/
/* MAIN */
/********/
void main() 
{ 
    const int N = 1000000;

    int blockSize;      // The launch configurator returned block size 
    int minGridSize;    // The minimum grid size needed to achieve the maximum occupancy for a full device launch 
    int gridSize;       // The actual grid size needed, based on input size 

    int* h_vec1 = (int*) malloc(N*sizeof(int));
    int* h_vec2 = (int*) malloc(N*sizeof(int));
    int* h_vec3 = (int*) malloc(N*sizeof(int));
    int* h_vec4 = (int*) malloc(N*sizeof(int));

    int* d_vec1; cudaMalloc((void**)&d_vec1, N*sizeof(int));
    int* d_vec2; cudaMalloc((void**)&d_vec2, N*sizeof(int));
    int* d_vec3; cudaMalloc((void**)&d_vec3, N*sizeof(int));

    for (int i=0; i<N; i++) {
        h_vec1[i] = 10;
        h_vec2[i] = 20;
        h_vec4[i] = h_vec1[i] + h_vec2[i];
    }

    cudaMemcpy(d_vec1, h_vec1, N*sizeof(int), cudaMemcpyHostToDevice);
    cudaMemcpy(d_vec2, h_vec2, N*sizeof(int), cudaMemcpyHostToDevice);

    float time;
    cudaEvent_t start, stop;
    cudaEventCreate(&start);
    cudaEventCreate(&stop);
    cudaEventRecord(start, 0);

    cudaOccupancyMaxPotentialBlockSize(&minGridSize, &blockSize, MyKernel, 0, N); 

    // Round up according to array size 
    gridSize = (N + blockSize - 1) / blockSize; 

    cudaEventRecord(stop, 0);
    cudaEventSynchronize(stop);
    cudaEventElapsedTime(&time, start, stop);
    printf("Occupancy calculator elapsed time:  %3.3f ms \n", time);

    cudaEventRecord(start, 0);

    MyKernel<<<gridSize, blockSize>>>(d_vec1, d_vec2, d_vec3, N); 

    cudaEventRecord(stop, 0);
    cudaEventSynchronize(stop);
    cudaEventElapsedTime(&time, start, stop);
    printf("Kernel elapsed time:  %3.3f ms \n", time);

    printf("Blocksize %i\n", blockSize);

    cudaMemcpy(h_vec3, d_vec3, N*sizeof(int), cudaMemcpyDeviceToHost);

    for (int i=0; i<N; i++) {
        if (h_vec3[i] != h_vec4[i]) { printf("Error at i = %i! Host = %i; Device = %i\n", i, h_vec4[i], h_vec3[i]); return; };
    }

    printf("Test passed\n");

}

EDIT

cudaOccupancyMaxPotentialBlockSize определен в файле cuda_runtime.h и определяется следующим образом:

template<class T>
__inline__ __host__ CUDART_DEVICE cudaError_t cudaOccupancyMaxPotentialBlockSize(
    int    *minGridSize,
    int    *blockSize,
    T       func,
    size_t  dynamicSMemSize = 0,
    int     blockSizeLimit = 0)
{
    return cudaOccupancyMaxPotentialBlockSizeVariableSMem(minGridSize, blockSize, func, __cudaOccupancyB2DHelper(dynamicSMemSize), blockSizeLimit);
}

Значения для параметров следующие

minGridSize     = Suggested min grid size to achieve a full machine launch.
blockSize       = Suggested block size to achieve maximum occupancy.
func            = Kernel function.
dynamicSMemSize = Size of dynamically allocated shared memory. Of course, it is known at runtime before any kernel launch. The size of the statically allocated shared memory is not needed as it is inferred by the properties of func.
blockSizeLimit  = Maximum size for each block. In the case of 1D kernels, it can coincide with the number of input elements.

. Обратите внимание, что с CUDA 6.5 необходимо вычислить собственные размеры 2D / 3D блока из размера блока 1D, предложенного API.

Обратите также внимание на то, что API-интерфейс драйвера CUDA содержит функционально эквивалентные API для расчета занятости, поэтому можно использовать cuOccupancyMaxPotentialBlockSize в коде API-интерфейса водителя так же, как показано для API-интерфейса времени выполнения в приведенном выше примере.

29
ответ дан talonmies 16 August 2018 в 01:25
поделиться
  • 1
    У меня есть два вопроса. Во-первых, когда нужно выбрать размер сетки как minGridSize по вычисляемому вручную gridSize. Во-вторых, вы упомянули, что «значения, предоставленные этой функцией, могут быть затем использованы в качестве отправной точки для ручной оптимизации параметров запуска. & Quot; - Вы имеете в виду, что параметры запуска по-прежнему необходимо оптимизировать вручную? – nurabha 26 May 2015 в 13:34
  • 2
    Есть ли какие-либо рекомендации относительно того, как рассчитать размеры блока 2D / 3D? В моем случае я ищу размеры 2D блока. Это просто случай вычисления коэффициентов х и у при умножении вместе дает исходный размер блока? – Graham Dawes 19 November 2015 в 14:56
  • 3
    @GrahamDawes это может представлять интерес. – Robert Crovella 1 December 2015 в 23:50
  • 4
    «Количество потоков в блоке должно быть круглым, кратным размеру основы». это не обязательно, но вы теряете ресурсы, если это не так. Я заметил, что cudaErrorInvalidValue возвращается cudaGetLastError после запуска ядра со слишком большим количеством блоков (похоже, что compute 2.0 не может обрабатывать 1 миллиард блоков, может вычислить 5.0), поэтому здесь есть ограничения. – masterxilo 21 March 2016 в 22:52
  • 5
    Ссылка Василия Волкова мертва. Я предполагаю, что вам понравилась его статья за сентябрь 2010 года: лучшая производительность при низком заполнении (в настоящее время она находится в nvidia.com/content/gtc-2010/pdfs/2238_gtc2010.pdf ). Здесь есть битбакет с кодом: bitbucket.org/rvuduc/volkov-gtc10 – ofer.sheffer 10 April 2017 в 09:04
Другие вопросы по тегам:

Похожие вопросы: