Загалом, алгоритми, які працюють швидше на графічному процесорі, - це ті, де ви виконуєте однотипні інструкції для багатьох точок даних.
Простий приклад проілюструвати це - матричним множенням.
Припустимо, ми робимо обчислення матриць
A × B = C
Простий алгоритм процесора може виглядати приблизно так
// починаючи з C = 0
for (int i = 0; i < C_Width; i++)
{
for (int j = 0; j < C_Height; j++)
{
for (int k = 0; k < A_Width; k++)
{
for (int l = 0; l < B_Height; l++)
{
C[j, i] += A[j, k] * B[l, i];
}
}
}
}
Головне, що тут слід побачити, - це те, що є багато вкладених циклів і кожен крок повинен бути виконаний один за одним.
Дивіться схему цього
Зауважте, що обчислення кожного елемента С не залежить від жодного з інших елементів. Тож не має значення, в якому порядку проводяться розрахунки.
Отже, на GPU ці операції можна робити одночасно.
Ядро GPU для обчислення множення матриці виглядало б приблизно так
__kernel void Multiply
(
__global float * A,
__global float * B,
__global float * C
)
{
const int x = get_global_id(0);
const int y = get_global_id(1);
for (int k = 0; k < A_Width; k++)
{
for (int l = 0; l < B_Height; l++)
{
C[x, y] += A[x, k] * B[l, y];
}
}
}
Це ядро має лише дві внутрішні для циклів. Програма, що надсилає це завдання GPU, скаже GPU виконувати це ядро для кожної точки даних у C. GPU виконуватиме кожну з цих інструкцій одночасно у багатьох потоках. Так само, як і колишня приказка «Дешевше за десяток» графічні процесори розроблені так, щоб швидше робити те саме, багато разів.
Однак існують деякі алгоритми, які сповільнюватимуть графічний процес. Деякі не дуже підходять для GPU.
Наприклад, існували залежності даних, тобто: уявіть, обчислення кожного елемента C залежало від попередніх елементів. Програмісту доведеться поставити бар'єр у ядрі, щоб дочекатися завершення кожного попереднього обчислення. Це було б серйозним уповільненням.
Також алгоритми, які мають багато логіки розгалуження, тобто:
__kernel Foo()
{
if (somecondition)
{
do something
}
else
{
do something completely different
}
}
прагнуть повільніше працювати на графічному процесорі, оскільки GPU вже не робить те саме в кожному потоці.
Це спрощене пояснення, оскільки є багато інших факторів, які слід враховувати. Наприклад, передача даних між процесором та графічним процесором також забирає багато часу. Іноді варто робити обчислення на графічному процесорі навіть тоді, коли його швидше на процесорі, просто щоб уникнути зайвого часу надсилання (І навпаки).
Також багато сучасних процесорів підтримують паралельність зараз, а також з багатопотоковими процесорами з гіперпотоком.
Графічні процесори також здаються не дуже хорошими для рекурсії, дивіться тут, що, ймовірно, пояснює деякі проблеми з алгоритмом QR. Я вважаю, що людина має деякі рекурсивні залежності даних.