티스토리 툴바


이 홈페이지 주인장은 취업을 한 지 1주일 정도 지났습니다.
포스팅을 보시면 많이 부족한 놈이라는 것을 아실텐데요(사실 알고 있는 것들도 아직 개념 정립이 안되서 포스팅 안한 것이 많습니다)

뜻하지 않은 행운과 더불어 취업을 하게 되었습니다.

여튼 문제는
회사의 소스를 분석하면서 여러가지 신기하고 재밌는 게 튀어나왔습니다.
....문제는 기밀인 것 같아서-_-;;- 공개를 못하고 물어보지도 못하고 그냥 끙끙거리다가 책을 보다가 발견한다거나 해서 으쌰으쌰 하고 있다는 점입니다. 대표적으로 제가 물어보는 사이트는 사장님 홈페이지라서 거기다 올릴 수도 없구요-ㅅ-

야밤에 일어나서 GPG1권을 보는 데 범용 핸들 기반이라는 챕터가 있더군요.
윈도우 핸들과 비슷한 구현을 하는 것 같은데 새로운 개념을 만나서 두근거리고 있습니다.

여러분도 즐거운 공부 하세요.
PS. GPG시리즈는 게임프로그래머의 필독서입니다. 게임프로그래머가 되고 싶으시다면 반드시 읽어두세요. 전 사놓고 지금에야 읽는 중입니다.
Posted by 휴리첼
InitializeCriticalSectionAndSpinCount에 대해서 살짝 정리.
어제 정리했다시피 EnterCriticalSection 에서는 시스템 메모리가 부족할 때 오류가 발생합니다. 이것을 처리해 주기 위해서 throw-catch를 이용하거나 InitializeCriticalSectionAndSpinCount로 초기화 하는 방법을 제시 했었습니다.

그렇다면 InitializeCriticalSectionAndSpinCount이 뭐냐!!?
msdn을 뒤져보니까

BOOL InitializeCriticalSectionAndSpinCount(
  LPCRITICAL_SECTION lpCriticalSection,  DWORD dwSpinCount);

Return. 성공하면 0이 아닌 값.
실패하면 0이 나옴. GetLastError로 에러를 살펴보면 되겠습니다.


이렇게 생겼습니다. 리턴값도 있고 기존의 것과 다른 인자가 하나 더 들어가는군요. 바로 dwSpinCount. Spin을 할 횟수를 지정하면 됩니다.

dwSpinCount는 싱글코어일 경우에 인자가 무시됩니다.(자동으로 0으로 셋팅됩니다)

멀티코어일 경우, 만약 Critical Section이 이용되지 않으면, Critical Section과 함께 세마포어 연합(?) 의 기다리는 동작이 Thread Spin을 부를 때 dwSpinCount만큼 부르게 됩니다.
만약 Spin동작에서 CriticalSection이 무효화 된다면 부르게 되는 Thread는 기다리는 동작을 피하게 됩니다.

...이게 뭔소리냐-_- 직독직해를 해버리니까 이따위로 나오는군요. 대충 의미를 되세겨 봅시다.

Spin은 Wait 상태로 들어가기 전에 함수의 인자로 주어진 dwSpinCount만큼 루프를 돈다는 의미입니다. 루프 도는 중간에 Critical Section을 획득할 수 있다면 Thread를 교체하는 (Thread Context Switch)가 발생하지 않고 임계영역안에 접근 가능하다는 의미. 검색을 해보니까 dwSpinCount를 2000정도로 주는 것이 좋다고 합니다.


예를 들어보자면 1번 쿼리와 2번 쿼리가 있다고 했을 때 2번 쿼리에서 어는 특정 테이블을 변경하고 있다고 해 봅시다. 그런데 1번 쿼리에서 2번 쿼리가 변경하고 있는 테이블 값에 접근을 하려고 하는 경우 Critical Section에 의해서 접근이 불가됩니다. 이 때 일반적인 CriticalSection이라면 대기상태로 돌입하게 되는데, SpinCount가 지정되어 있다면 2번이 빠리 끝나기를 바라며 dwSpinCount만큼 재접근을 하는 겁니다. 그리고 실패해버리면 기다리는 모드로 들어가게 되고 성공하면 접근하는 것입니다.

