최근 DirectX를 사용한 간단한 게임을 TDD를 사용해서 작성하고 있다. 그런데 테스트 도중 D3DXVECTOR3 비교가 자꾸 실패하는 것이다. 스프라이트에 속력과 방향을 지정하고 1초가 흐르게 한 뒤에 정확히 목적지에 도달했는지 테스트하는 코드였다. 3차원 벡터를 사용하기 때문에 1초 후의 좌표는 부동소수로 나오게 된다. 그걸 직접 sqrt() 함수 등으로 계산해서 결과가 같을 것이라고 예상했는데 자꾸 다르게 나오는 것이다.

그래서 브레이크 포인트를 걸고 직접 양쪽 vector를 비교해 보았는데, 소수점 7째 자리부터 약간 달랐다. 당연히 float는 오차가 있지만, 문제는 내가 보았던 아래의 내용이다.

DirectX9를 이용한 3D GAME 프로그래밍 입문(정보문화사) 책에는 아래와 같은 내용이 나온다:

const float EPSILON = 0.001f;
bool Equals(float lhs, float rhs)
{
    // 만약 lhs == rhs라면 두 수의 차이는 0이어야 한다.
    return fabs(lhs - rhs) < EPSILON ? true : false;
}
D3DXVECTOR3 클래스를 이용할 때는 오버로드된 비교 연산자가 이 작업을 대신하므로 우리가 직접 신경 쓸 필요는 없지만, 부동 소수점을 비교하는 방법에 대해서는 반드시 기억하고 있어야 한다.

그래서 이 말대로 당연히 D3DXVECTOR3 클래스를 비교할 때는 오차를 감안해서 비교할 것이라고 생각했는데, 실제로는 그렇지 않았다.

예를 들면 아래와 같은 코드는 비교에 실패한다:

D3DXVECTOR3 torr1(0.0000001f, 0.0f, 0.0f);
D3DXVECTOR3 torr2(0.0000002f, 0.0f, 0.0f);
EXPECT_EQ(torr1, torr2);    // 여기서 실패!

뭔가 문제가 있는 듯 하여 브레이크 포인트를 걸고 비교문을 추적해 보았다.
그 결과 operator==()이 d3dx9math.inl 파일에 아래와 같이 구현되어 있었다:

D3DXINLINE BOOL
D3DXVECTOR3::operator == ( CONST D3DXVECTOR3& v ) const
{
    return x == v.x && y == v.y && z == v.z;
}
헐~ 그냥 == 으로 비교하고 있었다(참고로 x, y, z는 float 형이다). 이래서 오차 범위를 허용하고 있지 않았나보다. 

아무튼 책과 실제 동작이 다르니 한참을 헤메었고, 책을 쓴 사람이 제대로 테스트해본건지 의구심이 들 정도다. 아마도 DirectX가 버전업이 되면서 비교시에 오차를 허용하던 부분이 사라진 게 아닐까 하는 생각만 들 뿐이지만 확인하기가 어렵다.

사실 D3DXVECTOR3와 같은 기본형에 가까운 구조체는 자주 쓰이는 만큼 쓸데없는 연산을 최대한 줄여야 하는 것이 맞다. 심지어 생성자에서도 x, y, z를 0으로 초기화해주지 않는다(이것도 해주는 줄 알고 한번 테스트에 실패했었다).

D3DXVECTOR3 등으로 구글링을 해 보았는데 이와 같은 비교 문제는 보이는게 거의 없다. 다들 어떻게 쓰고 있는건지...??

그래서 D3DXVECTOR3를 랩핑한 래퍼 클래스를 하나 만들어 쓰려고 생각중이다. 여기서 operator==을 하나 정의해서 오차를 허용하도록 써야겠다.

일단은 임시방편으로 아래와 같이 쓰고 있다 -_-;

EXPECT_FLOAT_EQ(expected.x, sprite_.GetPosition().x);
EXPECT_FLOAT_EQ(expected.y, sprite_.GetPosition().y);
EXPECT_FLOAT_EQ(expected.z, sprite_.GetPosition().z);

참고로 테스팅 프레임워크는 GoogleTest를 사용 중이다.

