답변:
GPU 하드웨어에는 두 가지 장점이 있습니다 : 원시 컴퓨팅 (FLOP) 및 메모리 대역폭. 가장 어려운 계산 문제는이 두 가지 범주 중 하나에 속합니다. 예를 들어, 밀도가 큰 선형 대수 (A * B = C 또는 Solve [Ax = y] 또는 Diagonalize [A] 등)는 시스템 크기에 따라 계산 / 메모리 대역폭 스펙트럼에 해당합니다. FFT (Fast Fourier Transforms)는 또한 높은 총 대역폭 요구 사항으로이 금형에 적합합니다. 다른 변환, 그리드 / 메시 기반 알고리즘, Monte Carlo 등도 마찬가지입니다. NVIDIA SDK 코드 예제 를 살펴보면 가장 일반적으로 해결되는 문제에 대해 느낄 수 있습니다.
더 유용한 대답은`GPU가 실제로 어떤 종류의 문제입니까? '라는 질문에 대한 것이라고 생각합니다. 이 범주에 속하지 않는 대부분의 문제는 GPU에서 실행될 수 있지만 일부는 다른 것보다 더 많은 노력이 필요합니다.
제대로 매핑되지 않은 문제는 일반적으로 너무 작거나 예측할 수 없습니다. 아주 작은 문제는 GPU의 모든 스레드를 사용하는 데 필요한 병렬 처리가 부족하거나 CPU의 저수준 캐시에 적합하여 CPU 성능을 크게 향상시킵니다. 예측할 수없는 문제에는 의미있는 분기가 너무 많아 SIMD 패러다임 을 깨뜨려 GPU 메모리에서 코어로 데이터를 효율적으로 스트리밍하거나 병렬 처리를 줄일 수 있습니다 ( ' 분산 왜곡 ' 참조 ). 이러한 종류의 문제의 예는 다음과 같습니다.
__synchtreads()
).
산술 강도가 높고 정기적 인 메모리 액세스 패턴이있는 문제는 일반적으로 GPU에서 구현하기 쉽고 더 효율적입니다.
고성능 GPU 코드를 사용하는 데있어 기본적인 어려움은 많은 코어가 있으며 가능한 한 최대한의 성능을 발휘하기를 원한다는 것입니다. 메모리 액세스 패턴이 불규칙하거나 산술 강도가 높지 않은 문제로 인해 결과를 전달하는 데 시간이 오래 걸리거나 메모리에서 항목을 가져 오는 데 시간이 오래 걸리고 시간이 오래 걸리고 숫자가 충분하지 않습니다. 물론 코드에서 동시성의 가능성은 GPU에서도 잘 구현 될 수있는 능력에 중요합니다.
이것은 독자적인 답변이 아니라 maxhutch 와 Reid.Atcheson 의 다른 답변에 추가 된 것 입니다.
GPU를 최대한 활용하려면 문제가 병렬화 될뿐만 아니라 GPU에서 실행될 핵심 알고리즘도 가능한 작아야합니다. 에서 의 OpenCL 기간이 대부분으로 불린다 커널 .
보다 정확하게, 커널은 GPU 의 각 멀티 프로세싱 유닛 (또는 컴퓨팅 유닛 )의 레지스터에 맞아야 합니다. 레지스터의 정확한 크기는 GPU에 따라 다릅니다.
커널이 충분히 작 으면 문제의 원시 데이터는 GPU의 로컬 메모리 (읽기 : 로컬 메모리 (OpenCL) 또는 공유 메모리 (CUDA))에 맞아야합니다 . 그렇지 않으면 GPU의 높은 메모리 대역폭조차도 처리 요소를 항상 바쁘게 유지할만큼 빠르지 않습니다 .
일반적으로이 메모리는 약 16 ~ 32 KiByte입니다 큰가 .
아마도 이전 답변에 더 기술적으로 추가 된 CUDA (즉, Nvidia) GPU는 각각 32 개의 스레드에서 자율적으로 작동하는 일련의 프로세서로 설명 될 수 있습니다. 각 프로세서의 스레드는 잠금 단계로 작동합니다 (길이 32의 벡터를 가진 SIMD를 생각하십시오).
GPU로 작업하는 가장 유혹적인 방법은 모든 것이 절대적으로 잠금 단계로 실행되는 것처럼 가장하는 것이지만, 이것이 항상 가장 효율적인 방법은 아닙니다.
코드가 수백 / 수천 개의 스레드로 훌륭하게 / 자동으로 병렬화 되지 않으면 병렬화 가 잘되는 개별 비동기 작업으로 분류하고 잠금 단계에서 실행되는 32 개의 스레드 만있는 작업을 실행할 수 있습니다. CUDA는 뮤텍스 를 구현할 수있는 일련의 원자 적 명령어를 제공 하여 프로세서가 스레드 풀 패러다임 에서 작업 목록을 처리 할 수 있도록합니다 . 그런 다음 코드는 멀티 코어 시스템에서와 같은 방식으로 작동하지만 각 코어에는 32 개의 스레드가 있습니다.
CUDA를 사용하는 방법에 대한 작은 예가 있습니다.
/* Global index of the next available task, assume this has been set to
zero before spawning the kernel. */
__device__ int next_task;
/* We will use this value as our mutex variable. Assume it has been set to
zero before spawning the kernel. */
__device__ int tasks_mutex;
/* Mutex routines using atomic compare-and-set. */
__device__ inline void cuda_mutex_lock ( int *m ) {
while ( atomicCAS( m , 0 , 1 ) != 0 );
}
__device__ inline void cuda_mutex_unlock ( int *m ) {
atomicExch( m , 0 );
}
__device__ void task_do ( struct task *t ) {
/* Do whatever needs to be done for the task t using the 32 threads of
a single warp. */
}
__global__ void main ( struct task *tasks , int nr_tasks ) {
__shared__ task_id;
/* Main task loop... */
while ( next_task < nr_tasks ) {
/* The first thread in this block is responsible for picking-up a task. */
if ( threadIdx.x == 0 ) {
/* Get a hold of the task mutex. */
cuda_mutex_lock( &tasks_mutex );
/* Store the next task in the shared task_id variable so that all
threads in this warp can see it. */
task_id = next_task;
/* Increase the task counter. */
next_tast += 1;
/* Make sure those last two writes to local and global memory can
be seen by everybody. */
__threadfence();
/* Unlock the task mutex. */
cuda_mutex_unlock( &tasks_mutex );
}
/* As of here, all threads in this warp are back in sync, so if we
got a valid task, perform it. */
if ( task_id < nr_tasks )
task_do( &tasks[ task_id ] );
} /* main loop. */
}
그런 다음 커널을 호출하여 main<<<N,32>>>(tasks,nr_tasks)
각 블록에 32 개의 스레드 만 포함되므로 단일 워프에 적합해야합니다. 이 예제에서는 또한 단순화를 위해 작업에 종속성 (예 : 하나의 작업이 다른 결과에 의존 함) 또는 충돌 (예 : 동일한 전역 메모리에서 작업)이 없다고 가정했습니다. 이 경우 작업 선택이 조금 더 복잡해 지지만 구조는 본질적으로 동일합니다.
물론 이것은 하나의 큰 셀 배치에서 모든 것을 수행하는 것보다 더 복잡하지만 GPU를 사용할 수있는 문제의 유형을 크게 넓 힙니다.
아직까지 한 가지 요점은 현재 세대의 GPU가 단 정밀도 계산에서와 같이 배정도 부동 소수점 계산에서 잘 수행하지 못한다는 것입니다. 계산이 배정 밀도로 수행되어야하는 경우, 단일 정밀도보다 10 배 정도 런타임이 증가 할 것으로 예상 할 수 있습니다.
오래된 질문이지만 통계적 방법과 관련이 있지만 루프가 무엇인지 아는 사람에게는 일반화 할 수있는 2014 년 의이 답변 은 특히 설명적이고 유익 하다고 생각합니다 .
GPU는 대기 시간이 긴 I / O를 가지므로 메모리를 포화시키기 위해 많은 스레드를 사용해야합니다. 날실을 바쁘게 유지하려면 많은 스레드가 필요합니다. 코드 경로가 10 클럭이고 I / O 레이턴시 320 클럭이면 32 개의 스레드가 워프를 포화 상태로 만들어야합니다. 코드 경로가 5 클럭이면 스레드를 두 배로 늘립니다.
코어가 천 개이면 GPU를 완전히 활용할 수있는 수천 개의 스레드를 찾으십시오.
메모리 액세스는 캐시 라인 (보통 32 바이트)입니다. 1 바이트를로드하는 데 드는 비용은 32 바이트입니다. 따라서 스토리지를 통합하여 사용의 지역성을 높입니다.
각 워프에는 많은 레지스터와 로컬 RAM이있어 이웃 공유가 가능합니다.
큰 세트의 근접 시뮬레이션은 잘 최적화되어야합니다.
임의 I / O 및 단일 스레딩은 죽이는 기쁨입니다 ...
Traveling Salesman과 같이 많은 무차별 대입으로 해결할 수있는 문제를 상상해보십시오. 그런 다음 각각 8 개의 스팬 키 비디오 카드가있는 서버 랙이 있고 각 카드에 3000 개의 CUDA 코어가 있다고 가정하십시오.
가능한 모든 영업 사원의 경로를 해결 한 다음 시간 / 거리 / 일부 메트릭별로 정렬하면됩니다. 물론 작업의 거의 100 %를 버리고 있지만 때로는 무차별 대입이 실행 가능한 솔루션입니다.
많은 엔지니어링 아이디어를 연구하면서 gpu는 작업, 메모리 관리, 반복 가능한 계산에 중점을 둔 형태라고 말합니다.
많은 수학 공식은 작성하기는 쉽지만 행렬 수학과 같이 계산하기가 어려울 수 있습니다. 단일 답변은 없지만 많은 값을 얻습니다.
이것은 계산 된 값이 없으면 일부 수식을 실행할 수 없기 때문에 컴퓨터가 값을 계산하고 수식을 실행하는 속도로 계산하는 데 중요합니다 (따라서 속도가 느려짐). 컴퓨터는 이러한 프로그램에서 사용할 수식을 계산하거나 값을 계산할 순서를 잘 모릅니다. 그것은 주로 빠른 속도로 힘을 뚫고 공식을 척으로 분해하여 계산하지만 요즘 많은 프로그램에서 계산 된 척이 필요하고 대기열 (ques of ques and ques of ques)을 기다리고 있습니다.
예를 들어 충돌에서 가장 먼저 계산되어야하는 시뮬레이션 게임에서 충돌의 손상, 물체의 위치, 새로운 속도? 시간이 얼마나 걸리나요? CPU가 어떻게이로드를 처리 할 수 있습니까? 또한 대부분의 프로그램은 데이터를 처리하는 데 더 많은 시간이 걸리는 매우 추상적이며 항상 멀티 스레딩을 위해 설계된 것은 아니며 추상적 프로그램에서 효과적으로 수행하기위한 좋은 방법이 아닙니다.
CPU가 나아지면서 더 나은 사람들이 프로그래밍을 느슨하게 만들었으므로 많은 다른 유형의 컴퓨터에도 프로그래밍해야합니다. GPU는 동시에 많은 간단한 계산을 통해 무력을 발휘하도록 설계되었습니다 (메모리 (2 차 / 램)는 물론 가열 냉각은 컴퓨팅의 주요 병목 현상입니다). CPU는 많은 수의 대기열을 동시에 관리하거나 여러 방향으로 끌어 당겨서 수행 할 수없는 작업을 파악하고 있습니다. (이것은 거의 인간이다)
GPU는 지루한 작업을 거친 작업자입니다. CPU는 완전한 혼란을 관리하고 있으며 모든 세부 사항을 처리 할 수는 없습니다.
그래서 우리는 무엇을 배우나요? GPU는 한 번에 지루한 작업을 자세히 설명하며 CPU는 너무 많은 작업에 집중할 수없는 멀티 태스킹 머신입니다. (주의 장애와 자폐증이 동시에있는 것처럼).
엔지니어링에는 아이디어, 디자인, 현실 및 많은 거친 작업이 있습니다.
내가 떠날 때, 단순하게 시작하고, 신속하고, 신속하게, 신속하고, 빠르게 시작하며, 시도를 멈추지 않는 것을 잊지 마십시오.