Мои два цента.
Вероятно, все это будет связано с задержкой связи между мультипроцессорами и памятью графического процессора.У вас есть код, для выполнения которого требуется около 30-50 тактов, и он генерирует как минимум 3 обращения к памяти, каждый из которых занимает более 200 тактов, если необходимые данные не находятся в кеше.
Использование текстурной памяти является хорошим способом решения этой проблемы, но это не обязательно оптимальный способ.
По крайней мере, попробуйте по 4 пикселя за раз (по горизонтали) на поток.Глобальная память может быть доступна по 128 байтов за раз (если у вас есть перекос, пытающийся получить доступ к любому байту с интервалом в 128 байтов, вы также можете использовать всю строку кэша практически без дополнительных затрат).Поскольку деформация состоит из 32 потоков, работа каждого потока на 4 пикселях должна быть эффективной.
Кроме того, вы хотите, чтобы вертикально смежные пиксели обрабатывались одним и тем же мультипроцессором.Причина в том, что смежные строки используют одни и те же входные данные.Если у вас есть пиксель (x = 0, y = 0), обработанный одним MP, а пиксель (x = 0, y = 1) обрабатывается другим MP, оба MP должны выполнить три глобальных запроса памяти каждый.Если они оба обрабатываются одним и тем же MP, и результаты должным образом кэшируются (неявно или явно), вам нужно всего четыре.Это можно сделать, если каждый поток будет работать с несколькими вертикальными пикселями, или с помощью blockDim.y> 1.
В более общем случае, вы, вероятно, захотите, чтобы каждый 32-ниточный деформирующий модуль загружал столько памяти, сколько выиметь доступ к MP (16-48 кбайт или, по крайней мере, блок 128x128), а затем обрабатывать все пиксели в этом окне.
На устройствах с совместимостью вычислений до версии 2.0 вы захотите использовать общую память,На устройствах с вычислительной совместимостью 2.0 и 2.1 возможности кэширования значительно улучшены, поэтому с глобальной памятью все в порядке.
Можно добиться некоторой нетривиальной экономии, если убедиться, что каждая деформация обращается только к двум строкам кэша в каждой горизонтальной строкевходные пиксели вместо трех, как это происходит в простой реализации, которая работает с 4 пикселями на поток, 32 потоками на деформацию.
Нет веских причин использовать float в качестве типа буфера.Вы не только в четыре раза увеличиваете пропускную способность памяти, но и код становится ненадежным и подверженным ошибкам.(Например, вы уверены, что if(neighbors == 3)
работает правильно, поскольку вы сравниваете число с плавающей точкой и целое число?) Используйте unsigned char.А еще лучше, используйте uint8_t и typedef, чтобы обозначать беззнаковый символ, если он не определен.
Наконец, не стоит недооценивать ценность экспериментов.Довольно часто производительность кода CUDA не может быть легко объяснена логикой, и вам приходится прибегать к настройке параметров и видеть, что происходит.