TDD의 좋은 점 중 하나는 이런 식으로 잠재된 문제를 최대한 빨리 수면 위로 부상시켜서, 설계를 이른 시점에 수정할 수 있게 해주는 점 같다.

만약 이걸 당연히 되겠거니 하고 지나갔으면 아마 이상하게 조금씩 나는 오차에 고심하다가 겨우 원인을 찾아내서 D3DXVECTOR3를 사용했던 모든 부분을 뜯어 고치고 있었겠지...


반응형

도서관에서 빌려서 본 책.


프로그래밍은 사람이 하는 일임에도 불구하고, 프로그래머는 기계를 다루는 기계처럼 취급되어 왔다.

작업 일정 계산에 개개인의 개성과 관계자들의 커뮤니케이션 부하 등이 무시되기 일쑤다.


비자아적 프로그래밍을 해야 한다. 나도 전에는 다른 사람이 내 코드에 버그가 있다는 것을 알려줄 때 기분을 언짢아했었다. "그럴리가 없어요", "있을 수 없는 일인데", "제대로 보신 것 맞죠?" 등 버그를 대신 발견해준 사람에게 고마움을 표시하는 않을 망정, 의심의 눈초리를 보내기도 했었다. 그리고 실제로 내가 작성한 코드의 버그가 아닐 것이라고 믿었었다. 하지만 10에 9는 내가 작성했던 코드에 버그가 있었다.

현재는 내 코드에 버그가 있을 수 있다는 것을 완전히 인정하고 있다. 버그 보고가 들어오면 재현을 딱 한번만 해본다. 이제 이건 - 비록 의도하지는 않았지만 - 실제로 일어난 일이다. 바로 인정해야 한다. 전혀 '이상한' 현상이 아니다. 이전에는 재현을 두번 이상 해보면서(거의 열번 가까이 해본 적도 있다) '이상한데'라고 생각했었다. 하지만 상당한 시간 낭비일 뿐이었다.

한번 현상을 봤으면 코드의 어떤 부분이 그런 문제를 일으킬 수 있는지 생각해본다. 그리고 그곳으로 가본다. 고친다. 다시 테스트한다.

될 수 있으면 디버그 출력을 하는 것은 최후의 수단으로만 사용한다. 디버그 출력을 하면서 따라가기 시작하면 대체로 버그가 있는 곳을 정확히 예상해서 출력해보기 보다는, 이곳저곳에 남발해놨다가 디버그 출력이 초당 수십개 올라가는것을 바라보면서 스트레스를 크게 받는 경우가 많았다.


프로그래밍에 심리 상태가 크게 영향을 끼친다는 사실은 일정 압박이 심해질 때 버그 발생율이 급격히 상승한다거나, 그런 경우 "거의 다 됐습니다"를 4주째 반복하는 등의 현상을 바라보며 알 수 있다.



반응형

'일기' 카테고리의 다른 글

Test Driven Development, 첫 걸음부터 세미나를 하기까지  (0) 2010.10.10
레거시 코드와 씨름 중  (0) 2010.10.10
ONE MUST FALL 2097  (0) 2010.02.27
으아앙대  (0) 2010.02.26
Misirlou 연습중  (0) 2010.02.17

TDD(Test Driven Development)를 시작하고 있다. 


C++에서의 TDD의 힘겨움

그런데 유명한 TDD 책들을 보니 죄다 Java 언어로 되어 있다.
그 유명한 녹색 막대도 Eclipse IDE에 프로그래스바 플러그인이 JUnit과 함께 녹아들어가 있어서 가능했던 것이었다.

C++ 진영에서는 테스팅 프레임워크가 좀 많다. 그렇지만 IDE 애드온을 제공하는 곳은 없기 때문에 아무래도 까만 콘솔 화면밖에 볼 수 없는 것 같았다.

TDD는 안그래도 시작하기가 힘든데, 이렇게 프레임워크가 다양해서 막막했다. 대체 뭘 골라야 잘 골랐다는 소리를 들을까? Java의 JUnit과 같이 표준스러운게 있다면 선택의 여지도 좁지만 반대로 시작하기는 훨씬 쉬웠을 것이다.

노엘의 홈페이지
CppUnit
CppUnitLite
CxxUnit
UnitTest++
GoogleTest
등등...

