Проблема в том, что cublas и т. Д. Предназначены для использования всех SM для умножения больших матриц. Это не то, что вы хотите; Вы хотите сделать много маленьких матричных умножений.
Может быть, есть какой-то способ превратить это во что-то, что CUBLAS может сделать для вас хорошо, но я этого не вижу. Мое предложение будет следующим:
Напишите ядро, которое использует один блок потока для умножения двух ваших маленьких матриц, и выведите результат.
Затем запустите журнал ядра 2 N с тоннами блоков и выполните умножение попарно:
- Шаг 1: умножить M 1 x M 2 , M 3 x M 4 ... M N - 2 x M N-1 , вывод M ' 1 , M' 2 .. M ' N / 2
- Шаг 2: умножить M ' 1 x M' 2 , M ' 3 x M' 4 ... M ' N / 2 - 2 x M N / 2-1 , с выводом M' ' 1 , M' ' 2 .. M '' N / 4 ...
и т.д.
Там будет коэффициент загрузки памяти в 50%, но я думаю, что вы будете лучше использовать ваши ядра таким образом.
Обновление
Хорошо, если вы действительно не хотите делать это поэтапно, вы можете сделать это таким образом, но это потребует большего количества кодирования, и производительность, вероятно, будет хуже по сравнению с тем, что вы могли бы получить с чем-то вроде cuBLAS и асинхронным перечислить. Я предполагаю, что вы используете Fermi, и вы отключили кэш L1, чтобы у вас было 48 Кбайт памяти на SM.
Сохраните 100 матриц в виде блоков 2x2, каждый блок должен быть непрерывным в памяти. Итак, matrix[matrixnum,i,j]
начинается с matricies[matrixnum*100*100 + i*100*50 + j*50*50]
. Обратите внимание, что каждый блок имеет размер 50 * 50 * 4 байта ~ 10 КБ, поэтому 4 удобно помещаются в общей памяти.
Назначьте для каждого 4-х потоковых блоков (Nmatricies / Nblocks) длинную цепочку матриц для умножения, причем один из четырех отвечает за каждый блок умножения.
Допустим, вы являетесь блоком потоков 1 из 4, и первой из умножаемых матриц является AxB. Вы несете ответственность за (1,1) результата - (AB) 1,1 = A 1,1 B 1,1 + A * +1075 * 1,2 * 1 076 ** В 2,1 * +1078 *. Вы предварительно загружаете A 1,1 в myblock [0] в разделяемой памяти.
- загрузить в myblock [1] = B 1,1 из глобальной памяти
- myblock [3] = myblock [0] * myblock [1] (матричный мульт, все в общей памяти)
- загрузить в myblock [1] = A 1,2 от глобальных
- загрузить в myblock [2] = B 2,1 от глобальных
- myblock [0] = myblock [3] + (myblock [1] * myblock [2]) (матрица мульт и сложение, все в общей памяти).
Теперь вы можете повторить это для остальной части последовательности матриц в вашей части цепочки, выводя только когда закончите.
Когда вы закончите, вы получите (#SMs) матрицы в глобальной памяти, которые еще нужно умножить, но в глобальной памяти не будет никакого дополнительного временного хранилища, и вы не будете пришлось копировать данные в глобальную память, отличную от исходных матриц и списков, которые нужно решать.
Опять же, нет реальной причины делать это, за исключением того, что вы не можете потрудиться передавать данные в GPU поэтапно, и производительность почти наверняка будет хуже; меньше записей в глобальную память, но вы, вероятно, заплатите за это с помощью GEMM. Хорошая новость заключается в том, что 50 не кратно 8, так что вам, вероятно, не придется слишком много избегать конфликтов банков с общей памятью.
Опять же, для получения бонусных баллов вы можете предварительно вычислить все блоки всех парных матричных продуктов, а затем половину длины вашего списка.