CUDA - блоки и темы - PullRequest
       3

CUDA - блоки и темы

0 голосов
/ 18 февраля 2012

Я реализовал алгоритм сопоставления строк в графическом процессоре. Время поиска параллельной версии значительно уменьшено по сравнению с последовательной версией алгоритма, но при использовании различного количества блоков и потоков я получаю разные результаты. Как определить количество блоков и блоков, чтобы получить наилучшие результаты?

Ответы [ 2 ]

1 голос
/ 22 февраля 2012

@ Очки Криса тоже очень важны, но больше зависят от самого алгоритма.

  1. Обратитесь к руководству Cuda по выравниванию нитей для поиска в памяти. Массивы разделяемой памяти также должны быть кратны 16.

  2. Использовать объединенные чтения глобальной памяти. Но при разработке алгоритма это часто так, и использование общей памяти помогает.

  3. Не используйте атомарные операции в глобальной памяти или вообще, если это возможно. Они очень медленные. Некоторые алгоритмы, использующие атомарные операции, могут быть переписаны с использованием различных методов.

Без показанного кода никто не может сказать вам, что лучше и почему производительность меняется.

Количество потоков в блоке вашего ядра является наиболее важным значением.

Важными значениями для расчета этого значения являются:

  • Максимальное количество резидентных потоков на один мультипроцессор
  • Максимальное количество резидентных блоков на мультипроцессор
  • Максимальное количество потоков в блоке
  • Количество 32-битных регистров на мультипроцессор

Ваши алгоритмы должны быть масштабируемыми для всех графических процессоров, достигая 100% загрузки. Для этого я создал вспомогательный класс, который автоматически определяет лучшие номера потоков для используемого графического процессора и передает его ядру как DEFINE.

/**
 * Number of Threads in a Block
 *
 * Maximum number of resident blocks per multiprocessor : 8
 *
 * ///////////////////
 * Compute capability:
 * ///////////////////
 *
 * Cuda [1.0 - 1.1] =   
 *  Maximum number of resident threads per multiprocessor 768
 *  Optimal Usage: 768 / 8 = 96
 * Cuda [1.2 - 1.3] =
 *  Maximum number of resident threads per multiprocessor 1024
 *  Optimal Usage: 1024 / 8 = 128
 * Cuda [2.x] =
 *  Maximum number of resident threads per multiprocessor 1536
 *  Optimal Usage: 1536 / 8 = 192
 */ 
public static int BLOCK_SIZE_DEF = 96;

Пример Cuda 1.1 для достижения 786 резидентных потоков на SM

  • 8 блоков * 96 потоков на блок = 786 потоков
  • 3 блока * 256 потоков на блок = 786 потоков
  • 1 блоков * 512 потоков на блок = 512 потоков <- 33% графического процессора будут простаивать </li>

Это также упоминается в книге:

Программирование массивно-параллельных процессоров: практический подход (приложения серии GPU Computing)

Хорошие советы по программированию:

  1. Проанализируйте код вашего ядра и запишите максимальное количество потоков, которые он может обработать, или сколько «единиц» он может обработать.
  2. Также выведите информацию об использовании регистра и попытайтесь снизить его до соответствующей целевой версии CUDA. Потому что, если вы используете слишком много регистров в вашем ядре, будет выполнено меньше блоков, что приведет к меньшей загруженности и производительности.
    Пример: Используя Cuda 1.1 и используя оптимальное количество 768 резидентных потоков на SM, вы можете использовать 8192 регистра. Это приводит к 8192/768 = 10 максимальным регистрам на поток / ядро. Если вы используете 11, графический процессор будет использовать на 1 блок меньше, что приведет к снижению производительности.

Пример: мое независимое от матрицы ядро ​​нормализует вектор строки.

/*
 * ////////////////////////
 * // Compute capability //
 * ////////////////////////
 *
 * Used 12 registers, 540+16 bytes smem, 36 bytes cmem[1]
 * Used 10 registers, 540+16 bytes smem, 36 bytes cmem[1] <-- with -maxregcount 10 Limit for Cuda 1.1
 * I:   Maximum number of Rows = max(x-dim)^max(dimGrid)
 * II:  Maximum number of Columns = unlimited, since they are loaded in a tile loop
 *
 * Cuda [1.0 - 1.3]: 
 * I:   65535^2 = 4.294.836.225
 *
 * Cuda [2.0]:
 * II:  65535^3 = 281.462.092.005.375
 */
1 голос
/ 18 февраля 2012

Я думаю, что на этот вопрос трудно, если не невозможно, ответить по той причине, что он действительно зависит от алгоритма и того, как он работает. Так как я не могу видеть вашу реализацию, я могу дать вам некоторые выводы:

  1. Не используйте глобальную память и проверьте, как можно максимально использовать общую память. Как правило, вы получаете представление о том, как потоки обращаются к памяти, как извлекаются данные и т. Д.

  2. Поймите, как работают ваши варпы. Иногда потоки в деформации могут ожидать завершения других потоков, если у вас есть соответствие 1: 1 между потоком и данными. Таким образом, вместо сопоставления 1: 1 можно сопоставить потоки с несколькими данными, чтобы они были заняты.

  3. Поскольку блоки состоят из потоков, сгруппированных в деформации 32 потоков, лучше всего, если число потоков в блоке кратно 32, так что вы не получите деформации, состоящие из 3 потоков и т. Д.

  4. Избегайте расходящихся путей в перекосах.

Надеюсь, это немного поможет.

...