너무 녹색 막대가 보고 싶어서(나도 'Green Bar' 보고 싶다고 ㅠㅠ) 처음에는 vutpp + CppUnitLite를 사용했다. 처음에는 녹색 막대를 본다는 사실에 많이 들떠있었지만, 버그도 좀 있고 사용하기가 생각보다 그닥 편하지가 않아서 다른 것을 찾아보게 되었다.

찾아보다 느낀 사실은 Windows + VisualStudio + C++ 조합의 개발자가 적지 않은데도 제대로 된 오픈소스 VS IDE Addon이 없다는 사실이다. TDD 책에 보면 빨간 막대 -> 녹색 막대 -> 리팩토링 이라고 말하는데 그건 자바 사정이고 C++에서 녹색 막대 보기는 물건너간것 같다.


VisualAssert의 발견

라고 생각하던 와중에 VisualAssert라는 VS Addon형 테스팅 프레임워크를 발견했다. 이거 아주 쓸만하다. 초보자라면 CppUnitLite 만큼 강추한다.

이것도 좀 쓰다 보니 문제가 있었는데, 테스트 도중 하나라도 실패하면 전체 테스트가 멈춘다는 것이다.


GoogleTest의 발견

그래서 결국 GoogleTest로 왔다.

역시 구글은 다르다는 느낌을 소스만 보고도 느꼈다. 비록 콘솔창으로 결과가 출력되지만 텍스트에 색을 입혀서 성공과 실패를 금방 알 수 있게 해준다. 녹색 막대를 못 보는 사람들을 세심한 배려(?)가 아닐 수 없다.

C++에는 리플렉션이 없어서 테스팅 프레임워크마다 하나씩 아쉬운 점이 보였는데, GoogleTest에서는 그런 점을 상당 부분 개선하려고 한 부분이 보인다.

예를 들면 특정 문자열로 시작하는 테스트만 실행시킬 수 있는 등 필터 기능과, 에러가 발생했을 때 디버거를 붙일 수 있는 기능 등 실제로 필요하다고 생각되는 기능은 다 있는 것 같다. 그리고 Windows도 차별 없이 잘 지원해주고 있다.

현재 모든 프로젝트는 GoogleTest를 사용해서 진행하고 있다.

반응형
결론부터 말하자면
 
1. 링크드 리스트 당 하나의 동기화 객체를 사용할 것.

2. 성능을 위해 Read Write Lock(또는 Shared Exclusive Lock이라고도 함)을 사용할 것.
 (동시에 여러명이 Read 가능, 한명만 Write 가능)

3. 리스트가 길어져서 탐색 시간과 대기 시간이 증가할 것 같으면 해시 테이블을 사용하고, 각 버킷별로 하나의 동기화 객체를 사용할 것.



멀티스레드 프로그래밍
에서 링크드 리스트(Linked List)를 다루다 보면 마주치는 문제가 있다. 바로 동기화이다.

이게 접근할 수 있는 스레드가 한개면 상관이 없는데, 여러개일 경우에는 push와 pop이 동시에 진행되면서 잘못된 메모리 참조를 하고 프로그램이 죽어버리는 상황이 발생할 수 있다. 실제로 이 점을 전혀 염두하지 않고 프로그램을 만들었다가 프로그램이 피를 토하며 죽고 나서야 실수를 깨달았던 경우가 있었다. 그 후부터는 항상 리스트를 만들 때 동기화가 필요한지에 대해 생각한다.

대개 이런 상황에서는 리스트 한개당 하나의 크리티컬 섹션을 사용해서 동기화하는 방법을 사용했다. 그런데 리스트가 길어지다 보면 탐색 시간이 오래 걸리기 마련인데, 만약 여러 스레드가 하나의 리스트를 '읽으려' 할 때 동시에 읽어도 되지만 크리티컬 섹션에 막혀서 하나씩 대기를 타고 있는 비효율적인 상황이 발생한 적이 있다.


이 문제를 해결하기 위해서 처음에 생각해 낸 방법은 '각 노드마다 동기화 객체 사용'이었다. 리스트 전체를 잠그는 대신 하나의 노드만 잠그고 읽거나 쓰면, 잠기지 않은 다른 노드는 다른 스레드가 접근할 수 있기 때문이다.

