Могу ли я запускать приложения без MPI CUDA одновременно на графических процессорах NVIDIA Kepler с MPS? Я хотел бы сделать это, потому что мои приложения не могут полностью использовать GPU, поэтому я хочу, чтобы они совместно работали вместе. Есть ли какой-нибудь пример кода для этого?Как использовать многопроцессорную службу Nvidia (MPS) для запуска нескольких не-MPI-приложений CUDA?
ответ
Необходимые инструкции содержатся в documentation для службы MPS. Вы заметите, что эти инструкции на самом деле не зависят от MPI или вызывают их, поэтому на самом деле нет ничего особенного в MPI.
Вот пошаговое руководство/пример.
Прочитайте раздел 2.3 вышеупомянутой документации для различных требований и ограничений. Для этого я рекомендую использовать CUDA 7, 7.5 или более поздней версии. Были некоторые различия в конфигурации с предыдущими версиями CUDA MPS, которые я не буду здесь описывать. Кроме того, я продемонстрирую использование только одного сервера/одного GPU. Машина, которую я использую для тестирования, представляет собой узел CentOS 6.2 с использованием графического процессора K40c (cc3.5/Kepler) с CUDA 7.0. В узле есть другие графические процессоры. В моем случае заказ перечисления CUDA помещает мой K40c на устройство 0, но порядок перечисления nvidia-smi, по-видимому, помещает его как id 2 в порядке. Все эти детали имеют значение в системе с несколькими графическими процессорами, что влияет на приведенные ниже сценарии.
Я создам несколько вспомогательных скриптов bash, а также тестовое приложение. Для тестового приложения нам нужно что-то с ядром (ядрами), которое, очевидно, может работать одновременно с ядрами из других экземпляров приложения, и нам также хотелось бы что-то, что делает его очевидным, когда эти ядра (из отдельных приложений/процессов) выполняются одновременно или нет. Чтобы удовлетворить эти потребности в демонстрационных целях, давайте иметь приложение, в котором есть ядро, которое работает только в одном потоке на одном SM, и просто ждет некоторое время (мы будем использовать ~ 5 секунд), прежде чем выходить и печатать сообщение. Вот тест приложение, которое делает это:
$ cat t1034.cu #include <stdio.h> #include <stdlib.h> #define MAX_DELAY 30 #define cudaCheckErrors(msg) \ do { \ cudaError_t __err = cudaGetLastError(); \ if (__err != cudaSuccess) { \ fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \ msg, cudaGetErrorString(__err), \ __FILE__, __LINE__); \ fprintf(stderr, "*** FAILED - ABORTING\n"); \ exit(1); \ } \ } while (0) #include <time.h> #include <sys/time.h> #define USECPSEC 1000000ULL unsigned long long dtime_usec(unsigned long long start){ timeval tv; gettimeofday(&tv, 0); return ((tv.tv_sec*USECPSEC)+tv.tv_usec)-start; } #define APPRX_CLKS_PER_SEC 1000000000ULL __global__ void delay_kernel(unsigned seconds){ unsigned long long dt = clock64(); while (clock64() < (dt + (seconds*APPRX_CLKS_PER_SEC))); } int main(int argc, char *argv[]){ unsigned delay_t = 5; // seconds, approximately unsigned delay_t_r; if (argc > 1) delay_t_r = atoi(argv[1]); if ((delay_t_r > 0) && (delay_t_r < MAX_DELAY)) delay_t = delay_t_r; unsigned long long difft = dtime_usec(0); delay_kernel<<<1,1>>>(delay_t); cudaDeviceSynchronize(); cudaCheckErrors("kernel fail"); difft = dtime_usec(difft); printf("kernel duration: %fs\n", difft/(float)USECPSEC); return 0; } $ nvcc -arch=sm_35 -o t1034 t1034.cu $ ./t1034 kernel duration: 6.528574s $
Мы будем использовать Баш скрипт для запуска сервера MPS:
$ cat start_as_root.bash #!/bin/bash # the following must be performed with root privilege export CUDA_VISIBLE_DEVICES="0" nvidia-smi -i 2 -c EXCLUSIVE_PROCESS nvidia-cuda-mps-control -d $
И Баш скрипт для запуска 2 копии нашего тестового приложения " одновременно ":
$ cat mps_run #!/bin/bash ./t1034 & ./t1034 $
Мы могли бы также иметь Баш скрипт, чтобы выключить сервер, хотя это не требуется для этого руководства:
$ cat stop_as_root.bash #!/bin/bash echo quit | nvidia-cuda-mps-control nvidia-smi -i 2 -c DEFAULT $
Теперь, когда мы просто запустить наше тестовое приложение с помощью
mps_run
сценария выше, но фактически не позволяет серверу MPS, мы получаем ожидаемое поведение, один экземпляр приложения берет ожидаемый ~ 5 секунд, в то время как другой экземпляр занимает примерно вдвое больше (~ 10 секунд), поскольку, поскольку он не запускается одновременно с приложением из другого процесса, он ждет 5 секунд, пока работает другое приложение/ядро, а затем тратит 5 секунд на свое собственное ядро, в общей сложности ~ 10 секунд:$ ./mps_run kernel duration: 6.409399s kernel duration: 12.078304s $
с другой стороны, если мы начнем сервер MPS первый, и повторите тест:
$ su Password: # ./start_as_root.bash Set compute mode to EXCLUSIVE_PROCESS for GPU 0000:82:00.0. All done. # exit exit $ ./mps_run kernel duration: 6.167079s kernel duration: 6.263062s $
мы видим, что оба приложения принимают одинаковое количество времени, чтобы бежать, потому что ядра работают одновременно, в связи с МПС.
Вы можете экспериментировать по своему усмотрению. Если эта последовательность работает правильно для вас, но запуск собственного приложения, похоже, не дает ожидаемых результатов, одна из возможных причин может заключаться в том, что ваши приложения/ядра не могут одновременно работать с другими экземплярами приложения/ядер, к построению ваших ядер, а не к MPS. Вы можете проверить the requirements for concurrent kernels и/или изучить concurrentKernels sample app.
Значительная часть информации здесь была переработана из теста/работы here, хотя презентация здесь с отдельными приложениями отличается от представленного там случая MPI.