Я пытаюсь оценить скалярную функцию f (x), где x - k-мерный вектор (т. Е. F: R^k-> R). Во время оценки мне приходится выполнять много матричных операций: инверсию, умножение и поиск матричных детерминант и следы для матриц умеренных размеров (большинство из них меньше 30 х 30). Теперь я хочу оценить функцию на разных Xs одновременно, используя разные потоки на графическом процессоре. Вот почему мне нужно устройство api.Вычислить матричные детерминанты с помощью устройства cublas API
Я написал следующие коды для проверки вычислительных матричных детерминант с помощью API-интерфейса cublas, cublasSgetrfBatched, где я впервые нашел LU-разложение матрицы и вычислил произведение всех диагональных элементов в U-матрице. Я сделал это как для потока GPU, так и для CPU, используя результат, возвращаемый cublas. Но результат от GPU не имеет никакого смысла, в то время как результат на CPU правильный. Я использовал cuda-memcheck, но не обнаружил ошибок. Может кто-то помочь пролить свет на эту проблему? Большое спасибо.
cat test2.cu
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <cuda_runtime.h>
#include <cublas_v2.h>
__host__ __device__ unsigned int IDX(unsigned int i,unsigned int j,unsigned int ld){return j*ld+i;}
#define PERR(call) \
if (call) {\
fprintf(stderr, "%s:%d Error [%s] on "#call"\n", __FILE__, __LINE__,\
cudaGetErrorString(cudaGetLastError()));\
exit(1);\
}
#define ERRCHECK \
if (cudaPeekAtLastError()) { \
fprintf(stderr, "%s:%d Error [%s]\n", __FILE__, __LINE__,\
cudaGetErrorString(cudaGetLastError()));\
exit(1);\
}
__device__ float
det_kernel(float *a_copy,unsigned int *n,cublasHandle_t *hdl){
int *info = (int *)malloc(sizeof(int));info[0]=0;
int batch=1;int *p = (int *)malloc(*n*sizeof(int));
float **a = (float **)malloc(sizeof(float *));
*a = a_copy;
cublasStatus_t status=cublasSgetrfBatched(*hdl, *n, a, *n, p, info, batch);
unsigned int i1;
float res=1;
for(i1=0;i1<(*n);++i1)res*=a_copy[IDX(i1,i1,*n)];
return res;
}
__global__ void runtest(float *a_i,unsigned int n){
cublasHandle_t hdl;cublasCreate_v2(&hdl);
printf("det on GPU:%f\n",det_kernel(a_i,&n,&hdl));
cublasDestroy_v2(hdl);
}
int
main(int argc, char **argv)
{
float a[] = {
1, 2, 3,
0, 4, 5,
1, 0, 0};
cudaSetDevice(1);//GTX780Ti on my machine,0 for GTX1080
unsigned int n=3,nn=n*n;
printf("a is \n");
for (int i = 0; i < n; ++i){
for (int j = 0; j < n; j++) printf("%f, ",a[IDX(i,j,n)]);
printf("\n");}
float *a_d;
PERR(cudaMalloc((void **)&a_d, nn*sizeof(float)));
PERR(cudaMemcpy(a_d, a, nn*sizeof(float), cudaMemcpyHostToDevice));
runtest<<<1, 1>>>(a_d,n);
cudaDeviceSynchronize();
ERRCHECK;
PERR(cudaMemcpy(a, a_d, nn*sizeof(float), cudaMemcpyDeviceToHost));
float res=1;
for (int i = 0; i < n; ++i)res*=a[IDX(i,i,n)];
printf("det on CPU:%f\n",res);
}
nvcc -arch=sm_35 -rdc=true -o test test2.cu -lcublas_device -lcudadevrt
./test
a is
1.000000, 0.000000, 1.000000,
2.000000, 4.000000, 0.000000,
3.000000, 5.000000, 0.000000,
det on GPU:0.000000
det on CPU:-2.000000
Большое спасибо, Роберт. Ваше предложение работает и может дать ожидаемые результаты. У меня есть другая процедура устройства, которая сначала использует cublasSgetrfBatched для получения LU-декомпозиции, а затем cublasSgetriBatched, чтобы получить обратный результат LU. Между ними, нужно ли использовать cudaDeviceSynchronize()? Для малой матрицы (3 на 3) результаты кажутся одинаковыми. – Jack
Другой вопрос связан с освобождением памяти, созданной malloc в рутине устройства. Если я использую free() для освобождения памяти, cuda-memcheck выдает ошибки, когда код скомпилирован с -lcublas_device (отлично без него). У вас возникли какие-то идеи, чтобы обойти это? Любые последствия, если я не освобожу память? Большое спасибо. – Jack
Что касается первого вопроса, то cuda [семантика потока] (http://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#streams) применима и к операциям на стороне устройства. Запуски на стороне устройства, которые не указывают, что поток должен запускаться в поток по умолчанию (в потоке!), Что означает, что операция cublas устройства A, за которой следует операция cublas устройства B, выданная тем же потоком в один и тот же поток, должна сериализоваться. B не должен начинаться до завершения A. Что касается второго вопроса, мне нужно будет точно знать, где вы выполняете операцию free(). –