이렇게 되면 싱글코어에서는 별 의미없겠지만 다중 프로세서일 경우에는 많은 의미가 생기겠지요? Thread의 ContextSwitch에 부하가 있는 데 그것을 피할 수 있을 가능성이 높아지겠지요. 기다리는 모드로 돌입하기 전에 접근 가능하는 경우가 꽤 많을 테니까요. 이상.
Posted by 휴리첼
1. 메모리의 Access 비용
1.1 역시나 Register 메모리가 제일 빠르다. Register 메모리는 1cycle.
1.2 L1, L2 . L1 > L2
1.3 Memory 50-300
1.4 Disk access cost 1-15ms
1.5 Network Disk 100ms-10Sec

요약 : 가장 빠른 것은 레지스터, L1, L2 순서지만 L2와 메모리의 속도차이는 아주 많다.

2. 보통 쓰레드를 생성할 때는 _beginthreadex 를 사용하는 게 좋다.
(일반 프로그램이니까 이게 게임에도 들어갈 듯)

MFC를 사용하는 UI 응용 프로그램은 AfxBeginThread
WindowOS에서는 CreateThread() 를 사용하자. ...지만, 이건 Window Os에 종속적인 놈이라 좀 꺼림칙할 듯 함.

3. 쓰레드마다 서로 다른 메모리 영역을 사용하나?
Thread Local Storage를 이용한다. 많은 수의 TLS를 사용하지 않는 게 좋다

3.1 Thread Stack Size
많은 수의 Thread를 사용하려면 Thread Staack Size를 조정해야 한다.
Ex) HANDLE h = CreateThread(NULL, 4096, ThreadProc, NULL, STACK_SIZE_PARAM_IS_A_RESERVATION, &id);

3.2 Thread를 최소한으로 만든다.
1프로세서당 1개의 스레드를 만들고 작업은 큐를 이용해서 순차적으로 처리하게 스레드를 생성해야 한다.

3.3 대용량 멀티 스레드 프로그램을 개발할 때는 Thread Pool을 이용해서 개발하는 게 좋을 것임.

4. Thread 종료
4.1 Thread 함수가 return 또는 ExitThread를 호출할 때
종료되는 Thread의 Stack이 제거되고 초기화된 모든 Pending I/O가 취소된다.
4.2 Closehandle을 꼭 수행해 주도록 한다.
4.3 TerminateThread API는 최대한 사용하지 않는 게 좋다.
-좀 위험함

그럼 어케 종료할 꺼냐? 바로 이벤트 오브젝트를 이용하는 것이다!
1. 이벤트 오브젝트 생성
2. 쓰레드 생서
3. WaitForSingleObject로 timeout 시간 설정을 0으로 해서 감시
4. Thread에서 감시중인 이벤트 오브젝트에 시그널 되면 종료시키자.

5. thread동기화
EnterCriticalSection의 API를 MSDN에서 확인해 보면 리턴값이 VOID형이다.
시스템에 메모리가 부족할 때 EnterCriticalSection 함수 호출한 곳에서 Exception이 발생한다.

5.1 그럼 어케 해야 하느냐?
Structured Execetpion handling을 이용한다. 그래 그거. try catch
initializeCriticalSection 초기화 함수 대신
InitializeCriticalSectionAndSpinCount를 사용한다.
스핀카운트는 다중 프로세서 시스템에서 여러 프로그램이 동시에 같은 리소스를 액세스해야 할 때 성능을 높이기 위한 방법이다. 스핀카운트는 프로세서가 기다리기 전에 리소스를 액세스하기 위해 시도하는 횟수를 말함.

5.2 Vista에서 CriticalSection이 보안 때문에 수정되었다. 이건 나중에.

