У меня есть приложение, в котором я разделяю нагрузку обработки между графическими процессорами в пользовательской системе. В принципе, на каждый графический процессор имеется поток ЦП, который инициирует цикл обработки GPU, когда он запускается периодически по основному потоку приложения.Плохая производительность при одновременном вызове cudaMalloc с двумя графическими процессорами
Рассмотрите следующее изображение (сгенерированное с использованием инструмента профилирования NVIDIA CUDA) для примера интервала обработки графического процессора - здесь приложение использует один графический процессор.
Как вы можете видеть, большая часть времени обработки GPU потребляется двух сортировочных операций, и я использую библиотеку Thrust для этого (тяги :: sort_by_key). Кроме того, похоже, что thrust :: sort_by_key вызывает несколько cudaMallocs под капотом, прежде чем он начнет фактический сортировку.
Теперь рассмотрим тот же интервал обработки, когда приложение распределения нагрузки обработки в течение двух графических процессоров:
В идеальном мире можно было бы ожидать интервал обработки 2 GPU, чтобы быть точно половину от один GPU (потому что каждый GPU выполняет половину работы). Как вы можете видеть, это не так, частично потому, что cudaMallocs, кажется, занимает больше времени, когда их называют одновременно (иногда в 2-3 раза больше) из-за какой-то вопрос раздора. Я не понимаю, почему это должно быть так, потому что пространство для выделения памяти для 2 графических процессоров полностью независимо, поэтому не должно быть общесистемной блокировки cudaMalloc - блокировка за один GPU была бы более разумной.
Чтобы доказать свою гипотезу, что проблема связана с одновременными вызовами cudaMalloc, я создал смехотворно простую программу с двумя потоками ЦП (для каждого графического процессора), каждый раз вызывающий cudaMalloc несколько раз. Я первый запустил эту программу так, что отдельные нити не называют cudaMalloc в то же время:
Вы видите это занимает ~ 175 микросекунд на распределение. Далее, я запустил программу с нити вызова cudaMalloc одновременно:
Здесь каждый вызов принял 538 ~ микросекунд или в 3 раза дольше, чем в предыдущем случае! Излишне говорить, что это значительно замедляет мое приложение, и разумно, что проблема будет только ухудшаться с более чем 2 графическими процессорами.
Я заметил это поведение в Linux и Windows. В Linux я использую драйвер Nvidia версии 319.60, а в Windows я использую версию 327.23. Я использую CUDA toolkit 5.5.
Возможная причина: Я использую GTX 690 в этих тестах. Эта карта в основном состоит из 6 680-подобных графических процессоров, размещенных в одном устройстве. Это единственная настройка «multi-GPU», которую я запускал, поэтому, возможно, проблема cudaMalloc связана с некоторой аппаратной зависимостью между 6 GPU 690?
Обычная рекомендация для высокопроизводительного кода заключается в том, чтобы получить операции malloc из любых циклов производительности. Я понимаю, что это не тривиальный вопрос, так как вы используете тягу.Существуют высокопроизводительные библиотеки сортировки, которые могут заменить push_by_key тяги, что позволит вам делать распределения раньше времени и повторно использовать их для операций сортировки. [CUB] (http://nvlabs.github.io/cub/), [b40c] (http://code.google.com/p/back40computing/) и [MGPU] (http: //nvlabs.github .io/moderngpu /) - все возможности. –
Да, я просмотрел CUB и b40c (сайт b40c говорит, что проект устарел). Прежде чем выполнить работу по удалению тяги, я хотел бы увидеть некоторые сравнительные графики между библиотеками. Не могли бы вы указать мне некоторые показатели производительности? Какую библиотеку вы рекомендуете? ... Похоже, что тяга не очень высокая производительность, например, я уже отключил кучу pushout: reduce и reduce_by_key с моими собственными ядрами - это сократило время обработки в два раза. Без шуток. – rmccabe3701
Thrust фактически основан на конкретном варианте b40c (или раньше был). Для эквивалентных тестовых случаев в моем тестировании между b40c и MGPU не было большой разницы. В одном тесте, который я запускал, я сортировал только около 22 бит 32-битного значения. У MGPU был циферблат, на который я мог повернуть только на 22 бит, и я заметил, что на 40% ускоряется эта тяга. Я не использовал CUB много. Если вы пробиваете эти ссылки, вы можете найти некоторые данные о производительности. Например, некоторые данные MGPU perf [здесь] (http://nvlabs.github.io/moderngpu/performance.html#performance) –