로 이사갑니다.
워드프레스에서 글작성하기가 너무 힘드네요.
원문 Cliff Bleszinski’s Game Developer Flashcards
WordPress 기본 에디터가 엄청 불편하네요. 잘모르겠는 부분이나 제 생각은 이탤릭채+언더라인으로 표시하겠습니다. 직역한 것도 있고 그렇지 않은 것도 있습니다. 개인적으로 읽기 위해서 작성한 것이므로, 저작권에 문제가 된다면 언제든지 삭제될 수 있습니다.
By Cliff Blenszinski
Epic Games의 디자인 디렉터 Cliff Blenszinski 가 개발자 공통의 행동에 대해서 분석했습니다.
이번 여름에 들어서, 저는 20년간 게임을 전문적으로 만들어왔어요. 저는 캐릭터 마스코트 플래폼(캐릭터 IP를 이용한 게임인거 같음), FPS, 싱글 캠페인, 멀티플레이 경험, 그외 많은 게임들을 디자인해왔습니다. 수많은 대단한 프로그래머, 아티스트, 애니매이터, 작가, 프로듀서들과 함께 일했습니다. 그 시간동안, 저는 우리가 창의적인 전문가로서 소통하는 방법에 어떠한 패턴이 있다는 것을 알았어요.
개발자들은 엄청나게 똑똑하지만, 반면에 그들은 동료들과 비교해서 얼마나 똑똑한가에 대해서논할 때에는 조금 불안정해진다는 것을 배웠습니다. 개발자 게시판에서 수억달러의 프렌차이즈나 인디 개발자, 그리고 그 사이에 있는 사람들끼리 확대해석하고 트집잡으며 헐뜯는 것을 봐왔습니다. 우리는 항상 우리의 아이디어가 다른 누구도 생각하지 못했던 것임을 혹은 그런 아이디어가 이미 실험되었고, 성공했거나 실패했고, 이젠 영향력이 없는 아이디어임을 증명하기를 원해요.
짧게 이야기하면, 이 글은 게임 개발자들 사이에서 논의, 논쟁, 토론에서 승리하기 위해 자주 사용되는 의사소통 기술에 대해서 정의하고 있습니다.
이 관찰들과 이름(아래에 나오는 예들에 대한 타이틀인듯)은 누구를 적대시하기 위한 것이 아닙니다. 사실 여기서 언급된 몇몇 접근들은 유효한 추론에 의해서 사용되었습니다. 예를 들면, 패턴 매칭은 심한 경우 탄탄하다고 생각했던 아이디어를 죽일 수 있는 파생 제품을 만드는 것을 막기위한 좋은 방법이죠. 게임 개발 소통 플래시카드들입니다.
자기가 알고 있는 비슷한 아이디어(게임 혹은 대중문화의)들 중 나쁘거나 실패한 예들을 들어 반대하는 것입니다.
예를 들면, 영화 아바타에 대해서, “숲에 사는 시퍼런 사람들이 사악한 군대와 기계에 대항하는 영화를 만들고 싶다고? 스머프 외계인이야?” 기어즈 오브 워 프렌차이즈도 삐딱한 시선에서 보면 80년대 싸구려 호러영화 C.H.U.D처럼 보일 수 있어요.
잠재적으로 어마어마한 아이디어를 막기위해 극심한 경우를 예로 드는 것을 말합니다. 이 방법을 사용하는 사람들은 스카이림이 방대한 세계를 가지고 있다고 깔 수 있어요. 세계가 넓기 때문에 이동하는 것이 지루하고 고될수 있다는 걱정을 하면서요. “Fast travel” (포탈이나 추가적인 이동수단을 통해서 빠르게 움직이는 것을 말하는 듯 “Fast Travel은 플레이어가 한번이라도 가봤던 마을이나 스팟을 지도상에서 클릭해 바로 이동하는 기능이에요. 일반적인 포탈과 다른 점은 금전적인 비용은 전혀 없고 대신 게임내에서 목적지까지의 시간이 경과된다는 점이네요” 라네요. Brian Jo님 감사합니다.) 과 같은 개념을 도입하면 이러한 문제를 쉽고 명료하게 풀수 있는데 말이죠.
Edge Blocking 의 변종들:
“The Networker.” 극심한 경우나 코옵 상황에서는 그 아이디어가 동작하지 않을 것임을 예로 드는 것.”맥스패인의 슬로우 모션은 코옵에서 어떻게 돌아가? 불가능하지!” (항상 멀티플레이를 예로 들기 때문에 Networker 라는 이름이 붙은게 아닐까 생각)
“Perfectionist.” Edge Blocking 과 마찬가지로 개발자가 좋은 아이디어가 완벽해보이지 않는 하나의 예를 찾아내는 것. 예를 들면 아수라장 같은 상황에서는 캐릭터들끼리 겹쳐보이게 될거야.
혹은 “이전에 이런 기능을 본적이 없다 혹은 이런 기능이 잘된적을 본적이 없어요. 그러니까 이거 하면 안되” 따로 설명이 필요없는 항목이기도 한데, 사실은 이게 왜 이 아이디어를 실행해야 되는지에 대한 이유도 될 수 있습니다. 이러한 논리를 따라가다 보면 다른 사람이 성공한 것을 모방할 수 밖에 없어요.
한가지 언급하자면, Locust 주민들(기어즈 오브 워에 나오는 외계인들인듯)은 많은 이유에서 잘동작했지만, 우리는 아직도 그것에 대한 의구심을 자기도 있습니다. 그것은 마침내 천상/우주에서 온 외계인이라는 특징을 가진 보통의 다른 프렌차이즈들과 기어즈 오브 워가 차별화되는데 도움을 주었습니다.
대부분의 개발자가 그들이 어떤 아이디어를 지지하는 경우에도 변명의 한 형태로써 이 테크닉을 사용하는 경향이 있습니다.
개발자가 다른 성공적인 제품을 묵살하기 위해서 오기를 부리는 것입니다. 왜냐하면 그 공식은 알아보기 쉽거든요. 공식이 쉽고 분명하다는 것은 왜 제품이 성공적인가라는 말도 됩니다.
예를 들면, Words with Friends: “이건 그냥 비동시 스크래블이군.” 네 맞아요 그래서 그게 멋진거죠.
개발자가 한 아이디어(좋거나 나쁜)를 듣습니다. 그리곤 말하는 거죠. 다음 버전이나 차기작에는 잘 맞을거 같아. “나는 그 아이디어가 좋은거 같지는 않은데, 니 기분 좋으라고 이건 다음에 하기로 하자고 이야기 하고 안할꺼야” 고 관례적으로 말하는 것입니다.
이 테크닉은 개발자가 어떤 간단한 기능에 너무 많은 부가기능을 붙여서 궁극적으로 이러한 추가때문에 그 기능이 위대롭게 되는 것을 말합니다. 그 기능이 무게를 이기지 못하고 쓰러지는 것이죠.
다르게는 연속적인 의존으로 알려져 있습니다. 애니매이션, UI, 아트와 같은 다른 부서에 더 많은 일을 해야된다는 이유로 그 아이디어를 죽이는 것입니다. 주로 흥미롭고 할만한 가치가 있는 기능들은 다른 부서들의 업무를 합해야하는 이러한 파급효과를 가지고 있어요.
너무 생각만 과도하게 해서 실제로 아무것도 수행하지 못하는 것.
다른 말로 “우리가 어떻게 경쟁할 수 있겠어?” 경쟁자가 너무 많다고 위협을 느껴서 개발자가 성심성의를 다하기 전에 포기하기 위한 방법.
이 문구는 개발자들이 경쟁팀과 그들의 수가 얼마나 많은지 언급하기 위해 자주 사용되는 말입니다. 그리고 “Why Even Try?” 라고 말하기 위해서 자주 사용되요. 위대한 길은 항상 최고의 사람들에게 업무를 할당하고 최고의 툴을 제공해 스마트하게 일하는 것입니다. (크기가 중요한게 아니라는 말같네요)
“그렇지만 이게 우리가 항상 하던 방식인데!” 엔터테인먼트, 특히 기술 산업에서 혁신과 재고는 살아남기 위해서 필요합니다. 자기만족하거나, 어떠한 일을 같은 방법으로 계속 계속 하는 것은 실패로 가는 확실한 방법이에요.
규칙적인 업무의 20년 베테랑이 되는 것은 장점이 될 수도 있지만, 기술 산업에서는 아니에요. 그것이 때로는 당신을 제한할 것입니다. 개발자가 나이가 들수록 열린 마음을 유지하는 것과 항상 배우는 것이 중요합니다.
이것은 현재의 성공에 안주하기 위해 준비된 사무실의 전쟁 함성입니다. 우리는 특정 레벨의 성공에 도달했고 이미 우리는 한가닥 하는 사람이라고 생각하는 것입니다. 사무실 사람들이 이 말을 사용하기 시작하면, 누군가는 스튜디오의 붕괴를 손꼽아 기다릴 것입니다. 왜냐하면 더 젊고, 더 배고픈 바깥에 있는 사람들이 우리가 가진 것을 원할테고 이루려 노력할것이고 이룰것이니까요.
이전의 실패를 상기시켜서 새로운 (성공 가능성이 있는) 아이디어를 죽이는 것.
너의 아이디어는 죽여줘! 사실, 이건 너무 끝내주고 혁신적이야. 그래서 우린 이걸 하면 안돼. 왜냐하면 일이 많아질거 같거든.
개발자가 다른 부서와의 논쟁에서 이기기 위해서 그의 부서에서만 통할 전문적인 용어를 사용하는 것입니다.
개발자가 자기의 분야(아트, 코드, 디자인 등)이 스튜디오의 다른 사람보다 뛰어난다고 믿기 때문에 다른 애들은 X까라고 하는 것.
“이 아이디어는 멋져 그런데 이건 우리 프로젝트의 영역이 아니야.” 때로는, 불행하게도, 최고의 기능은 주변에 레이더 아래에 숨어있거나 원래 예정에는 없던 것입니다.
개발자가 새로운 기능이나 무기가 충분히 준비되지 않았는데 플레이 테스트 동안 고쳐서 밸런스를 잡겠다고 외치는 것입니다. 가끔 유저들은 스나이퍼건을 들고 아무것도 할 수 없고 다른 유저들에 의해 죽어나가도 그것은 괜찮아요.(스나이퍼건이 제대로 만들어지지도 않았다는 뜻인듯)
누군가 내 아이디어를 듣습니다. 처음에는 내 그걸 듣는채도 안하더니, 어딜가서는 똑같은 아이디어를 그들의 언어로 어디서 들었는지는 잊어버리고 자기것인냥 말합니다. 이것은 좋은 아이디어가 어딘가로부터 와서 잘 구현되기만 한다면, 궁극적으로 크게 중요하지는 않아요.
(TL;DR은 too long, didn’t read)
이 사람은 디자인 제안이나 논의에 대해서 3페이지 분량을 메일을 보냅니다.
맨날 보냅니다.
잊어버리지도 않구요.
결국에는 당신은 이 사람의 메일을 필터링할것입니다.
항상 이 사람이 보낸 메일을 읽으면 병신같습니다. 심지어 진정 그런 의미로 보낸게 아니라도, 솔직히 메일쓰는 능력이 없어서요.
이 사람은 현재 진행되고 있는 사항들을 모두 갈아엎고 주관적으로 더 괜찮아 혹은 더 나쁜 새로운 아이디어로 바꾸는 사람입니다. 그래서 본질적으로는 모두 처음부터 시작하게 합니다.
어떤 아이디어를 아무런 분명한 이유없이 거부하는 사람입니다: “난 그건 잘 몰라..” 가끔 … 를 상대할 때는 효과적입니다. (… 가 누구인지 잘 모르겠네요. 하기싫은 일을 시키는 사람인가?)
어떤 아이디어에 흥미를 가지고 달려드는 하지만 그것의 디자인이나 파급효과에 대해서는 전반적으로 생각해본적이 없는 디자이너입니다. 간단하게 모든 사람들이 그 아이디어에 대해 잘될거라는 믿음을 가지고 있다고 디자이너 혼자 예상하는 것입니다. 그 경우에 대해서 제대로 생각하지도 않았는데 말이죠. 이것은 주로 젊고, 경험이 덜한 디자이너에게서 나타나는 흔한 행동입니다.
(“모비 딕의 등장인물입니다. 모비 딕을 잡기 위해 끝없이 집착하는 것에 비유한 듯” – 라네요. 하얀까마귀님 감사합니다.)
디자이너가 어떤 아이디어가 잘 진행되고 있지 않음을 인정하지 않고 끝없이 계속하는 것입니다. 소중한 코드와 아트 리소스들을 사용하다보면 언젠가는 재미있어 질거라 믿고요.
이런 논의는 이런 식으로 흘러가기 마련: “이 막연하게 연관되어 있고 완벽하게 공정하신 데이터님께서 너의 아이디어가 절대로 성공할수 없음을 보여주고 있지. 많은 사람들은 너를 불쾌하게 여길 걸. 이건 명령이야. 우린 이걸할 여유가 없어”
코더들에 의해서 사용되어지는 테크닉인데, 코더가 특정 기능을 자기가 듣길 원하는 특정한 방식으로 듣기 전까지는 이해하기를 거부하는 것입니다.
개발자가 의도적으로(혹은 고의가 아니게) 너무 빤한 것을 이해하지 못하고, 잠재적으로 좋은 기능에 굶주려 있는 상태. 그 기능을 없앨때 까지 지속됨.
디자이너가 다른 사람에게 아이디어를 제공받고 그 아이디어에 대한 판정을 요구하고 있는 상태. 그동안은 자기스스로 만든 아이디어말고는 조용히 모든걸 무시한다.
정원사는 어떤 아이디어의 씨앗을 일찍 뿌려놓고 미팅과 평상시 대화에서 자꾸자꾸 그 이야기를 꺼냅니다. 결국에는 그 아이디어가 뿌리를 내리고 자라서 실재로 게임내 기능이 되고, 아무도 그 아이디어가 처음에 어디에서 왔는지 기억못하게 됩니다. 이것은 실제로 매우 유용한 테크닉이에요.
개발자가 개발중인 기능을 보여주기 시작합니다. 그 기능이 어디로 향하고 있는지, 무엇을 할 수 있는지는 생각하는것 대신에, 분명히 지금은 잘동작하지 않지만 궁극적으로는 완벽하게 고쳐질 버그(예: Z-fighting)를 보여주고 싶은 욕구를 느낍니다.
한 사람이 명백하게 정해진 상사나 명령 체계가 없는 상태입니다. 그래서 주로 어려 사람에게 어떤 일을 해야하는지, 어떤 일에 촛점을 맞춰야 하는지 전달받습니다. 그 사람의 디자인 디렉터나, 제작책임자, 사장님 각각은 그가 무엇을 해야할지에 대한 다양한 의견을 가지고 있고, 그를 혼란에 빠트릴 것입니다.
이 용어는 미디어에 어떤 기능을 약속하는 대변인을 의미합니다. 한번 대중에게 공개되버린 그 기능을 구현하느라 그는 팀을 궁지에 몰아세웁니다.
이 용어는 최근 해본 유명한 게임들에 들어간 기능이라면 무엇이든 추가하길 원하는 창의적인 사람을 의미합니다. 그것이 게임을 더 좋게 하는 방법이라고 하면서요. 혁신하기 위한 다른 방법은 찾지 않고요.
끝으로, 여기까지가 제가 이제까지 만난 성격과 테크닉들이었습니다. 블라블라..
다시 한번 원문 Cliff Bleszinski’s Game Developer Flashcards
기본 워드프레스에서는 각주가 지원이 안되는 군요 ㅠ 중간중간에 문맥이 끊기는 부분이 있네요.
gdb 상에서는
gdb) p *array@length 로 쉽게 할 수 있는데
xcode 에 있는 lldb 는 이게 지원이 안된다.
(lldb) p array
(int *) $8 = 0x0000000105600a50(lldb) memory read -fd -c10 0x0000000105600a50
0x105600a50: 1
0x105600a54: 2
0x105600a58: 3
0x105600a5c: 4
0x105600a60: 5
0x105600a64: 6
0x105600a68: 7
0x105600a6c: 8
0x105600a70: 9
0x105600a74: 0
즉 p (var) 로 시작 주소를 따온 다음에
memory read -f(type) -c(length) (start address)
로 보면 됨.
After therefore because of it
이 후, 따라서 이것 때문에
A이후에 B가 발생했으니 A의 이유는 B이다
항상 그런것만은 아니다.
책읽는데 다음과 같은 문장이 나왔다
Data-checking and verification is one of the most importantㅡif not the most importantㅡpart of graph design.
암만봐도 Data-checking과 verification 이 graph design 할때 가장 중요한 부분중 하나라는건 알겠는데
if not the most important 이게 도통 무슨 말인지 모르겠다
네이버 영영사전님께서 도움을 주셨다
3. used to suggest that something may be even larger, more important, etc. than was first stated
그리고 깨알같은 예문
Alex Ferguson was one of the, if not THE first
알렉스 퍼거슨은 첫번째가 아니라면, 첫번째들중 하나였을 것이다.
그래 이거야!!!
Data-checking과 verification 이 graph design의 가장 중요한 부분이 아니라면 가장 중요한 부분 중 하나이다.
이제 한국을 떠난지 1주일 정도 되었다.
어찌됐건 호바트에 잘도착했다. (이젠 공항이 무섭쥐 않아~)
주소는 20 Fizroy Place, Sandy Bay 7005, Hobart, Taz, Australia.
신기하게 지도에 영국지명 이름이 많이 보인다.
지금 살고 있는 집
이런 모습인데 처음에 봤을때는 진짜 놀랐다. 뭐 이렇게 큰거야 ㅋㅋㅋ
실내도 매우 넓고 좋다. 근데 단점은 난방따위 없다. 욜라 춥다. ㅋㅋ 특히 아침에 자고 일어났을 때의 그 오한이란;;
이것은 나의 방.
(침대곁에 있는 아이유 사진은 모른체 하자.)
처음에 왔을때는 이 잉여를 감당하지 못하고 (그렇다고 디아블로가 되는것도 아니고) 친구도 없고, 춥고, 배고프고 해서 집에 가고 싶었는데 하하..
컴투스에서 같이 일하던 Jeff, Joe, Peter, Ben 친구들의 마음이 이제야 이해가 된다. 돌아가면 더 잘해줘야지..
이제는 어느 정도 적응을 해서 그런지 부엌에 가서 잘 꺼내먹고 해먹고 한다. 하하..
막상 이렇게 보니 집이 완전 마르지 않는 샘같다. 정말 먹을게 많다. 까딱하면 몸무게 3자리 갈지도 –;
영어 공부는 지겹다, 이제. (이래도 못하니까 문제지만..)
재미없는거는 아닌데 아 역시 General English, 특히 단어부분은 너무 너무 약해서, 복습도 하고 해야되는데 귀차니즘 발동 ㅋㅋ
주말에 이 잉여를 잘 폭발시킬 수 있으면 완벽하게 적응할듯
스레드는 프로그램이 작업을 처리하는 실행 컨텍스트(execution context)
명령어 포인터(instruction pointer) : 현재 실행 중인 명령어의 위치를 가리킴
‘실행’ 과정
fetch : 스레드 코드에서 프로세서가 다음에 실행할 명령어를 가져옴
decode : 해당 명령어를 해독
실행
IP 를 조정 : 일반 명령어인 경우에는 단순히 증가, 분기나 함수 호출인 경우에는 적절하게 조정
레지스터는 물리적으로는 프로세서내에 있지만 레지스터의 상태 역시 스레드가 올바르게 동작하기 위해 필요한 정보 (스레드 실행이 멈췄다가 다시 실행될 때 레지스터의 상태를 복구해야만 함)
컨텍스트 스위치(context switch) : 레지스터의 상태를 메모리에 저장하거나 복구하는 과정
윈도우 스레드가 원시 코드를 실행하는 것이라면 CLR 스레드는 관리되는 코드(managed code)를 실행
OS는 관리되는 코드에 대해 아무것도 모르기 때문에, 윈도우 스레드에 CLR 에 관련된 정보를 덧붙여서 만듬
CLR 호스트(SQL 서버, ASP 닷넷)에서 메모리 관리, 처리되지 않은 예외를 다루는 방식 같은 정책을 변경할 수 있게 여러 인터페이스를 제공하고 이것을 위해서 윈도우 스레드와 CLR 스레드가 분리되어 있음
스레드풀 쓰세여
상태(state)는 동시성 프로그램을 작성할 때 중요한 개념
동일한 공유 메모리에 읽기와 쓰기 작업이 동시에 진행되므로, 적절히 처리하지 않으면 문제 발생(data race, race condition)
동시성 시스템에서 상태를 관리할 때 주로 사용하는 기법
공유 상태(shared state)란 무엇일까?
‘두 개 이상의 스레드에서 동시에 접근 가능한 모든 상태’
어떻게 구분할 수 있나?
동시성 시스템을 어떻게 구현했느냐에 따라 달라진다 (물론..)
공유는 전이적(transitive)인 특징이 있다
데이터의 공유상태가 변경 되는 작업을 데이터 공개(data publication)와 데이터 비공개(data privatization)라고 한다
공유 상태가 되면 객체에 대한 소유권이 어떻게 이전되었는지 파악하기가 힘들다
동시성 환경에서는 C 클래스의 동일한 인스턴스에서 여러 스레드가 동시에 f 메소드를 호출 할 수 있음
<공유 상태가 아닌 것>
<공유 상태인 것>
<정보가 부족한 것>
<공유 상태가 아닌 것>
<공유 상태인 것>
<정보가 부족한 것>
탈출 분석(escape analysis) 이란 기법으로 비공개된 메모리가 공유 메모리 영역으로 ‘탈출’하는 경우를 판단할 수 있음
but, 프로그래밍 언어에 제약을 두지 않고는 적용이 어려움,
false negatives (탈출하지 않았다고 했는데 실제로는 탈출한 경우) 우려
소유권 형식(ownership type) 같은 아이디어도 연구중
이래서 동시성 프로그래밍이 어렵다!
순차적으로 실행되리라 가정하고 작성된 코드라도 여러 쓰레드가 동시에 실행되면 시간이 겹치는 구간이 생김
a 가 가리키는 가상 메모리 주소에서 네 개의 바이트를 EAX 레지스터에 복사
EAX 레지스터의 내용을 1 증가
증가된 값을 a 가 가리키는 가상 메모리 주소에 복사
여러개의 하드웨어 명령어를 요구하는 소프트웨어 연산은 어떤 경우이든 비원자적(nonatomic)이다
최근 프로세서는 기본 워드 크기의 메모리(32bit 프로세서면 32bit, 64bit 프로세서면 64bit)에 대한 읽기나 쓰기연산은 atomic 하다고 보장된다.
그렇다면 뭐가 문제?
<운이 좋은 경우>
<전형적인 데이터 경쟁 상태>
결과는 2가 나와야 하지만 1이 나옴
원자적 연산 간에는 특정 연산이 다른 연산보다 먼저 처리되도 프로그램이 정상적으로 동작 가능하게 순서를 자유롭게 바꿀 수 있어야 한다. 물론 순서가 바뀌어도 프로그램의 처리 내용이 서로 동일해야 한다는 것을 의미하는 것은 아니며, 바뀐 순서에 의해 프로그램의 정확성이 근본적으로 바뀌어서는 안된다는 말이다.
상태를 특정 시점에서 특정 ‘에이전트’가 ‘소유’한 개념으로 볼 때 쉽게 이해되기도 한다
소유권 : 다른 컴포넌트나 에이전트 들이 해당 데이터에 동시에 접근할 수도 있음을 에이전트가 알고 있고, 이 경우에 에이전트가 연산을 수행할 때 읽기 및 쓰기 안정성에 어떤 영향이 있는지를 확실히 알아야 된다는 의미
소유권은 이전할 수 있지만, 세심한 처리가 요구됨
데이터에 대한 소유권 처리가 까다로운 이유는 데이터가 공유된 상태와 격리된 상태간에 잦은 전환이 일어나기 때문
또한, 데이터가 격리되는 시점을 정확하게 파악하기도 어려움
얕은 불변성(shallow immutable) : 객체가 살아있는 동안 해당 객체의 모든 필드가 변하지 않을 때
깊은 불변성(deeply immutable) : 객체의 필드가 참조하는 다른 객체들 역시 상태가 변하지 않는 경우
데이터가 비공개 상태와 공개 상태간의 전환이 자주 일어나는 경우 불변성을 활용하고 싶을 때에는 공유된 상태에서는 불변성을 유지하고, 비공개된 상태에서는 변경이 가능한 식으로 조건적으로 불변성을 지원하는 타입을 이용하는 것도 한 방법
CLR : initonly 한정자, C#에서는 readonly 키워드
C++ : const 한정자, type cast 를 통해 ‘상수성’을 없앨 수도 있음
정확성(정확한 코드를 구현할 수 있는지), 성능, 활동성(liveness), 확장성(프로세서 추가할 경우 성능이 향상되는가) 정도의 기준으로 동기화 구현을 정할 수 있다.
임계 영역(critical region) : 다른 스레드가 동시에 실행할 수 없고 순서대로 처리하게 설정된 일련의 코드 집합
S 코드영역을 실행할 수 있는 스레드는 한 번에 하나 뿐임 – 원자적으로 처리되는 것이나 다름없다
물론, S 에서 처리되는 공유 변수나 상태를 임계 영역 밖에서 처리하는 스레드가 없어야 하고, S 가 실행되면 중간에 실패하지 않는다는 가정이 필요
세마포어 : 지정된 개수만큼의 스레드가 임계 영역에 동시적으로 들어갈 수 있게 처리하는 기법, 다익스트라(Dijkstra)가 만듬
접근 카운트가 0 이상일 때 스레드는 카운트를 1 감소시키고 임계 영역으로 진ㅋ입ㅋ
상호 배재(mutual exclusion)가 보장되지 않는 예
동기화 자체도 이미 충분히 어려운데, 임계 영역을 여러 함수에 걸쳐 나눠 놓으면 더욱 구현이 어려워진다
고아(orphaned)락 : 락이 해제되는 것을 확실히 보장하지 않아 다른 스레드가 해당 영역에 들어가려고 할 때 들어갈 수 없는 것, 데드락의 원인
실제로는 처리 중간에 단순히 락을 해제하는 것만으로는 충분하지 않다
원자성을 보장하는 속성(순간적으로 처리된 것처럼 보여야 하고 처리 중간에 실패하지 않아야함)을 잘 생각해보면
문제가 발생하자 마자 락을 바로 해제하면 데이터가 손상될 수 있다
데이터 손상보다 차라리 데드락이 디버깅하기 쉬우므로 실패시 데이터를 되돌리지 힘들다면 그냥 두는게 낫다
모든 서브시스템이나 복합 데이터 구조의 모든 구성 요소를 보호하는 데 한개의 락만 사용
잘못된 공유(false sharing) 우려 : 프로그램이 정확하게 동작하는 데 아무런 상관이 없는데도 데이터에 대한 동시 접근을 막는 경우
서로 관련없는 데이터는 동시에 여러 스레드에서 처리될 수 있게 연관된 데이터 집합별로 락을 사용
프로그램의 활동성과 확장성을 높힌다
데드락의 우려, 데이터 경쟁을 피하기가 훨씬 어려움
임계 영역의 요구사항
최근에는 하드웨어에서 원자성을 보장하는 비교와 교환CAS(compare and swap) 연산을 사용해서 뮤텍스를 구현
상호 배제는 가능!
공정성은 없음. 임계 영역에 도착한 순서를 전혀 고려 안함 (정해진 순서대로 처리되므로)
즉, 아직 임계 영역에 들어가 있지도 않은 스레드 때문에 다른 스레드가 진입하지 못하는 상황 발생 -> 활동성(liveness)에 악영향
잘못된 경쟁은 성능이나 확장성을 저해시킴
스레드가 다음 스레드에 소유권을 넘기기 위해서는 임계 영역을 나가야 하므로, 스레드들이 주기적으로 임계 영역을 들락날락하는 경우에만 제대로 작동!
동적으로 스레드 수가 바뀔 때는 사용 못함
flags 라는 완충재를 하나 두었음, 그리고 두개의 스레드가 동시에 진입하는 경우는 turn 변수를 사용
다이직스트라는 이 알고리즘을 조금 바꿔서 N개의 스레드에서도 동작하게
(j == i || flags[j] != F.Active) == false 일 경우에는 j < N 이 되어서 기다림
대우는 (j != i && flags[j] == F.Active) == true 이고 이게 더 알아보기가 쉽다
위의 두 알고리즘은 컴파일러에서 코드 최적화 하는 과정에서 실행 순서가 바뀔 가능성이 매우 높은 요즘에는 제대로 동작하지 않음
임계 영역을 사용할 수 있거나 내 턴이 될 때까지 기다림
빵집(요즘으로 말하면 은행과 비슷하겠다)이 돌아가는 모습과 비슷하기 때문에 빵집 알고리즘이라고 불림
티켓을 뽑고 내 티켓번호보다 낮은 번호(먼저 온) 스레드의 처리가 끝날 때 까지 기다림
같은 티켓 번호를 뽑을 수 있기 때문에, 스레드끼리 구분할 수 있는 인자(thread_id) 같은게 있어서 이게 낮은 스레드가 먼저 처리됨
임계 영역 진입 처리가 공정해지는 장점이 있다
하드웨어에서 원자적으로 처리되는 비교후 교환(compare and swap) 명령어들을 지원하기 시작
Win32 와 닷넷 계열에서는 interlocked 계열의 연산으로 정의
CAS 함수는 세개의 인수를 받는데
CAS 함수의 리턴은 3의 값이 1의 포인터가 가리키는 메모리의 값과 같고 그래서 2의 값이 메모리에 저장되는 경우 true 를 반환
반대로 3의 값이 1의 포인터가 가리키는 메모리의 값과 일치하지 않으면 false 를 반환
입계 영역에 진입하려는 스레드는 taken 값이 0 일 때에만 taken 변수에 1을 쓰려고 시도하고 이 처리는 원자적으로 수행됨
다른 스레드가 임계 영역을 나가면 taken 값은 0이 되고, 계속 시도하던 스레드는 taken 변수에 1을 저장하고 진입
요즘 프로세서, 컴파일러, 런타임 들은 어떤 최적화가 적법하고 적법하지 않은지를 메모리 모델을 이용해 지정한다
Win32 나 닷넷 프레임워크를 사용하면 이런 메모리 모델을 정확하게 이해하지 않아도 됨, 대부부분의 경우 이런 도구만으로도 충분히 처리 가능 함
앞에서도 이야기 했지만 프로그램을 거대한 상태 기계를 모아 작성한 것으로 볼 수 있다
스레드도 자체적인 상태 기계에 따라 동작한다고 볼 수 있다
여기서 스레드가 처리하는 작업이란 메모리 읽기나 쓰기 도중에 스레드 고유의 상태나 프로그램의 상태에 전환을 가져오는 처리를 의미
여기서 상태가 깨지지 않으려면 적절한 동기화 처리가 필요
내부 상태와 외부 상태를 구분할 필요도 있다
내부 상태 : 스레드 처리 시 내부 구현에만 필요한 상태
외부 상태 : 시스템 내의 다른 스레드에서도 접근할 수 있는 상태
스레드는 뭔가 작업을 처리하기 위해 시스템 내의 다른 스레드들과 상호 작용할 필요가 있고 결과적으로 스레드 간에 의존성이 생김
약간 추상화 된 예시
조건 P 가 참인 집합을 SP 라고 하자
P 가 참인 경우 스레드가 작업을 진행할 수 있다면 실제로는 SP 에 포함된 여러 가지 상태 중 한가지 상태로 전환되기를 기다리는 것과 같다
조건 P 의 값을 검사하는 것은 실제로는 ‘프로그램이 특정한 상태에 있는가?’ 라고 묻는것과 같다
만약 답이 ‘아니오’ 라면 할 수 있는 것은 3가지 정도인데
2와 관련된 예가 임계 영역이다
S 는 P 에 의해 보호된다라고 함
루프를 도는 처리에는 CPU 사이클이 소모된다
대기 중인 스레드 또한 실행가능한(runnable) 상태로 유지되기 때문에, 진짜 필요한 작업을 처리하는 스레드가 실제 작업과는 상관없이 대기만 하는 스레드 뒤에서 프로세서를 할당받을 때 까지 기다려야 한다
결국 루프를 도는(busy wait) 스레드나 실제 작업을 처리하는 스레드 모두에게 성능 저하가 생긴다
OS 수준에서 제공하는 커널 객체들
이벤트를 이용한 대기 매커니즘
특정 스레드를 대기 상태로 바꾸거나, 대기 중인 스레드를 꺠울 수 있다
대기 중인 스레드는 실행 가능한(runnable) 상태가 아니라 대기(wait) 상태에 있고, 스레드가 대기 상태일 때 컨텍스트 스위치가 발생해 프로세서에서 바로 제고되고, 윈도우의 스레드 스케줄러는 다음번 실행할 스레드에서 해당 스레드를 무시하고 선택한다
대기를 사용하면 스레드를 스케줄링하기가 쉽지가 않다
함수형 프로그래밍(functional programming)에서 자주 사용하는 ‘나중에 할 일을 전달하는 방식 CPS(continuation passing style)’을 사용할 수 있다
이벤트가 일어나기를 기다리는 대신에, 남은 계산을 클로저 형태로 묶어서 API 로 전달하고, 이 API 는 대기 조건이 만족되었을 때 해당 연산이 다시 실행되기를 보장해준다
2장에서는 대략적인 내용을 많이 알아봤으니 더 자세한 내용은 다음 장들에서 나올 것이다