Комментировать

1) Скорей всего при расчётах

1) Скорей всего при расчётах - всё double, по крайней мере матрицы в double формируется(около 17 значащих цифр) и вектор сил хранится в double.
2) По-этому поводу есть несколько предположений:
1]Это может быть связанно с немного разными размерностями системы, но вроде эта разница не такая большая и не должна сильно влиять. В связи с этим может быть, что ускорение времени выполнения SPMV меньше чем GFlop/S ускорение(Количество FLOP считал так: NonZeros*2).
2]Хоть матрица и симметричная, хранится она у меня не как симметричная. SPMV запускается как несколько потоков на строку матрицы, если использовать при этом симметричность - может возникать ситуация записи несколькими потоками в одно место global mem(есть несколько идей как это обойти - например, иметь несколько векторов результатов, хранить дополнительную инфу кто куда должен писать, а после суммировать. Но тут надо ещё продумать нормальное общение с памятью - если потоки будут писать в произвольные места памяти, будет очень uncoalesced). Кстате, многие реализации CG тоже забивают на симметричность.
3]Матрица хранится у меня в формате CSR, хотя для МКЭ выгодней использовать блочный CSR - BCSR(я не стал с этим заморачиваться, так как у меня каждый узел может иметь разное количество степеней свободы, в связи с чем блоки могут быть разного размера - в таком случае я думаю делать несколько раз SPMV для частей матриц с разными размерами блоков). Может Ansys использует BCSR (в одном из форматов записи матриц жёсткости, AnSys использует CSR)
4]Выгодно умножать главную диагональ отдельно, точнее даже блочную диагональ, в этом случае нету никакой служебной инфы, а только числа. Может AnSys использует это.
5]Может не все операции на итерации у меня имеют одинаковое ускорение
6]Во время одной итерации у меня есть копирование с устройства результата скалярного произведения, для вычисления коэффициента, на который должен умножаться вектор, в принципе это можно избежать - запустив ядро для вычисления коэффициента, которое будет просто делить два числа, но профилировщик говорит что это копирование занимает очень мало времени. Тем не менее полностью избежать копирования чего-то с устройства может не получиться - нужно ведь знать какая сейчас невязка, чтобы знать когда остановиться. Может можно попробовать как-то маякнуть с устройства вызвав что-то типа exception(если вообще такое возможно). Проверять невязку не на каждой итерации можно, но особенность CG, в том, что невязка уменьшается не на каждой итерации - бывает сильно скачет.

Конечно можно ещё повозиться с текущим алгоритмом, выжать из него ещё процентов 50 скорости, но я считаю для МКЭ лучше использовать принципиально другой подход.