Como descobri da maneira mais difícil, você não pode simplesmente enumerar dispositivos e, em seguida, chamar cudaSetDevice (). CudaSetDevice () sempre terá êxito se o dispositivo estiver presente e você não tiver criado um contexto. A solução que eu trabalhei aqui com algumas dicas de NVidians é usar nvidia-smi para definir o modo de computação em todas as GPUs para processar exclusivo e, em seguida, filtrar dispositivos que não podem ser usados para sua tarefa com cudaSetValidDevices (), finalmente fazendo uma chamada para cudaFree () para forçar o driver CUDA a criar um contexto em um dispositivo disponível.
Se a chamada para cudaFree falhar, não há dispositivos disponíveis:
// Let CUDA select any device from this list of device IDs filtered by your
// own criteria (not shown)
status = cudaSetValidDevices(pGPUList, nGpus);
if (status != cudaSuccess)
{
printf(("Error searching for compatible GPU\n");
exit(-1);
}
// Trick driver into creating a context on an available and valid GPU
status = cudaFree(0);
if (status != cudaSuccess)
{
printf("Error selecting compatible GPU\n");
exit(-1);
}
// Get device selected by driver
status = cudaGetDevice(&device);
if (status != cudaSuccess)
{
printf("Error fetching current GPU\n");
exit(-1);
}
// Your amazing CUDA program goes here...
Nota: se as GPUs não estiverem no modo exclusivo, você precisará gerenciá-las explicitamente de seu sistema de enfileiramento de alguma forma. O método descrito aqui permitiria o uso de um recurso consumível para monitorar todas as tarefas em um nó para garantir que elas nunca solicitassem mais GPUs do que as disponíveis nele e, em seguida, explorasse o modo exclusivo para evitar colisões.