구조

CPU(Central Processing Unit)와 GPU(Graphics Processing Unit)는 모두 컴퓨터의 기본적인 연산 장치로서 중요한 역할을 한다. 하지만 그 구조는 크게 다르다.

CPU

GPU

내부 구조와 동작

CPU

CPU는 최고의 성능을 위해 파이프라이닝, 분기 예측, 명령어 수준 병렬성(Instruction-Level Parallelism, ILP) 등 다양한 기법을 사용한다.

\text{ILP} = \frac{\text{Instruction\ completion\ rate}}{\text{Cycles\ per\ instruction}}

GPU

GPU는 방대한 양의 데이터를 병렬로 처리할 수 있는 멀티스레딩(Multithreading)을 위해 설계되었다.

T = \frac{\mathbf{A} \cdot \mathbf{B}}{\mathbf{C}}

활용 분야

CPU

CPU는 일반적인 컴퓨팅 작업에 널리 사용된다. 예를 들어, 운영체제의 다양한 기능 처리, 애플리케이션 실행, 데이터베이스 관리 등이다. 복잡한 조건문 처리와 상호의존적 연산 처리에 강점을 가지고 있다.

GPU

GPU는 대규모 병렬 연산이 필요한 작업에 널리 사용된다. 예를 들어 그래픽 렌더링, 과학적 계산, 딥러닝 모델의 학습 등이 있다. 데이터 병렬성을 극대화할 수 있는 알고리즘에 적합한다.

성능 비교

처리 능력

CPU는 복잡한 단일 연산을 빠르게 처리하는 능력이 뛰어나지만, 여러 연산을 동시에 처리하는 능력은 상대적으로 낮다.

GPU는 수천 개의 코어를 이용해 많은 연산을 동시에 처리할 수 있다. 따라서 병렬 처리가 중요한 작업에서 높은 성능을 보인다.

전력 소비

CPU는 높은 클록 속도와 복잡한 회로 구조 때문에 상대적으로 전력 소비가 크다.

GPU는 많은 코어가 동시에 동작하기는 하지만, 개별 코어의 전력 소비가 낮아 전체적인 전력 효율이 높다.

유연성

CPU는 다양한 연산을 처리할 수 있는 범용성을 가지고 있다. 다양한 명령어 세트를 지원하며, 여러 가지 컴퓨팅 작업에 적합한다.

GPU는 특정 타입의 연산에 최적화되어 있으며, 병렬 처리가 가능한 작업에 중요한 역할을 한다. 하지만 범용성 측면에서는 CPU에 비해 제한적이다.

병목 현상

CPU와 GPU 사이에 존재하는 중요한 차이점 중 하나는 메모리 접근과 관련된 병목 현상이다. CPU는 큰 캐시 메모리를 사용해 데이터 접근 속도를 높이는데 반해, GPU는 메모리 대역폭을 활용하여 대규모 데이터를 병렬로 빠르게 처리한다.

계산 모델

CPU

CPU는 일괄 처리 방식을 사용하며, 각 코어가 순차적으로 작업을 처리한다. 이는 높은 시리얼 성능을 보장할 수 있지만, 병렬 처리 성능은 제한적이다.

\text{Execution\ Time\ (CPU)} = \sum_{i=1}^{n} \frac{\mathbf{C}_i}{\text{Frequency}}

GPU

GPU는 대규모 병렬 처리 방식을 사용하며, 많은 양의 데이터와 작업을 동시에 처리할 수 있다. 이는 병렬 처리 성능을 극대화할 수 있지만, 시리얼 성능은 상대적으로 낮다.

\text{Execution\ Time\ (GPU)} = \frac{\sum_{i=1}^{n} \mathbf{C}_i}{\text{Number\ of\ Cores}}

예제 및 활용 사례

예제 1: 이미지 처리

이미지 처리에서 CPU와 GPU의 차이는 극명하게 나타난다. 예를 들어, 한 이미지 파일의 모든 픽셀 값들을 조정해야 할 때, CPU는 각 픽셀을 순차적으로 처리한다. 반면, GPU는 모든 픽셀을 병렬로 처리할 수 있어 훨씬 빠르게 작업을 완료할 수 있다.

for (int i = 0; i < image_width; i++) {
    for (int j = 0; j < image_height; j++) {
        processPixel(image[i][j]);
    }
}
__global__ void processImageKernel(int* image, int width, int height) {
    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;
    if (x < width && y < height) {
        processPixel(image[y * width + x]);
    }
}

int main() {
    int* dev_image;
    cudaMalloc((void**)&dev_image, image_size);
    cudaMemcpy(dev_image, host_image, image_size, cudaMemcpyHostToDevice);

    dim3 threadsPerBlock(16, 16);
    dim3 numBlocks((image_width + threadsPerBlock.x - 1) / threadsPerBlock.x,
                   (image_height + threadsPerBlock.y - 1) / threadsPerBlock.y);
    processImageKernel<<<numBlocks, threadsPerBlock>>>(dev_image, image_width, image_height);

    cudaMemcpy(host_image, dev_image, image_size, cudaMemcpyDeviceToHost);
    cudaFree(dev_image);
}

예제 2: 과학적 시뮬레이션

과학적 시뮬레이션에서도 GPU는 엄청난 성능을 발휘한다. 예를 들어, 물리 시뮬레이션에서 각 입자의 위치와 속도를 계산해야 하는 경우, GPU는 모든 입자를 동시에 계산할 수 있어 시뮬레이션 시간을 크게 단축시킬 수 있다.

for (int i = 0; i < num_particles; i++) {
    updateParticle(particles[i]);
}
__global__ void updateParticlesKernel(Particle* particles, int num_particles) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx < num_particles) {
        updateParticle(particles[idx]);
    }
}

int main() {
    Particle* dev_particles;
    cudaMalloc((void**)&dev_particles, num_particles * sizeof(Particle));
    cudaMemcpy(dev_particles, host_particles, num_particles * sizeof(Particle), cudaMemcpyHostToDevice);

    int threadsPerBlock = 256;
    int numBlocks = (num_particles + threadsPerBlock - 1) / threadsPerBlock;
    updateParticlesKernel<<<numBlocks, threadsPerBlock>>>(dev_particles, num_particles);

    cudaMemcpy(host_particles, dev_particles, num_particles * sizeof(Particle), cudaMemcpyDeviceToHost);
    cudaFree(dev_particles);
}

CPU와 GPU는 각각의 강점과 약점을 가지고 있으며, 특정 작업에 따라 더 적합한 선택이 달라진다. CPU는 복잡한 제어 흐름과 조건문 처리를 필요로 하는 범용적인 작업에 강점을 가지고 있다. 반면, GPU는 병렬 처리가 가능한 대규모 데이터 작업에 뛰어난 성능을 발휘한다.

따라서, 컴퓨팅 자원을 효율적으로 활용하기 위해 서로 다른 장치와의 조합을 고려하는 것이 중요하다. 현대의 많은 고성능 컴퓨팅 환경에서는 CPU와 GPU를 함께 사용하여 두 장치의 장점을 극대화하고 있다.