У меня есть приложение, в котором я распределяю вычислительную нагрузку между графическими процессорами в системе пользователя. По сути, для каждого графического процессора существует поток ЦП, который инициирует интервал обработки графического процессора при периодическом запуске основным потоком приложения.
Рассмотрим следующее изображение (созданное с помощью инструмента профилирования NVIDIA CUDA) в качестве примера интервала обработки GPU — здесь приложение использует один GPU.
Как видите, большая часть времени обработки графического процессора потребляется двумя операциями сортировки, и я использую для этого библиотеку Thrust (thrust::sort_by_key). Кроме того, похоже, что Thrust::sort_by_key вызывает несколько cudaMalloc под капотом, прежде чем он начнет фактическую сортировку.
Теперь рассмотрим тот же интервал обработки, когда приложение распределило вычислительную нагрузку между двумя графическими процессорами:
В идеальном мире вы ожидаете, что интервал обработки 2 GPU будет ровно вдвое меньше, чем у одного GPU (поскольку каждый GPU выполняет половину работы). Как вы можете видеть, это частично не так, потому что cudaMallocs, кажется, занимают больше времени, когда они вызываются одновременно (иногда в 2-3 раза дольше) из-за какой-то проблемы с конкуренцией. Я не понимаю, почему это должно быть так, потому что пространство выделения памяти для двух графических процессоров полностью независимо, поэтому не должно быть общесистемной блокировки на cudaMalloc - блокировка для каждого графического процессора была бы более разумной.
Чтобы доказать свою гипотезу о том, что проблема связана с одновременными вызовами cudaMalloc, я создал до смешного простую программу с двумя потоками ЦП (для каждого графического процессора), каждый из которых несколько раз вызывает cudaMalloc. Сначала я запустил эту программу, чтобы отдельные потоки не вызывали cudaMalloc одновременно:
Вы видите, что на выделение уходит ~ 175 микросекунд. Затем я запустил программу с одновременным вызовом потоков cudaMalloc:
Здесь каждый вызов занимал ~538 микросекунд или в 3 раза больше, чем в предыдущем случае! Излишне говорить, что это сильно замедляет работу моего приложения, и понятно, что проблема только усугубится при использовании более двух графических процессоров.
Я заметил такое поведение в Linux и Windows. В Linux я использую драйвер Nvidia версии 319.60, а в Windows — версию 327.23. Я использую набор инструментов CUDA 5.5.
Возможная причина: в этих тестах я использую GTX 690. Эта карта, по сути, представляет собой 2 графических процессора типа 680, размещенных в одном блоке. Это единственная установка с несколькими графическими процессорами, которую я запускал, поэтому, возможно, проблема с cudaMalloc как-то связана с некоторой аппаратной зависимостью между двумя графическими процессорами 690?