Как и во многих других высокопроизводительных вычислениях, ключом к пониманию производительности здесь является понимание использования памяти.
Если вы используете один поток, выполните одно умножение, затем для этого потока вы должны извлечь две части данных из памяти, умножить их, а затем выполнить некоторое логарифмическое число операций добавления. Это три обращения к памяти для mult и add и немного - арифметическая интенсивность очень низкая. Хорошая новость заключается в том, что таким образом существует множество задач, каждый из которых требует лишь небольшого объема памяти / регистров, что хорошо для заполнения; но отношение памяти к работе плохое.
Простой однопоточный подход, использующий одноточечный продукт, имеет такую же проблему - для каждого умножения требуется два доступа к памяти для загрузки. Хорошая новость заключается в том, что для всего продукта точка есть только одно хранилище в глобальной памяти, и вы избегаете двоичного сокращения, которое также не масштабируется и требует большой синхронизации; обратная сторона в том, что теперь стало намного меньше потоков, что, по крайней мере, ваш (b) подход помог вам.
Теперь вы знаете, что должен быть какой-то способ сделать больше операций для доступа к памяти, чем этот; для квадратных NxN-матриц умножение выполняется N ^ 3, но только 3xN ^ 2 элемента - так что вы должны быть в состоянии найти способ выполнить более 1 вычисления за 2 обращения к памяти.
Подход, принятый в CUDA SDK, является наилучшим способом - матрицы разбиты на фрагменты, и используется ваш (b) подход - один поток на элемент вывода. Но ключ в том, как устроены темы. Извлекая целые маленькие подматрицы из медленной глобальной памяти в разделяемую память и выполняя вычисления оттуда, можно делать многократные умножения и добавлять каждое число, которое вы считали из памяти. Этот подход является наиболее успешным подходом во многих приложениях, потому что получение данных - будь то по сети, или из основной памяти для ЦП, или вне-чипового доступа для графического процессора - часто занимает гораздо больше времени, чем обработка данных.
На страницах CUDA от NVidia есть документы (особенно http://developer.nvidia.com/object/cuda_training.html), которые очень хорошо описывают пример SDK.