-
Notifications
You must be signed in to change notification settings - Fork 10
/
0106.txt
222 lines (222 loc) · 13.8 KB
/
0106.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
요즘 들어서 올바른 API 설계에 대해서 좀 생각을 되게
많이 하게 되요.
게임 회사 안에서 일하시는 분들이면 자체 Library쓰고
이러면 API가 엉망이면 뜯어 고치면 되니까 별 문제
없을텐데
엔진 팀이 따로 있다면 다른 얘기고요
예를 들어서 이제 엔진 제조 업체나 하드웨어 제조 업체 이런 곳들은 자체 API를 많이 제공 하잖아요
자체 API를 제공하면서 문서화를 잘해서 주는 사람도 있고 문서화를 안해서 주는 사람도 있고
문서화가 되던 말던 간에
문서를 제가 5,000장을 받든 10,000장을 받든
그 문서를 읽을 일은 솔직히 많지 않아요.
처음부터 끝까지 그 몇 백장을 읽을 사람도 없고
그래서 결과적으로는 옛날부터 그런 얘기 몇 번
들으셨을 거에요. 가독성이 좋은 코드를 써야 된다.
물론 그것도 제가 많이 생각하고 있는 문제고.
그 뿐만이 아니라 코드의 기본 behavior(동작).
이 코드를 이렇게 썼을 때
정작 내부 동작은 정확히 모르지만 그냥 상식 적으로
생각 했을때
이게 기본 동작 일거다 라는 그런
상식 수준의 그게 있잖아요
그 기본 동작을 어떻게 잘 맞추느냐도 중요한거고
그 기본 동작 얘기는 나중에 따른 비디오로
말씀을 드릴거 같애요
오늘 그냥 하고 싶은 얘기는 API 디자인 얘긴데
코드 함수만 딱 보고 '아 이거는 이렇게 작동을 하는거구나' 아니면
어떤 새로운 언어가 있을때 새로운 syntax만 보고
'아 이거는 이렇게 작동을 하겠구나' 라고
딱 봐서 알게 되는 그런 API 설계가 되게 중요해요
근데 봐서 모르겠다 그건 그나마 괜찮아요 왜냐면 봐서
모르니까 문서를 뒤지겠죠 질문을 하거나
제일 문제가 되는게
봐서 이해가 되는거 같은데 그게 잘못 된거야
제가 당연히 멍청해서 그런거 일 수도 있겠지만
상당히 많은 경우에는 오히려 하드웨어 제조 업체에서
바보 짓을 하는 경우가 더 많아요.
제 생각에는 아마도 소프트웨어 쪽에서 일하시는 사람들은 대충 일반적으로 저희가 쓰는 일반적인 뭐 프로세스가 있고 이름 짓는 방법이 있고 일반적으로 생각하는 그런 행동들이 있어요 모든 함수를 볼 때
저희 쪽은 보면서 당연히
'이 syntax는 이러니까, 저것도 저러니까 다 비슷하겠구나' 이렇게 보이는게 있는데
오히려 하드웨어 쪽만 설계를 하던 사람들이 API를 만들기 시작하면은
그 생각을 못하는거 같애요
뭐가 정말 읽기 좋은 거고 뭐가 안 읽기 좋은건지
생각을 못하는거 같애요
그래서 오늘은 한가지 예를 들어드릴건데
게임 콘솔 쪽에서 실제 있는 API 거든요
이거를 어느 콘솔에 뭐라고 말씀까지 드리면
분명히 저는 고소가 들어올거고
그거는 싹 빼고
shader 언어 였어요
이 shader가 좀 오래된 하드웨어고 그래서 shader 언어를 assembly 언어로 짜야만 되는 shader 언어 였어요
뭐 assembly 하면 놀라시는 분도 있고 너무 어려워
하시는 분들도 있는데
프로그래밍 하시는 분들한테 assembly가 솔직히 가장 쉬운 언어에요 쓰기에는
왜냐면은 assembly어는 딱 이것 밖에 없어요
변수가 있어요
뭐 A, B
뭐 Register라고 하죠 보통은
그게 있고 그 변수에 할 수 있는 명령어들이
몇 개가 있어요
그 대표적인 명령어가 move라고 해서
어떤 값을 그 register에 놓는다
뭐 C에서 보면 그냥 equal(=) 사인 넣으면 그거랑 똑같은 거에요
그리고 보통 add 가 있고요 더하기
그 다음에 이제 세개를 받아요 변수를
첫번째꺼는 최종적으로 들어갈 변수 아니면 register
그리고 나머지 두개는 더할 숫자 두개
아니면 그 숫자를 가지고 있는 register 두개
뭐 아니면 상수 뭐 어쩌고 이런거
그럼 뭐 비슷하게 곱하기 있고
더하기 곱하기 합쳐서 mad 라는거 있고
그리고 뭐 거기다 뭐 compare문 뭐 이런 것도 있고요
뭐 이 값이 0 하고 같냐 안같냐
같으면 여기로 점프해라
그래서 굉장히 간단한 코드에요
근데 이제 저는 shader 언어였으니까 보통
shader 언어는 float 네개를 한꺼번에 연산을 하잖아요
RGBA 이렇게 해갖고
그래서 거기서 이제 오는 건 Swizzling이라는게 있어요
그게 뭐냐면
예를 들어서 a 라는 register에 xyzw 네개가 있잖아요
그러면 이중에서 제가 x하고 z만 뽑아갖고 쓰고 싶어요
그러면은 저희는 그냥 a.xz 하면
그 두 값이 순서대로 나오는 거에요
그러면 이제 a.xz 하고 b.yz 하고 더해갖고
이거를 c.wx에 넣겠다
그럼 이게 다 되는거에요 c.wx = a.xz
b.yz 그랬던거 같은데
뭐 그래서 그게 한 문장으로 쓸 수가 있어요
굉장히 편한거죠
저는 그래서 이 shader 언어를 열심히 썼고
근데 이상하게 어디선가 버그가 생기는데
debugger를 붙일 방법도 없고
굉장히 오래 문제를 겪었어요
이리저리 돌려가면서 대충 이렇게 하면 되는구나
해결을 봤는데
어느 순간 정말 운이 좋게 잘 작동하던 코드가
제가 실수로 하나 바꿨는데 작동을 안하는거에요
그래서 그 순간에 이게 뭔가 이상하다
아까 말한 Swizzling이라고 했잖아요
이 규칙이 뭔가 이상하다라고 생각을 해서
열심히 문서 300페이지 짜리를 쫙 읽었어요
읽다 보니까 마지막에 딱 한 구절이 있는거에요
근데 그 구절이 정말 말도 안되는 API 디자인을 설명한거에요
어떻게 되는거냐면 이게
예를 들어서 가장 쉬운걸 예로 들어볼게요
Move 그리고 Move에서 a.xy에 b.xy를 넣고 싶다
저희는 일반적으로 생각 할 때 a.xy = b.xy 이러잖아요
아니면 우리가 지금 하고 싶은게
a.yz에 b.xy를 넣고 싶은거에요
그러면 우리는 당연히 생각 할 때
b.yz = a.xy 하면 들어갈 거 같잖아요
근데 이 API에서는 그게 아니에요
Shader언어를 해보신 분들은 다 알겠지만
이건 당연한거라고 생각 해야되거든요
근데 그게 아니더라고
이게 재밌는게 뭐냐면
아까 a 에다 b로 넣는다 그랬어요
그러니까 들어가는데 필요 없고
(멘붕)
a.yz에 b.xy를 넣는다고 해보죠
그럼 b.xy를 하면은 여기서는 b.xy가 올바르게 나와요
근데 여기서 저희는 y하고 z에 넣고 싶잖아요
그럼 우리는 당연히 x가 y에 들어가고 y가 z에 들어간다고 생각을 하죠
근데 이 언어에서는 그게 아니에요
이 언어에서는 xy라고 넣으면 이 x가 이 변수에 x에 들어가는거에요 사실은 근데
이 변수에 집어 넣으려고 할 때 여기는 x가 표현이 안되어 있으니까 이것을 masking out을 하는거에요 mask로 아예 짤라 내주는 거에요
그러면 x는 원래 일로 들어가는 거지만 이건 masking out이 되고 두번째의 y라고 해준거 있죠
그게 여기 두번째에 들어가는 거에요
그럼 아까 y에 이게 들어가고
그럼 z는 어떡해
여기서는 xy밖에 지정 안해줬잖아요 그죠
그럼 z에 들어갈 땐 지정을 안해줬기 때문에 마지막에 지정을 한번 반복해 주겠다는 거에요
그래서 결과적으로 이게 어떻게 내부적으로 되는거냐면
a.xyz에 이걸 넣는건데
x = masking out을 하는거고
그리고 실제 여기서 아까 뽑았던 그
b.xy가 있잖아요 그럼 이게 b.xyy로 반복이 된 다음에
이거를 다 집어 넣는거에요
그래서 xyz에 xyy를 집어넣으면서
첫번째껀 masking을 하니까 x를 빼고 yz에 yy가 들어가는 거더라고요
근데 보면서 이게 무슨 개소리야..........................
여기에 가장 큰 문제는 몇 가지가 있어요
일단 첫번째 문제는 이쪽에서 yz를 뽑아오는건
굉장히 올바른 방법이긴 한데
그거를
그 세번째꺼 아까 z에 넣고 싶은거 제가 직접 넣어주지 않았다고 해서 이걸 반복 시켜준다는거 자체가
원칙상은 옳은거죠 보통 Shader언어에서 많이 그러니까
하지만 이 상황에서는 이게 error를 줬어야돼
명백하게 하기 위해
그 이유는 두번째에요
이 destination에 들어가는 xyz. yz를 masking으로 뽑아 버린다는거 이거는 Swizzling이 아니에요 더이상
이거는 masking이에요
그래서 얘네는 한 문장 안에 destination 들어갈 때는 masking을 쓰고
그 source에서 뽑아올 때는 Swizzling을 쓰고 있다는
얘기거든요
한마디로 개소리죠
보기에는 똑같아 보이는 문법이 의미가 두개라는 거에요
그리고 이 두가지를 섞어 놓으면서
'여기서 생략을 하면 반복을 해줄게'
이게 여기까지 하면 잘못해서 제가 잘못 대입을 하고 잘못 가정을 세웠을 때 compiler error가 안나는거에요
그럼 나는 당연히 이게 작동하는 코드라 생각하고
쓰고 있는데 나중에 결과가 뭔가 이상해
그럼 debugging은 정말 힘들어지는거죠
그래서 진짜 문제는 여기서 그거였어요
masking 하고 Swizzling을 똑같은 문법으로
표현 했다는거
그리고 어느 destination이냐 source이냐에 따라 그게
의미가 달라지고 그리고 여기서 만약에 내가 실수로 뭐
z를 안썼을 때
compiler는 그게 당연히 내가 원하는 거라 생각하고
기본 behavior(행동) 자체를 반복하는걸로 해주겠다라고 하고 그냥 넘어간거
문제는 이 두가지가 되는거 거든요
제가 그거를 보고 몇몇 프로그래머한테 얘기를 했어요
다들 학을 때요 어떻게 그런 API가 나올 수 있냐고
저도 어이가 없어요.
제가 assembly어를 몇 번 해봤고 assembly shader도 몇 번 짜봤지만...이렇게 x판 친건 못 본거 같아요.
보면서 '저러니 저 콘솔이 괜찮은 게임이 안나오는구나 생각이 들 정도로 심각하더라고요.
이거는 제가 최근에 겪었던 예 일뿐이고 하도 황당해서 말씀을 드리는 거 뿐인데
이런 식에 API 디자인들이 좀 있어요
특히나 하드웨어 쪽을 열심히 만드는 회사일 수록
좀 그런게 있고
정확히 말하면 소프트웨어 공학 쪽으로 프로그램을 파는거 쪽으로 시작을 하지 않은 회사는 그런 문제가 좀 있어요
특히 나중에 하드웨어를 만들다가 소프트웨어를 만들기 시작하는 회사도 그런 문제가 좀 있고
Apple API만 보셔도 좀 아시는 분들은 그런 생각 하실거고 API가 이상하다 기타 등등
뭐 Objective C도 상당히 문제가 있는 언어...라기 보단 별로 손대고 싶지 않은 언어였죠
문법 상으로 다행히 Swift라는 언어가 나와서 다행이긴
한데
Swift가 Objective C보다 나은건 확실해요
제가 마음에 안드는 default behavior가 두개가 있어요 그건 나중에 말씀을 드릴 거고
그것도 상당히 하......
생각 없이 만든 부분이라고 생각해요.
default behavior를 굉장히 잘 설정하는게 중요한건데
Objective C에서 할 수 있는 실수들을 고치겠다고
새로운 언어를 만들면서
똑같이 실수를 만들 수 있는 여지를 그렇게 남겨둔다는게 좀 아쉬운 부분이 두어개 있었어요.
오늘 할려고 했던 얘기는
혹시라도 API 설계하시는 분들은
좀 그런 거를 생각을 하셔야 될거 같아요.
똑같은 문법에 두가지 의미를 넣지 말자라는 것도 되게
중요한거고
일반 사람들이 그냥 코드만 봤을 때 문서를 안본다고 가정하고 코드만 보고 뭐 매개함수(?)만 봤을때
'아 이게 이렇게 작동하는거다'
아니면 문법만 봤을 때 이게 이렇게 작동하는 거다라는 그런 일반적인 공감대를 가지고 있는 분야라면
괜히 이상한 짓을 하면 안될 거 같아요.
그런 의미에서 C++나 이런 Operator Overloading 하는 것도 한 때 굉장히 인기를 끌다가 갑자기 확 사그라든 것도 있잖아요.
옛날에 무슨 닷 product할때 곱하기를 썼다거나 뭐 이상한 걸 쓰면 뭘 하거나 이런 코드가 되게 많았거든요
요즘은 다행히도 Java부터 시작해서 C# 뭐 이런 언어들이그런 Operator들을 Overloading하는 거 보다는 실제
이름을 제대로 써서
String을 비교할 때도 뭐 equal 이런거 많이 쓰잖아요. 그렇게 의미 있는 함수를 써서 오히려 실수를 막고 명백하게 하려는 움직임이 있었기 때문에
다행히도 저희 C++ 프로그래머들 중에도 그런걸 좀 배워서 이제는 연산자를 이상하게 Overloading 하는 걸 피하려고 하죠
참 다행이라고 생각하고
요즘 어이없게 이런저런 새로운 언어도 많이보고 새로운 API도 많이 보고 새로운 콘솔도 많이 봐왔고 그렇게
됐어요.
아직
이런 큰 회사에서 나오는 콘솔 조차 이렇게 API 설계가 안될 정도가 될까라는 게 참 아쉽고
하드웨어 엔지니어링 하시는 분들 중에서는 그 정도 하드웨어면 뛰어나다고 생각하실 수 있겠지만
주로 소프트웨어 쪽이 백그라운드인 저로서는 좀 많이 아쉬운 부분이 많아요 그런 회사에서
정말 제대로 된 소프트웨어 아키텍 몇 명만 채용해서 그런 API 검토만 제대로 시켜도
자기네가 직접 안하더라도 실무에 있는 저희들한테 가지고 와서 미리 보여주고 그것만 제대로 하더라도
이런 문제는 고칠 수 있을텐데
콘솔 같은건 한번 나오면 5년 동안 못고치는게 문제니까
포프였습니다.