6. Thread 동기화(Synchronization)
6.1세마포어(Semaphore) 오브젝트 사용하기
CreateSemaphore()

하나의 동기화 객체를 기다리는 Thread가 너무 많은 경우 세마포어를 하나 더 둬서 새당 동기화 객체를 기다리는 Thread 숫자를 줄여주는 것이 성능향상에 도움이 된다.

6.2 이벤트 오브젝트 사용하기
기본적인 것은 알고 있으니까 넘어가고
10개의 Thread가 동일한 이벤트 오브젝트를 기다리고 있을 경우?
SetEvent() : Signal로 전환. 이건 아는 것이니까 넘어가고
PulseEvent() : Signaled로 갔다가 바로 non-Signaled로 전환. Manual reset Event이면 대기 중이던 스레드가 전부 깨어나고 auto reset이면 한 개만 꺠어난다.

6.3. WaitForMultipleObjects API 사용하기
6.3.1
두 개 이상의 이벤트가 동시에 시그널 상태가 될 수 있다.(호.. 이런 경우는 생각안해봤는데)

6.3.2 하나의 이벤트가 두 번 이상 시그널 될 수 있다.
이건 코딩을 잘 못한 것이다. 이벤트 오브젝트가 완료된 이후에 재사용하도록 코드를 작성했다면 하나의 이벤트에 두 번 이상 시그널 되지 않는다.

6.3.3 비동기 이벤트이므로 실행 순서를 보장받지 않는다.
이건 나중에 버그로 발견되기가 쉽지 않으므로 주의하고 있어야 함.

7. STL/MFC Collection을 사용할 때는?
-여러 Thread에서 읽는 것은 안전
-각각 다른 컨테이너를 쓰는 것은 안전

단. 삽입 삭제, 조회를 하는 모든 경우에는 동기화 필요
반복자를 수행하는 것도 전부 동기화가 필요

8. 그 외 주의점.
8.1 사용하는 운영체제와 CPU 특성을 알고 있어야 한다.
Window200 Professional과 Server는 Thread Quantum값이 다르다.

8.2 메모리 조각화가 발생하지 않도록 해야 한다.
8.3 최대한 if문 사용을 자제한다.
8.4 4가지 동기화 객체 특징을 알고 있어야 한다.
mutex, Semaphore, Event, CriticalSection.

8.5 리스트에서 빈번한 추가 삭제가 일어난다면 아예 배열을 사용하셈.
8.6 포인터를 사용하지 않는다. ..사용하게 된다면 스마트 포인트를 사용한다.
8.7 한 번 할당한 메모리 포인터는 삭제하지 않고 재사용하게 클래스를 설계할 것.
8.8 메모리 삭제 생성을 빈번하게 해야 한다면 메모리 풀을 이용한다.


9. 쓰레드가 너무 많으면?
디버깅이 힘들다. 당근.
동기화 오브젝트는 64개가 한계. 그 외는 사용자가 직접 동기화를 해야 한다.
쓰레드 수에 비례해서 switching의 빈도수와 부하가 커진다.
100이라는 양을 2번 처리해야 하는 경우 한 개의 Thread에서 100을 두번 처리하는 것 보다 두 개의 Thread에서 100을 각각 하나씩 동시에 처리하는 경우 더 시간이 걸린다.

10. Thread Pool
Thread Pool은 천하무적이 아님. 우리한테 꼭 필요한 것인가를 먼저 검토한 다음에 사용여부를 결정해야 한다.
- 분산 처리가 안된다
- 사용자당 처리 응답 시간이 긴 경우가 많다



---이것은 1월6일의 세미나 PT를 정리한 것입니다. 자세한 사항은 http://blog.naver.com/process3 이곳을 참고하면 좋습니다. ...새로운 것을 많이 알게 되었네요. PT및 발표 데모 자료는 위 홈페이지에서 받으실 수 있습니다.---


Posted by 휴리첼