일반 CS/OS, System

I/O 컴플리션 포트 (윈도우 버전)

TheShield 2021. 10. 4. 23:43
반응형

1. I/O 컴플리션 포트의 생성 배경

 

- 동시에 수행할 수 있는 스레드 갯수의 상한 설정의 필요성 대두

(입력이 오는 대로 스레드를 생성/깨우기 위해서는 컨텍스트 스위칭의 비용과

스레드 생성 비용의 증가 때문에 시스템 성능의 문제가 발생)

 

- CPU 갯수보다 많이 유지하는 것은 위의 이유 때문에 오히려 비 효율적

- 스레드 생성 비용을 줄이기 위해 스레드를 미리 준비 - 스레드 풀을 사용

 

2. I/O 컴플리션 포트의 API

 

HANDLE CreateIoCompletionPort(

HANDLE hFile, 

HANDLE hExistingCompletionPort,

ULONG_PTR ComletionKey,

DWROD dwNumberOfConcurrentThreads);

 

https://docs.microsoft.com/en-us/windows/win32/fileio/createiocompletionport

 

CreateIoCompletionPort function (IoAPI.h) - Win32 apps

Creates an input/output (I/O) completion port and associates it with a specified file handle, or creates an I/O completion port that is not yet associated with a file handle, allowing association at a later time.

docs.microsoft.com

ㄱ. I/O 컴플리션 포트의 생성

I/O 컴플리션 포트를 생성할 때에는 앞의 3개의 매개변수는 

INVALID_HANDLE_VALUE, NULL, 0 값을 전달하면 된다. (생성 된게 없으니 다 NULL(0) 값이 들어간다)

-> 차라리 CreateNewIoCompletionPort 라는 API를 따로 만드는게 나아보이는데, 

원래 윈도우 API가 하나로 여러 개를 해먹으려는 심보가 많아서.. 복잡해졌다. 이런 API가 많쥬.. 

 

dwNumberOfCompletionPort <동일 시간 동시 수행 최대 스레드 갯수 >

: 0으로 설정 시에는 머신의 설치된 수행 가능한 최대 스레드 갯수로 설정

이것으로 해도 스레드 간의 컨텍스트 스위칭을 최대한 막을 수 있다. 

이 값을 최대한 크게 하고 싶은 욕구가 들 수 있겠으나, 그것은 퍼포먼스 테스트로 최대 효율을

따져보는 것이 낫겠다.

ex) 한번에 긴 연산을 하는 경우엔 여러 개를 해도 좋아보인다. 

 

ㄴ. 장치와 I/O 컴플리션 포트를 연계

 

 

BOOL GetQueueCompletionStatus(

HANDLE hCompletionPort,

PDWORD pdwNumberOfBytesTransferred,

PULONG_PTR pCompletionKey,

OVERLAPPED** ppOverlapped,

DWORD dwMilliseconds);

https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatus

 

GetQueuedCompletionStatus function (ioapiset.h) - Win32 apps

Attempts to dequeue an I/O completion packet from the specified I/O completion port.

docs.microsoft.com

 

3. IO CompletionPort의 스레드 유지 방식

 

IO CompletionPort는 수행 가능한 스레드가 2개이고 4개의 스레드에서 대기하도록 하면 

순간적으로 3개의 완료 통지가 IO 컴플리션 큐에 삽입될 때에 2개만이 깨어나 요청을 처리하게 된다. 

 

그러나 만약 수행 중인 스레드 중 하나가 대기 함수인 Sleep이나 WaitForSingleObject같은 함수를 써서

'대기모드'에 들어가버리면, IO CompletionPort는 현재 가용한 수행 스레드가 1개가 되므로 

이내 3번 째 스레드를 깨워서 2개를 맞춰준다. 

 

그러나 이렇게 되면 대기 중인 스레드가 깨면 2+1이 되어서 순간적으로 3개가 되는데, 

이런 경우엔 다시 2개로 만들어질 때까지 대기 스레드는 추가로 깨워지지 않는다.

 

이렇게 스레드를 유지한다. 

굉장히 가동성이 높은 영민한 방식을 쓴다. 

 

 

 

 

 

 

 

 

 

 

 

 

같이 읽어보면 좋은 글들

https://jungwoong.tistory.com/43

반응형