12
아! 그거... 모르겠습니다 - TLS 해강입니다…

[14.10.20] 아! 그거...모르겠습니다 - TLS(devrookie)

  • View
    498

  • Download
    0

Embed Size (px)

DESCRIPTION

본문 링크 용과 같이 – 유신 : http://www.slideshare.net/agebreak/141004-cedec-2014-1 MSDN : http://msdn.microsoft.com/en-us/library/ms686749.aspx WIKI : http://en.wikipedia.org/wiki/Thread-local_storage Optimizing software in C++ : http://www.agner.org/optimize/optimizing_cpp.pdf

Citation preview

Page 1: [14.10.20] 아! 그거...모르겠습니다 - TLS(devrookie)

아! 그거... 모르겠습니다 - TLS

해강입니다…

Page 2: [14.10.20] 아! 그거...모르겠습니다 - TLS(devrookie)

TLS는 Thread Local Storage의 약자입니다 “용과 같이 – 유신”에서

최적화 기법(http://www.slideshare.net/agebreak/141004-cedec-2014-1)으로 사용했다는데 뭔지 몰라서 기록해둡니다

멀티스레드에서 사용되는 용어이고, 아래를 참고했습니다 MSDN : http://msdn.microsoft.com/en-us/library/ms686749.aspx

WIKI : http://en.wikipedia.org/wiki/Thread-local_storage

지역 저장 잘 하게 생겼네…

Page 3: [14.10.20] 아! 그거...모르겠습니다 - TLS(devrookie)

기본적으로 프로세스의 모든 스레드는 가상 주소 공간을 공유합니다 때문에 함수 안의 로컬 변수야 스레드 각각 쓰지만, static 변수나 전역 변수는 공유됩니다 보통 이런거 접근 순서 때문에 엉뚱한 값이 되거나 DeadLock이니 뭐니 문제 만들게 되죠

그러나 TLS를 사용하면 프로세스가 글로벌 인덱스를 사용하여,

각각의 스레드마다 고유의 데이터를 제공합니다 하나의 스레드안에 다른 스레드들이 특별히 이용할 수 있는 인덱스를 만들어 놓는거죠

자기 몫은 자기가 챙겨라 이거지….

Page 4: [14.10.20] 아! 그거...모르겠습니다 - TLS(devrookie)

TLS가 작동하는 구조는 아래 그림과 같습니다 똑같은 gdwTIsIndex2에 접근하는데 Thread1은 메모리5에, Thread2는 메모리2에 접근하죠

TLS Index를 통해 TLS 슬롯의 메모리 블록에 접근, 각각의 lpvData값을 얻는 겁니다

다시 말해, TLS는 스레드마다 포인터를 지정, 각각의 메모리 블록에 접근할 수 있게 해줍니다 따라서 전역 데이터가 아닌, 서로 다른 블록의 데이터를 사용하게 되는 거죠

Virtual table을 통한 지역변수화 정도로 이해하면 될 것 같습니다

당신과 내가 똑같다고 생각하면 곤란해란 느낌…

Page 5: [14.10.20] 아! 그거...모르겠습니다 - TLS(devrookie)

TLS를 사용하기 위해서는 변수 선언 앞에 지정된 키워드를 적어주면 됩니다 __declspec( thread ) int number;

비주얼C++ 컴파일러 기준으로 제공되는 키워드입니다 컴파일러에 따라 __thread라는 키워드를 지원하기도 합니다

간단하지만 위와 같은 방법은 정적으로 선언하는 것이라 런타임중 로드한 dll같은 경우 메모리 블록을 제대로 잡지 못 하는 문제가 발생할 수 있어 추천되지는 않습니다

쉬운 길에는 언제나 함정이 있지….

Page 6: [14.10.20] 아! 그거...모르겠습니다 - TLS(devrookie)

동적으로 사용하기 위해서 제공되는 API는 다음과 같습니다 가능한 API를 사용하라고 권장하고 있습니다

DWORD TlsAlloc( void ); // Index 할당 BOOL TlsSetValue( DWORD dwTlsIndex, LPVOID lpTlsValue ); // 데이터 저장 LPVOID TlsGetValue( DWORD dwTlsIndex ); // 데이터 읽기 BOOL TlsFree( DWORD dwTlsIndex); // 해제

기껏 만들어줘도 안 쓰는 사람들이 있을지도 모르지….

Page 7: [14.10.20] 아! 그거...모르겠습니다 - TLS(devrookie)

간략한 코드로 보자면 이런 느낌이죠

DWORD dwTlsIndex; LPVOID lpvData; // Index를 할당해주고 if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES) ErrorExit("TlsAlloc failed"); // 데이터를 저장해준 뒤 if (! TlsSetValue(dwTlsIndex, lpvData)) ErrorExit("TlsSetValue error"); // 현재 스레드에 대한 데이터 포인트 검색해 읽고 lpvData = TlsGetValue(dwTlsIndex); // 마지막에는 꼭 해제해줍니다 TlsFree(dwTlsIndex);

적절히 잘 쓰는건 다음 문제지만….

Page 8: [14.10.20] 아! 그거...모르겠습니다 - TLS(devrookie)

몇 가지만 더 살펴보겠습니다 앞에 소스에 할당할 때 나온 TLS_OUT_OF_INDEXES는 아래와 같이 정의되어 있습니다

#define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF)

그외에 TLS_MINIMUM_AVAILABLE에는 각 프로세스에서 사용할 TLS 인덱스의 최소수가 정의되어 있습니다 시스템에 따라 최소 64 ~ 최대 1088의 인덱스를 사용할 수 있습니다

모른다고 크게 문제될 것도 없지만서도….

Page 9: [14.10.20] 아! 그거...모르겠습니다 - TLS(devrookie)

스레드가 생성될 때, 시스템은 LPVOID값의 배열을 할당하고 NULL값으로 초기화합니다 인덱스를 사용하기 전에 먼저 할당하는 것을 꼭 잊지 마세요

내부적으로 프로세스, 스레드의 진입/해제를 위해

DLL_PROCESS_ATTACH, DLL_THREAD_ATTACH, DLL_THREAD_DETACH, DLL_PROCESS_DETACH를 호출합니다

winnt.h 에 대부분 정의되어 있습니다

보는 사람도 이제 밑천 떨어지는걸 느끼겠지…

Page 10: [14.10.20] 아! 그거...모르겠습니다 - TLS(devrookie)

앞서 본 것처럼 LPVOID 값에 맞는 경우 TLS슬롯에 데이터를 직접 저장할 수 있습니다 그러나 대규모의 인덱스를 사용한다면,

별도의 저장 영역을 만들어서 통합하고, TLS 사용 슬롯은 최소화 하는게 좋습니다

포인터를 통해 TLS 블록에 접근되기 때문에, 아무래도 stack에 저장된 것보다는 느리겠죠 Optimizing software in C++에서 그렇다고 합니다

http://www.agner.org/optimize/optimizing_cpp.pdf

슬슬 이거 적는 것도 귀찮아 지고 있어…

Page 11: [14.10.20] 아! 그거...모르겠습니다 - TLS(devrookie)

“용과 같이 – 유신”에서는 TLS을 이용해 쉐이더를 최적화 했습니다 구체적인 내용은 모르지만, 쉐이더별로 다른 설정들을 특별한 스위칭 없이 사용한게 아닌가…

device_context니까 그렇겠지…하고 있습니다

사실 이 사례는 PS3를 빠르게 PS4로 바꾸는 과정에서 나온 이야기라 범용적이진 않겠지만… DX9 라이브에서 DX11로 빠른 수정을 하겠다! 라면 참고해 볼만하지 않을까 싶기도 하고요

어떻게 활용을 할 수 있을지는 모두의 마음속에…

뭐 전부 틀리고 망상이라도 그건 자유니까요…

Page 12: [14.10.20] 아! 그거...모르겠습니다 - TLS(devrookie)

내용상의 오류나 전수할 노하우가 있으시다면 [email protected] 으로 알려주세요

http://cafe.naver.com/devrookie 에 와서 알려주신다면 더욱 좋습니다!

감사합니다!

모르는게 없는 그날 까지… 는 못합니다