그런데 생각하다보니 문제가 하나 생겼는데, 일단 노드들이 주루룩 있는 상태에서는 각 노드를 잠가서 읽거나 쓰는(노드의 내용을 수정하는)데는 문제가 없었다. 하지만 링크를 끊거나 연결하는 과정에서 문제가 발생할 것이 분명했다.

동기화라는 것은 원자적(아토믹, Atomic)이지 않은 여러 개의 명령을 원자성을 가지도록 해주는 것이다. 원자적인 명령 단위는 멀티스레드 환경에서도 안전하다. 자세한 내용은 생략.

위에서 말한 그 문제는, 링크드 리스트에서 노드를 추가하거나 삭제할 때 연결을 맺고 끊는 과정이 원자적이지 않아서 발생한 것이다. 앞 노드의 연결을 수정하고, 뒤 노드의 연결을 수정하는 두개의 과정이 있기 때문이다. 그럼 락 걸고 앞뒤로 수정한 다음에 언락하면 되는거 아냐? 말은 쉽지만 1번 노드에 락 건다고 2번 노드까지 잠기는게 아니니까.

고민하던 끝에... 그래! 그냥 락 앞뒤로 걸어버리자! 하고 생각했다.

이론적으로 될 거라고 생각했다. 하지만 데드락과의 사투 끝에... 결국 내린 결론은 리스트 통째로 락 거는게 가장 낫다는 것이었다. 동기화 로직에 들어가는 코드의 양이 비대해지고 복잡해지면서 과연 다른 사람이 이 코드를 보고 이해할까 하는 생각이 드는 것은 물론이었다. 어찌어찌 해서 사실 완성은 했지만, 결국 이 코드를 버렸다.

쓰기를 할 때 뿐만이 아니라 읽기를 할 때도 필요한 노드를 포함해서 앞뒤 3개의 노드에 락을 걸어야 했다. 물론 데드락이 생기지 않게 왼쪽 오른쪽 자신 순서로 걸도록 했지만...

이제 여러 개의 스레드가 읽는다고 쳐보자. 락과 언락에 걸리는 시간때문에 오히려 퍼포먼스가 떨어질 지경이다.

그래서 버렸다. 사실 성능 측정으로 비교해보지는 않았다. 하지만 그렇게 직감이 들었다.


그 대안으로 해시 테이블을 선택했다. 말하자면 리스트를 쪼개서 분산 처리하는 것이다. 테이블의 각 버킷을 링크드 리스트로 만들면 된다. 그리고 각 버킷별로 동기화 객체를 사용하면 된다. 동시에 읽을 수 있게 Read Write Lock을 사용하면 더 좋다(Read Write Lock은 Interlock을 사용해서 직접 구현했다).

말은 마치 해시 테이블을 교묘하게 이용한 링크드 리스트처럼 했지만, 그냥 해시 테이블을 쓰는거다. 충돌이 발생하면 링크드 리스트로 이어가는 것일 뿐이지.

해시 테이블은 링크드 리스트보다 조금 더 메모리를 소모한다. 하지만 많은 데이터가 있어도 검색과 삽입, 삭제가 빠르므로 현업에서 자주 사용하고 있다.

반응형

'C, C++ 일반 > 알고리즘' 카테고리의 다른 글

알고리즘 문제 사이트  (0) 2010.11.04

High Cohesion, Low Coupling

진리이다. 항시 기억하자.


예를 들면 인체는 결합도가 높다. 뭔가 하나 수정하려면 주변에 영향을 주는 것이 많아서 굉장히 힘들다. 의사들을 보면 엄청나게 공부를 해야 하고 기억해야 할 것도 많다. 게다가 크고 작은 의료 사고(버그)도 끊이지를 않는다. 환자가 알아서 잘 설명하지 않으면 의사도 어디가 문제인지 모른다.

이번엔 결합도가 아주 낮은 전구와 소켓을 보자. 전구가 나갔으면 전구를 돌려서 슥 뺀다음 새 전구를 끼우면 된다.

반응형

참고 게시물: http://youtil.tistory.com/124 (한번 읽어보자)


토렌트 다운의 운영자가 FDISK에게 회원 정보를 팔아먹은 사건이 터졌다.
여기서 아이디@07, 비번@07 이런식으로 회원들 DB를 넘겼다고 했다.

