CUDA: почему доступ к одному и тому же массиву устройств не объединяется?

Я публикую детализированный код для обзора. Я считаю, что он должен компилироваться и выполняться без каких-либо проблем, но поскольку я исключил все ненужные части, возможно, я допустил какую-то ошибку.

struct Users {
    double A[96];
    double B[32];
    double C[32];
};

Это моя структура Users с массивами фиксированной длины. Ниже дана основная функция.

int main(int argc, char **argv) {

    int numUsers = 10;
    Users *users = new Users[numUsers];
    double Step[96];

    for (int i = 0; i < 32; i++) {
        Step[i]      = 0.8;
        Step[i + 32] = 0.8;
        Step[i + 64] = 0.8;
    }

    for (int usr = 0; usr < numUsers; usr++) {
        for (int i = 0; i < 32; i++) {
            users[usr].A[i]      = 10;
            users[usr].A[i + 32] = 20;
            users[usr].A[i + 64] = 30;
        }
        memset(users[usr].B, 0, sizeof(double) * 32);
        memset(users[usr].C, 0, sizeof(double) * 32);
    }


    double *d_Step;
    cudaMalloc((void**)&d_Step, sizeof(double) * 96);
    cudaMemcpy(d_Step, Step, sizeof(double) * 96, cudaMemcpyHostToDevice);


    Users *deviceUsers;
    cudaMalloc((void**)&deviceUsers, sizeof(Users) * numUsers);
    cudaMemcpy(deviceUsers, users, sizeof(Users) * numUsers, cudaMemcpyHostToDevice);


    dim3 grid;
    dim3 block;

    grid.x = 1;
    grid.y = 1;
    grid.z = 1;
    block.x = 32;
    block.y = 10;
    block.z = 1;
    calc<<<grid, block >>> (deviceUsers, d_Step, numUsers);

    delete users;
    return 0;
}

Обратите внимание, что массив Step представляет собой одномерный массив с 96 ячейками, и я охватываю 10 деформаций (32 потока в направлении x, и в моем блоке их 10). Каждая деформация будет обращаться к одному и тому же массиву шагов. Это можно увидеть ниже в ядре.

__global__ void calc(Users *users, double *Step, int numUsers) {

    int tId = threadIdx.x + blockIdx.x * blockDim.x;
    int uId = threadIdx.y;

    while (uId < numUsers) {

        double mean00 = users[uId].A[tId]      * Step[tId];
        double mean01 = users[uId].A[tId + 32] * Step[tId + 32];
        double mean02 = users[uId].A[tId + 64] * Step[tId + 64];

        users[uId].A[tId]      = (mean00 == 0? 0 : 1 / mean00);
        users[uId].A[tId + 32] = (mean01 == 0? 0 : 1 / mean01);
        users[uId].A[tId + 64] = (mean02 == 0? 0 : 1 / mean02);

        uId += 10;
    }
}

Теперь, когда я использую NVIDIA Visual Profiler, объединенные извлечения составляют 47%. Я дополнительно исследовал и обнаружил, что массив Step, к которому обращается каждая деформация, вызывает эту проблему. Если я заменю его какой-либо константой, доступ будет объединен на 100%.

Q1) Насколько я понимаю, объединенные доступы связаны с байтовой строкой, т.е. байтовые строки должны быть кратны 32, независимо от того, являются ли они целыми или двухбайтовыми строками. Почему я не получаю объединенный доступ?

Насколько мне известно, cuda всякий раз, когда назначает блок памяти в глобальной памяти устройства, ему назначается четный адрес. Таким образом, до тех пор, пока начальная точка + 32 местоположения доступны через варп, доступ должен быть объединен. Я прав?

Аппаратное обеспечение

Geforce GTX 470, вычислительные возможности 2.0


person Psypher    schedule 19.10.2013    source источник


Ответы (1)


Ваше ядро ​​прочитало Step 10 раз из глобальной памяти. Хотя кеш L1 может уменьшить фактический доступ к глобальной памяти, он все равно будет рассматриваться профилировщиком как неэффективный шаблон доступа.

Мой профилировщик называет это «глобальной эффективностью нагрузки». Он не говорит, слился он или нет.

person kangshiyin    schedule 19.10.2013
comment
Итак, если я правильно вас понимаю, доступы объединяются, но они неэффективны (потому что они считываются 10 раз из gmem). Поэтому мой другой вариант — загрузить его в общую память, что может повысить эффективность. Можно ли как-то определить, объединены ли доступы или нет. - person Psypher; 19.10.2013
comment
Да, ваш доступ объединен. Это должно быть легко определить. docs.nvidia. com/cuda/cuda-c-best-practices-guide/ - person kangshiyin; 19.10.2013
comment
Хороший ответ Эрик. Я проверил это, вставив if (uId == 0) ... перед умножением на Step[] в каждом из 3 экземпляров, чтобы ограничить нагрузку первым деформацией, и, конечно же, профайлер не сообщил о проблемах с глобальной эффективностью нагрузки. - person Robert Crovella; 20.10.2013