Theo nghĩa rộng, các thuật toán chạy nhanh hơn trên GPU là những thuật toán mà bạn đang thực hiện cùng một loại hướng dẫn trên nhiều điểm dữ liệu khác nhau.
Một ví dụ dễ dàng để minh họa điều này là với phép nhân ma trận.
Giả sử chúng ta đang thực hiện tính toán ma trận
A × B = C
Một thuật toán CPU đơn giản có thể trông giống như
// bắt đầu bằng 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];
}
}
}
}
Điều quan trọng để thấy ở đây là có rất nhiều vòng lặp lồng nhau và mỗi bước phải được thực hiện lần lượt.
Xem sơ đồ này
Lưu ý rằng việc tính toán từng phần tử của C không phụ thuộc vào bất kỳ phần tử nào khác. Vì vậy, việc tính toán được thực hiện theo thứ tự nào không quan trọng.
Vì vậy, trên GPU, các hoạt động này có thể được thực hiện đồng thời.
Một nhân GPU để tính toán nhân ma trận sẽ trông giống như
__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];
}
}
}
Hạt nhân này chỉ có hai vòng lặp bên trong. Một chương trình gửi công việc này tới GPU sẽ yêu cầu GPU thực thi hạt nhân này cho từng điểm dữ liệu trong C. GPU sẽ thực hiện đồng thời từng hướng dẫn này trên nhiều luồng. Giống như câu nói cũ "Cheaper by chục" GPU được thiết kế để nhanh hơn làm điều tương tự nhiều lần.
Tuy nhiên, có một số thuật toán sẽ làm chậm GPU. Một số không phù hợp cho GPU.
Nếu ví dụ, có các phụ thuộc dữ liệu, nghĩa là: hãy tưởng tượng tính toán của từng phần tử của C phụ thuộc vào các phần tử trước đó. Lập trình viên sẽ phải đặt một rào cản trong kernel để chờ cho mỗi tính toán trước đó kết thúc. Đây sẽ là một sự chậm lại lớn.
Ngoài ra, các thuật toán có rất nhiều logic phân nhánh, tức là:
__kernel Foo()
{
if (somecondition)
{
do something
}
else
{
do something completely different
}
}
có xu hướng chạy chậm hơn trên GPU vì GPU không còn làm điều tương tự trong mỗi luồng.
Đây là một lời giải thích đơn giản vì có nhiều yếu tố khác để xem xét. Ví dụ, gửi dữ liệu giữa CPU và GPU cũng tốn thời gian. Đôi khi nó đáng để thực hiện một tính toán trên GPU ngay cả khi CPU nhanh hơn, chỉ để tránh thời gian gửi thêm (Và ngược lại).
Ngoài ra, nhiều CPU hiện đại hỗ trợ đồng thời cũng như với bộ xử lý đa lõi siêu phân luồng.
GPU dường như cũng không tốt cho đệ quy, xem ở đây có thể giải thích một số vấn đề với thuật toán QR. Tôi tin rằng người ta có một số phụ thuộc dữ liệu đệ quy.