넓은 의미에서 GPU에서 더 빠르게 실행되는 알고리즘은 여러 데이터 포인트에서 동일한 유형의 명령을 수행하는 알고리즘입니다.
이것을 설명하는 쉬운 예는 행렬 곱셈입니다.
우리가 행렬 계산을하고 있다고 가정하자
A × B = C
간단한 CPU 알고리즘은 다음과 같습니다
// 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];
}
}
}
}
여기서 중요한 것은 중첩 된 for 루프가 많고 각 단계가 하나씩 실행되어야한다는 것입니다.
이것의 도표를보십시오
C의 각 요소의 계산은 다른 요소에 의존하지 않습니다. 따라서 계산 순서는 중요하지 않습니다.
따라서 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];
}
}
}
이 커널에는 두 개의 내부 for 루프 만 있습니다. 이 작업을 GPU로 전송하는 프로그램은 C에서 각 데이터 포인트에 대해이 커널을 실행하도록 GPU에 지시합니다. GPU는 여러 스레드에서 동시에 이러한 명령을 각각 수행합니다. 예전처럼 "수십 개의 저렴한"GPU는 동일한 작업을 여러 번 빠르게 수행하도록 설계되었습니다.
그러나 GPU 속도를 늦추는 알고리즘이 있습니다. 일부는 GPU에 적합하지 않습니다.
예를 들어, 데이터 의존성이 있었다면, 즉 C의 각 요소의 계산이 이전 요소에 의존한다고 상상해보십시오. 프로그래머는 이전의 각 계산이 끝날 때까지 커널에 장벽을 두어야합니다. 이것은 주요 속도 저하입니다.
또한 많은 분기 논리를 갖는 알고리즘은 다음과 같습니다.
__kernel Foo()
{
if (somecondition)
{
do something
}
else
{
do something completely different
}
}
GPU가 더 이상 각 스레드에서 동일한 작업을 수행하지 않기 때문에 GPU에서 느리게 실행되는 경향이 있습니다.
고려해야 할 다른 많은 요소가 있기 때문에 이것은 간단한 설명입니다. 예를 들어, CPU와 GPU간에 데이터를 보내는 것도 시간이 많이 걸립니다. 때로는 여분의 전송 시간을 피하기 위해 CPU에서 더 빠를 때에도 GPU에서 계산을 수행하는 것이 좋습니다 (그 반대의 경우도 마찬가지).
또한 많은 최신 CPU가 하이퍼 스레드 멀티 코어 프로세서와 함께 동시성을 지원합니다.
GPU는 재귀에 좋지 않은 것 같습니다 . QR 알고리즘의 일부 문제를 설명하는 여기 를 참조 하십시오 . 하나는 재귀 데이터 종속성이 있다고 생각합니다.