충격적인 부분이다. 비밀번호를 암호화하지 않았던 것이다. << 지금 이 글의 핵심이다!!!

애초에 토렌트 다운의 운영자는 누구라도 아이디와 비밀번호를 '눈으로' 직접 확인할 수 있었다는 말이다.
(지금은 FDISK의 운영자도 볼 수 있을 것이다)

비밀번호를 암호화했다면 DB에 들어가있는 비밀번호 정보가 애초에 인간이 읽을 수도 없고 역변환도 되지 않도록 해시 알고리즘 등으로 변환해서 들어가 있기 마련이다.

그런데 자신의 원래 비밀번호끝에 @07 이런식으로 텍스트가 붙었다는 것은 비번이 텍스트로 그대로 저장되어있었다는 이야기다.


예를 들어 회원가입 시 이렇게 입력했다고 치자:
ID: test
PW: myPassWord

회원 DB를 암호화하는 사이트는 다음과 같이 저장할 것이다:
 (예시일 뿐 사이트별로 어떤 방법을 쓰는지는 알 수 없다)
ID: test
PW: 4623336A7094BDB9FD626A7C82A97D811EB7A530

하지만 회원 DB를 암호화하지 않는(토렌트 다운같은) 사이트의 관리자는 그냥 보인다:
ID: test
PW: myPassWord


만약 비밀번호를 암호화해서 저장했다면 어떻게 뒤에 "@07" 같은 텍스트를 붙일 수 있었을까??
애초에 원래 비밀번호를 알 수도 없었을 것이다.

만약 이 글을 보는 지금 동일한 ID를 사용하는 사이트가 있다면 당장 비밀번호를 바꿔라.


토렌트 다운의 운영자 '도너츠'의 변명이 더 기가 막힌다 ^^

개인정보 유출은 절대 발생하지 않았으며 타 사이트로의 가입도 진행되지 않았습니다.
이게 개인정보, 그것도 비밀번호 유출이 아니면 무엇인가? 해당 사이트 운영자 도너츠는 지금이라도 경찰에 정보통신법 위반으로 자수하길 바란다.


참고로 어떤 사이트를 가입할 때는 비밀번호를 운영자가 볼 수 있다고 생각하는 것이 안전할 것이다. 사이트별로 비밀번호를 다르게 하고, ID 역시 신뢰할 수 있는 IT 계열 대기업이 아니면 다르게 만들어 쓰기를 권장한다.
반응형


ONE MUST FALL
 
초딩 시절 죽어라 했던 게임.
 
 
동생이 기억나게 해주었다.
 
그때 들었던 배경음악 아직도 선하다.
 
 
지금 보니, '누군가 하나는 떨어져야 한다'는
 
조금은 가슴아픈 현실의 제목이다.
 
 
1994년 당시 개발사였던 Epic MegaGames는 현재 국내, 국외의 많은 게임 개발사들이 수억원, 수십억원씩을 주고 구입하여, 혹은 로열티를 지불하여 사용하게 되는 Unreal Engine 시리즈를 만든 Epic Games가 되었다.



OMF 2097 배경음악 듣기: http://blog.naver.com/exacall/140061810197


반응형

'일기' 카테고리의 다른 글

레거시 코드와 씨름 중  (0) 2010.10.10
프로그래밍 심리학  (0) 2010.09.15
으아앙대  (0) 2010.02.26
Misirlou 연습중  (0) 2010.02.17
이틀째  (0) 2010.02.16

전역 변수를 사용하면 안되는 이유, 함수가 길어지면 안되는 이유가 무엇일까? 인간의 초 단기 기억력은 한 번에 7가지 정보만을 동시에 기억하고 있을 수 있기 때문이다. (관련글: 두뇌이야기 (4) - 워킹메모리를 늘리는 법)


함수를 짧게 만들어야 하는 이유

우리가 코드를 보는 중에 빠르게, 그리고 오래 기억하고 있을 수 있는 사실들은 매우 적다. 예를 들어 지금 지역 변수가 7개를 초과한다면 함수 분석 중 변수의 변화를 더 이상 따라갈 수 없게 된다. 또 if문이 2중, 3중으로 중첩되거나 함수가 길어질 경우에도 그 이전 기억을 모두 붙잡고 있어야만 이해할 수 있기 때문에 이해하기 어려워진다.


