У меня есть ядро CUDA, где есть много операций и несколько ветвей. ПохожеНизкопроизводительное ядро
__global__
void kernel(Real *randomValues, Real mu, Real sigma)
{
int row = blockDim.y * blockIdx.y + threadIdx.y;
int col = blockDim.x * blockIdx.x + threadIdx.x;
if (row >= cnTimeSteps || col >= cnPaths) return;
Real alphaLevel = randomValues[row*cnPaths+col];
Real q = 0.0;
Real x = 0.0;
if (alphaLevel < p_low)
{
q = sqrt(-2*log(alphaLevel));
x = (((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q+c6)/((((d1*q+d2)*q+d3)*q+d4)*q+1);
}
else if (alphaLevel < p_high)
{
q = alphaLevel-0.5;
Real r = q*q;
x= (((((a1*r+a2)*r+a3)*r+a4)*r+a5)*r+a6)*q/(((((b1*r+b2)*r+b3)*r+b4)*r+b5)*r+1);
}
else
{
q = sqrt(-2*log(1.0-alphaLevel));
x = -(((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q+c6)/((((d1*q+d2)*q+d3)*q+d4)*q+1);
}
randomValues[row*cnPaths+col] = sigma * x + mu;
}
где все a
'ы, b
' ы, c
's и d
' s являются постоянными величинами (в устройстве постоянной памяти)
static __device__ __constant__ Real a1 = 1.73687;
static __device__ __constant__ Real a2 = 1.12321100;
и так далее.
После профилирования ядра я обнаружил, что теоретическое занятие составляет 100%, но я получаю не более 60%.
Я прошел через this и this GTC говорит, чтобы попытаться оптимизировать мое ядро.
С одной стороны у меня есть, что IPC сообщает в среднем 1,32 выданных инструкций и 0,62 выполненных. Сериализация инструкций составляет около 50%, но активность SM составляет почти 100%. С другой стороны, существует около 38 активных перекопов, но 8 имеют право выполнять следующую инструкцию, но при эффективности эмиссионного анализа я получаю, что около 70% циклов не имеют подходящей основы. Причины срыва указаны как «Прочие», которые, как я полагаю, связаны с вычислением log
и sqrt
.
- Как может активность SM быть 99,82%, если в большинстве циклов нет подходящей основы?
- Как я могу уменьшить стойло?
- Поскольку потоки в деформации не могут попасть в одну ветвь, запросы на постоянную память, вероятно, серализованы, это правда? Должен ли я помещать эти константы в глобальную память (возможно, также использовать разделяемую память)?
Впервые я использую Nsight Visual Studio, поэтому я пытаюсь понять смысл всего анализа производительности. Кстати, моя карточка Quadro K4000.
относительно вашего вопроса 3, я не вижу ничего плохого в использовании постоянной памяти. Это разумное применение постоянной памяти. Дивергенция warp является несвязанной проблемой и сама по себе не приводит к какой-либо «сериализации» доступа к постоянной памяти. Все потоки в деформации по определенному пути выполняются в режиме блокировки, и эти потоки будут обслуживаться одновременно заданным запросом постоянной памяти, по крайней мере, в коде, который вы здесь указали. –
(1) С точки зрения производительности, вероятно, лучше использовать литеральные константы вместо данных '__constant__'. (2) Код, кажется, вычисляет рациональные аппроксимации некоторой математической функции, и похоже, что эта функция может быть тесно связана с функцией ошибки или с CDF нормального распределения. Если да, рассмотрите возможность использования одной из функций erf(), erfc(), erfinv(), erfcinv(), normcdf(), normalcdfinv() CUDA. – njuffa
@ BRabbit27: Более пристальное изучение приближений выше настоятельно указывает на то, что они представляют собой одноточное приближение обратной функции кумулятивного распределения нормального распределения. CUDA имеет встроенную функцию для этого, normcdfinvf().Я хотел бы предложить, чтобы попытаться выяснить, может ли его использование помочь улучшить производительность этого кода. – njuffa