@ Очки Криса тоже очень важны, но больше зависят от самого алгоритма.
Обратитесь к руководству Cuda по выравниванию нитей для поиска в памяти. Массивы разделяемой памяти также должны быть кратны 16.
Использовать объединенные чтения глобальной памяти. Но при разработке алгоритма это часто так, и использование общей памяти помогает.
Не используйте атомарные операции в глобальной памяти или вообще, если это возможно. Они очень медленные. Некоторые алгоритмы, использующие атомарные операции, могут быть переписаны с использованием различных методов.
Без показанного кода никто не может сказать вам, что лучше и почему производительность меняется.
Количество потоков в блоке вашего ядра является наиболее важным значением.
Важными значениями для расчета этого значения являются:
- Максимальное количество резидентных потоков на один мультипроцессор
- Максимальное количество резидентных блоков на мультипроцессор
- Максимальное количество потоков в блоке
- Количество 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)
Хорошие советы по программированию:
- Проанализируйте код вашего ядра и запишите максимальное количество потоков, которые он может обработать, или сколько «единиц» он может обработать.
- Также выведите информацию об использовании регистра и попытайтесь снизить его до соответствующей целевой версии 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
*/