전역 변수를 사용하면 안되는 이유

전역 변수의 경우에는 전역 변수가 언제 변하는지 파악하기가 아주 힘들다. 함수 중간에 다른 함수를 호출했는데 내가 쓰고 있던 전역 함수 값이 과연 바뀔까 안바뀔까? 그 밑 함수를 줄줄이 따라가봐야만 알 수 있게 된다.

처음에는 이곳저곳에서 편하게 쓰겠지. 하지만 나중에 가면 전역 변수는 '누구나 건드릴 수 있다'는 점이 문제다. "대체 어떤 자식이 이 변수값을 바꾸는거야" 혹은 "이게 대체 왜 바뀌는거야" 하고 생각하게 될 것이다.





반응형

IT와 스타벅스 커피를 비교한 기사.

1차, 2차, 3차 산업과 하드웨어, 소프트웨어, 컨텐츠를 빗대어 비교했고, 현재 크게 성장중인 IT 산업 - 소셜 네트워크 - 과 새로 생성된 4차 산업(말하자면, 스타벅스)과의 관계를 알기 쉽게 풀이했다.

"소비자가 원하는 것은 무엇인가?"

원가는 몇십원 혹은 몇백원밖에 안하는 커피에 비싼 돈을 지불하는 소비자의 심리를 이해할 수 있는 것은 덤이다.


링크: http://www.bloter.net/wp-content/bloter_html/2010/02/26367.html

반응형

얼릉 개장을 해야되는데

글도 쓰고 위젯도 달고 스킨도 이쁘게 꾸며야되는데!


막상 글을 쓰다 보면(혹은 쓰자고 생각하면) 왜이리도 내 마음은 다른 생각을 하는지 모르겠다.


마치 숙제는 해야되는데 일단 놀고 보자는 심리랑 비슷한 것 같다.

게다가 이 글쓰기라는건 기한이라는 것도 없어서 계속 놀게 된다.



사람들이 돈을 내고 학원을 가는 이유, 요가를 다니는 이유.

한 번 하는 방법을 배우면 집에서도 할 수 있다.

하지만 안하게 된다. 돈을 내면 그 돈이 아까워서라도 가게 된다(미성년자의 경우엔 집에서 혼날까봐인 경우가 많을 것이다).

즉 동기가 유발되는 효과가 있는 것 같다.



그렇다면 내가 글을 쓰지 않는 이유도 동기가 없기 때문일까??


동기는 있다. 말하기는 촘 부끄럽지만, 유명해지고 싶고, 방문자 수 올라가는거 보고 싶은거. 그밖에 부수적 효과로 다 내 포트폴리오가 되고 자기 계발도 되고 블로깅하다보면 아는 사람도 많아지고 등등등~

유치하지만 원초적인 본능(?)이다.


그런데 글을 쓰면 마치 숙제를 할 때와 같이 심리적인 압박 또는 .. 뭐랄까 스트레스가 온다. 피하고 싶은 그 느낌.

코드도 삽입해야되지, 코드 이쁘게 넣으려면 뭔가 플러그인같은걸 찾아야되는데 (어제 SyntaxHighlighter 찾았다)

또 이거 업로드하고 삽입하려니 귀차니즘이 하늘에 찌를락말락하네여 ^^


결국 동기보다 귀찮은 것이  큰게 글을 안싸게 되는 원인이었던 것 같다.

그렇다면 동기를 더 강하게 하거나 귀찮지 않게 하면(이건 불가능해 보인다) 글을 잘 쓰겠지??


작가들이 탱자탱자 놀다가 편집부장의 전화에 마감일 전날부터 밤새 원고 쓰는 심정을 알 것 같다.

하긴 우리 컴퓨터학도들도 학기 과제(텀이라고 한다) 마감 3일전부터 밤샘코딩으로 완성해서 보고서까지 써내니 비슷하긴 하다.

반응형

'일기' 카테고리의 다른 글

프로그래밍 심리학  (0) 2010.09.15
ONE MUST FALL 2097  (0) 2010.02.27
Misirlou 연습중  (0) 2010.02.17
이틀째  (0) 2010.02.16
블로그 시작  (0) 2010.02.16

+ Recent posts