CPU 코어가 하나만 있다고 가정해 봤을 때 운영체제의 멀티태스킹을 생각해보자.
스레드A, 스레드B가 있다.
1. 운영체제는 먼저 스레드A 실행
2. 멀티태스킹을 해야 하기 때문에 스레드A를 계속 실행할 수는 없으니 스레드A를 잠시 멈추고, 스레드 B 실행
3. 이후에 스레드A로 돌아가려 했을 때 !!
CPU에서 스레드를 실행하는데 스레드A의 코드가 어디까지 수행되었는지 위치를 찾아야 한다.
그리고 계산하던 변수들의 값을 CPU에 다시 불러들여야 하는데,
따라서 스레드A를 멈추는 시점에 CPU에서 사용하던 이런 값들을 메모리에 저장해두어야 한다.
그리고 이후에 스레드A를 다시 실행할 때 이 값들을 CPU에 다시 불러와야 한다.
이런 과정을 컨텍스트 스위칭(context switching)이라고 한다.
컨텍스트 스위칭 과정에서 이전에 실행 중인 값을 메모리에 잠깐 저장하고, 이후에 그 저장한 값을 CPU에 다시 불러와야 하는데 이 과정에는 약간의 비용이 발생한다.
여기서! 멀티스레드는 대부분 효율적이지만, 컨텍스트 스위칭 과정이 필요하므로 항상 효율적이라고 볼 수는 없다.
CPU 코어가 2개 있다면 스레드A, 스레드B로 나누어 멀티스레드로 병렬 처리하는게 효율적이다.
모든 CPU를 사용하므로 연산을 2배 빠르게 처리할 수 있다.
즉, 스레드A에서 1 ~ 5000까지 더하는 동시에 스레드B에서 5000 ~ 10000을 더함.
그리고 마지막에 스레드A + 스레드B
CPU 코어가 1개 있는데, 스레드를 2개로 만들어서 연산하면 중간중간 컨텍스트 스위칭 비용이 발생한다.
운영체제 스케줄링 방식에 따라서 다르겠지만, 스레드A를 1 ~ 1000정도까지 연산한 상태에서 잠시 멈추고, 스레드B를 5001 ~ 6001까지 연산했다가 잠시 멈추고... 이런식으로 반복할 수 있다.
이때 CPU는 스레드A를 멈추고 다시 실행할 때 어디까지 연산했는지 알아야 하고, 그 값을 CPU에 다시 불러와야 한다.
결과적으로 이렇게 반복할 때 마다 컨텍스트 스위칭 비용(시간)이 든다.
결과적으로 연산 시간 + 컨텍스트 스위칭 시간이 드는 것!
이런 경우는 단일 스레드로 1 ~ 10000까지 더하는 게 컨텍스트 스위칭 비용 없이, 연산 시간만 사용하기 때문에 더 효율적이라고 볼 수 있다.
예를 든 숫자의 크기가 작아서 실제로는 그렇게 비용이나 시간이 만이 들지는 않겠지만(실제로 컨텍스트 스위칭에 걸리는 시간은 아주 짧음!) 스레드가 매우 많다면 이 비용이 커질 수 있다.
스레드 숫자가 너무 적으면 모든 CPU를 100% 다 활용할 수는 없지만 스레드가 적어서 컨텍스트 스위칭 비용이 줄어든다.
스레드의 숫자가 너무 많으면 CPU를 100% 다 활용할 수는 있지만 컨텍스트 스위칭 비용이 늘어난다.
스레드의 숫자를 CPU 숫자에 맞춘다면 CPU를 100% 활용할 수 있고, 컨텍스트 스위칭 비용도 자주 발생하지 않기 때문에 최적의 상태가 된다.
이상적으로는 CPU 코어 수 + 1개 정도로 스레드를 맞추면 특정 스레드가 잠시 대기할 때 남은 스레드를 활용할 수 있다.
각각의 스레드가 하는 작업은 크게 2가지로 구분할 수 있다.
분야마다 다르지만 실무에서는 I/O-바운드 작업이 CPU-바운드 작업보다 많은 편이다.
백엔드의 경우 스레드가 1 ~ 10000 더하는 CPU 연산이 필요한 작업보다 사용자 입력을 기다리거나 데이터베이스를 호출하고 그 결과를 기다리는 등 기다리는 일이 더 많기 때문!
즉, 스레드가 CPU를 많이 사용하지 않는 I/O-바운드 작업이 많다는 뜻이다.
일반적으로 자바 웹 애플리케이션 서버의 경우 사용자 요청 하나를 처리하는데 1개의 스레드가 필요하다.
사용자 4명이 동시에 요청 == 4개의 스레드가 작동하는 것.
(그래야 4명의 사용자 요청을 동시에 처리할 수 있기 때문)
예를들어 사용자 요청을 하나 처리하는 스레드가 CPU 1% 를 사용하고 대부분 데이터베이스 서버에서 어떤 결과를 조회하면서 기다린다고 가정했을 때, CPU 코어가 4개 있다고해서 스레드 숫자도 CPU 코어에 맞추어 4개로 설정하면 안된다. 그럼 동시에 4명의 사용자 요청만 처리할 수 있고 CPU는 단순하게 계산해서 4% 정도만 사용하는...96%는 노는 사태가 벌어질 수 있다.
위에 예시만 봐도 요청 하나를 처리하는 스레드가 CPU 1%를 사용한다 하면 100개의 스레드를 만들 수 있는것이 아닌가?
따라서 실무에서는 성능 테스트를 통해서 최적의 스레드 숫자를 찾는 것이 이상적!
정리해보면,