파이버는 생성해두는 것만으로는 CPU를 전혀 사용하지 않습니다.
단, 파이버가 일시정지했다가 다시 실행을 재개하는 시점에 CPU 시간을 약간 소비합니다.
대략 엔진 API가 리턴하는 FTvoid나 FT<TResult>를 await으로 대기할 때마다
조금씩 오버헤드가 발생한다고 생각하시면 됩니다.
하드웨어마다 정확한 값이 다르겠지만, 대략의 계산을 위한 수치는 다음과 같습니다.
-
파이버 시작+종료 1회마다 약 10µs(0.01ms)로 어림잡을 수 있습니다. 즉, 파이버를 만들고 버리는 일이 대단히 싸기 때문에, 스레드를 풀링하듯이 파이버를 풀링할 필요가 없습니다.
-
어떤 이유로든 파이버의 일시정지+실행재개 1회마다 소비되는 최소 CPU 시간을 약 2µs(0.002ms)로 어림잡을 수 있습니다. 이 값은 파이버 개수가 많아질수록 커집니다.
-
Sleep을 통해 파이버를 일시정지+실행재개할 때마다 소비되는 CPU 시간을 약 4µs(0.004ms)로 어림잡을 수 있습니다. 이 값은 파이버 개수가 많아질수록 커집니다.
| Active fiber count | 1,000 | 10,000 | 100,000 |
|---|---|---|---|
| Overhead per EngineAPI.Fibering.Yield | 1.5µs | 2µs | 3µs |
| Overhead per EngineAPI.Fibering.Sleep | 3µs | 4µs | 7µs |
하나의 서버 프로세스에 오브젝트를 10,000개 만든다고 칩시다. 이 오브젝트들마다 각각 파이버를 붙이고, Sleep을 사용해서 50ms마다 업데이트 프로시저를 실행하게 해도 될까요?
계산해 봅시다.
오브젝트들마다 1초에 20번의 Sleep 호출이 발생할 것입니다. Sleep을 통해 파이버를 일시정지+실행재개할 때마다 CPU 시간을 약 4µs 소모하므로, 매 1초마다 오브젝트들마다 Sleep에만 80µs를 소모합니다. 10,000개의 오브젝트가 있으므로, 매 1초마다 전체 오브젝트가 Sleep에 소모하는 시간을 모두 합치면 0.8초입니다..;;;;
메인 스레드의 CPU 시간 중 80%를 파이버 전환에만 소모하게 됩니다. 이렇게 만들면 안 되겠죠.
파이버 하나마다 약 2.2KB로 어림잡을 수 있습니다. 즉 파이버를 10,000개 생성해두면 메모리를 22MB 사용합니다. 물론 파이버의 실행 스택에 물려있는 메모리가 있을 것이므로 실제 메모리 소모량은 더 커지지만, 대부분의 경우에 CPU 시간에 비해서 메모리 사용량을 무시할 수 있을 것입니다.
- Lenovo YOGA 900S 에서 측정했습니다. 2015년에 출시된 팬 없는 노트북 컴퓨터입니다.
- CPU: Intel(R) Core(TM) m7-6Y75 CPU @ 1.20GHz
- OS: Windows 10 Home 64bit
앞으로 최적화를 통해 Sleep이나 파이버 일시정지+실행재개에 드는 CPU 시간이 줄어들 여지가 있습니다.