스레드 풀 크기 산정 방법

스레드 풀 크기를 결정할 때는 시스템 처리량(Throughput) 요구사항을 고려해야 합니다. 단순한 접근법으로 TPS(초당 처리 트랜잭션)가 20이고 각 트랜잭션 처리 시간이 4초일 경우, 80개 스레드가 필요하다고 계산할 수 있지만, 이는 CPU 코어 수를 고려하지 않은 비현실적인 방법입니다.

기본 산정 공식

CPU 코어 수(N)를 기준으로 한 실용적인 접근:

  • CPU 집중 작업: N + 1
  • I/O 집중 작업: 2N + 1

고급 산정 공식

보다 정확한 계산을 위한 공식:

최적 스레드 수 = ((스레드 대기 시간 + 스레드 CPU 시간) / 스레드 CPU 시간) × CPU 코어 수

예시: CPU 시간 0.5초, 대기 시간 1.5초, 코어 8개 → ((0.5+1.5)/0.5)×8=32

이 공식은 다음 원리를 반영합니다:

  • 대기 시간 비율이 높을수록 더 많은 스레드 필요
  • CPU 시간 비율이 높을수록 적은 스레드 필요

시스템 처리량 최적화

Amdahl의 법칙에 따르면 병렬화 효과는:

가속비 = 최적화 전 소요시간 / 최적화 후 소요시간

시스템 병렬도(F), CPU 수(N)와의 관계:

가속비 ≤ 1 / (F + (1-F)/N)

동적 계산 구현 예제

실제 환경에서 최적값을 계산하는 Java 구현:

public abstract class PoolCalculator {
    private final int QUEUE_SAMPLE = 1000;
    private final int TIME_TOLERANCE = 20;
    private volatile boolean isExpired;
    private final long TEST_DURATION = 3000;

    public void compute(BigDecimal targetUsage, BigDecimal maxQueueSize) {
        // 큐 용량 계산 로직
        Runnable job = generateTask();
        executeJob(job); // 워밍업
        long cpuDuration = getThreadCpuTime();
        executeJob(job); // 실제 측정
        cpuDuration = getThreadCpuTime() - cpuDuration;
        long waitDuration = (TEST_DURATION * 1000000) - cpuDuration;
        // 스레드 수 계산 로직
    }
    
    // 추상 메서드 선언
    protected abstract Runnable generateTask();
    protected abstract BlockingQueue createJobQueue();
    protected abstract long getThreadCpuTime();
}

구체적 구현 예시:

public class NetworkPoolCalculator extends PoolCalculator {
    @Override
    protected Runnable generateTask() {
        return new NetworkTask();
    }

    @Override
    protected BlockingQueue createJobQueue() {
        return new LinkedBlockingQueue(1000);
    }

    @Override
    protected long getThreadCpuTime() {
        return ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime();
    }
}

class NetworkTask implements Runnable {
    public void run() {
        // 네트워크 요청 처리 로직
    }
}

실행 결과 활용

계산 결과를 바탕으로 스레드 풀 초기화:

ExecutorService executor = new ThreadPoolExecutor(
    88, // 계산된 스레드 수
    88,
    0L, 
    TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<>(2500) // 계산된 큐 크기
);

태그: 스레드풀 Java동시성 성능최적화 Amdahl법칙 CPU바운드

7월 7일 20:34에 게시됨