Я закодировал два простых ядра. Каждый из них добавляет два вектора типов int (32-бит)/long int (64 бит). Оказывается, на моем GPU (Tesla K80), который оказался довольно новым и хорошим, ядра всего лишь 32-битные.
Время примерно удваивается по мере увеличения размера вектора.
Ядра заключаются в следующем:
__global__ void add_32(int * c, int * a, int * b)
{
int gid = blockIdx.x * blockDim.x + threadIdx.x;
c[gid] = a[gid] + b[gid];
}
typedef long int int64;
__global__ void add_64(int64 * c, int64 * a, int64 * b)
{
int gid = blockIdx.x * blockDim.x + threadIdx.x;
c[gid] = a[gid] + b[gid];
}
Когда вектор размер 1 Мега элемент, add_32 занимает около 102,911 микросекунд, в то время как add_64 принимает 192.669 мксек. (Время выполнения сообщалось с использованием профайлера Nvidia при запуске двоичного файла режима выпуска).
Кажется, что 64-разрядные инструкции просто эмулируются с помощью 32-битных инструкций!
Это может быть решение грубой силы, чтобы выяснить, какие машины являются ядрами GPU, но определенно не изящными.
Update:
Благодаря @Paul A. Clayton комментарий, кажется, что решение выше не справедливое сравнение, как размер данных удваивается в 64-битном случае. Поэтому мы не должны запускать оба ядра с одинаковым количеством элементов. Правильным принципом было бы запустить 64-битную версию с половиной числа элементов.
Чтобы быть более уверенным, рассмотрим элементное умножение векторов вместо добавления. Если GPU эмулирует 64-битные инструкции с помощью 32-битных инструкций, ему требуется как минимум 3 32-битных инструкций умножения для умножения 2 64-разрядных чисел, используя, например, алгоритм Карацубы. Это означает, что если мы запустим 64-битное ядро умножения векторов с N/2 элементами, это займет больше времени, чем 32-разрядное ядро с N элементами, если бы 64-битные умножения были просто эмулированы.
Вот ядро:
__global__ void mul_32(int * c, int * a, int * b)
{
int gid = blockIdx.x * blockDim.x + threadIdx.x;
c[gid] = a[gid] * b[gid];
}
typedef long int int64;
__global__ void mul_64(int64 * c, int64 * a, int64 * b)
{
int gid = blockIdx.x * blockDim.x + threadIdx.x;
c[gid] = a[gid] * b[gid];
}
А вот детали эксперимента: представленные здесь времен от NVidia профилировщика на высвобождение двоичного режима: 1 Ядро mul_32 с векторным размером N = 256 Mega элементы , занимает 25,608 миллисекунды. 2- Ядро mul_64 с векторным размером N = 128 мега элементов, занимает 24,153 миллисекунда.
Я знаю, что оба ядра производят неверные результаты, но я думаю, что это не имеет никакого отношения к тому, как выполняется вычисление.
Я не очень хорошо знаком с графическими процессорами Nvidia, но такая информация должна быть в технических паспортах или других руководствах. Если в публике нет информации, вам, вероятно, понадобится NDA с Nvidia, чтобы получить эту информацию. Итак, у вас есть доступ к документации по графическому процессору, на который вы нацеливаетесь? – fsasm
Спецификации GPU не отображают эту информацию. Я думаю, что должен быть API/команда, которая может рассказать такую информацию !!! – caesar
В общих паспортах данных должна отображаться такая информация, поскольку это их цель. Если поставщик не публикует информацию, вам это не нужно. Драйвер вместе с PTX скрывает все детали аппаратного обеспечения, чтобы увеличить переносимость. Если вам действительно нужна эта информация, вы должны связаться с Nvidia. – fsasm