Я работаю над алгоритмом GPU, который должен выполнять множество модульных вычислений. В частности, различные операции над матрицами в конечном поле, которые в конечном счете (a * b - c * d) mod m или (a * b + c) mod m, где a, b, c и d - вычеты по модулю m, а m - 32-битное простое число.
Через эксперимент я узнал, что производительность алгоритма в основном ограничена медленной модульной арифметикой, потому что целочисленные по модулю (%) и операции деления не поддерживаются на графическом процессоре на оборудовании.
Я ценю, если кто-нибудь может дать мне представление о том, как реализовать эффективные модульные вычисления с CUDA?
Чтобы узнать, как это реализовано на CUDA, я использую следующий фрагмент кода:
__global__ void mod_kernel(unsigned *gout, const unsigned *gin) {
unsigned tid = threadIdx.x;
unsigned a = gin[tid], b = gin[tid * 2], m = gin[tid * 3];
typedef unsigned long long u64;
__syncthreads();
unsigned r = (unsigned)(((u64)a * (u64)b) % m);
__syncthreads();
gout[tid] = r;
}
Этот код не должен работать, я просто хотел посмотреть, как модульное сокращение реализованной на CUDA.
Когда я разбираю это с помощью cuobjdump --dump-sass (спасибо njuffa за советы!), я вижу следующее:
/*0098*/ /*0xffffdc0450ee0000*/ BAR.RED.POPC RZ, RZ;
/*00a0*/ /*0x1c315c4350000000*/ IMUL.U32.U32.HI R5, R3, R7;
/*00a8*/ /*0x1c311c0350000000*/ IMUL.U32.U32 R4, R3, R7;
/*00b0*/ /*0xfc01dde428000000*/ MOV R7, RZ;
/*00b8*/ /*0xe001000750000000*/ CAL 0xf8;
/*00c0*/ /*0x00000007d0000000*/ BPT.DRAIN 0x0;
/*00c8*/ /*0xffffdc0450ee0000*/ BAR.RED.POPC RZ, RZ;
Обратите внимание, что между двумя вызовами на bar.red.popc есть вызов процедуры 0xf8, которая реализует некоторый сложный алгоритм (около 50 инструкций или даже больше). Не сдерживая, что мода (%) работает медленно