2006년 11월 13일

개발자가 읽어야 할 필독서 모음

예전에 KLDP에서 "컴서적 명예에 전당에 들어갈 책은 ?" 이란 주제로 관심을 끌어던 내용이다. 현재 자신이 필요한 서적이 무엇인지 찾는데 도움이 될것이다.

이녀석들 뿐만 아니라 역대 Jolt 상을 수상했던 목록도 찾아보면 쏠쏠한 재미(?)를 느낄수 있을 것이다.

개발자가 놓지지말아야할 책 베스트70

Thinking In Java/Bruce Eckel => 3판의 경우 웹에서 열람가능하다. (http://www.mindview.net/Books/TIJ/) Effective Java 도 관심을 갖자
Practical C Programming/Steve Oualline
Instant CORBA/Robert Orfali,Dan Harkey,Jeri Edwards
Modern Database Management/Fred R.McFadden,Jeffrey A.Hoffer,Mary B.Prescott
Programming Pearls/Jon Bently => 현재 2판이 나와있다.
Effective C++/Scott Meyers => 3판이 나온듯 하다.
Unix Network Programming/W.Richard Stevens
MicroC/OS-II The Real-Time Kernel/Jean J.Labrosse => 2판이 나왔다.
Unix Internals:The New Frontiers/Uresh Vahalia
Extreme Programming Installed/Ron Jeffries,Ann Anderson,Chet Hendrickson


개발자가 놓지지말아야할 책 베스트40

Macintosh Human Interface Guidelines/Apple Computer Staff
Design Patterns/Gang of Four => 2판이 나왔으면 좋겠다 ~_~;
Refactoring/Martin Fowler
The Pragmatic Programmer:From Journeyman to Master/Andrew Hunt,David Thomas,Ward Cunningham(Preface)
Peopleware:Productive Projects and Teams/Tom DeMarco & Timothy Lister
Linkers and Loaders/John R. Levine
Client Server Database Enterprise Computing/James Martin
DataWareHouse From Architecture To Implementation/Bary Devlin
Operation System Design-The XINU Approach/Douglas Comer
Writing Solid Code/Steve Maguire
Algorithm+Data Structure=Programs/NIclus Wirth
Code Complete/Steve McConnell
Component Software:Beyond Object Oriented Programming/Clemens Szyperski
Software Reuse-Architecture,Process and Organization for Business Success/Ivar Jacobson,Martin Griss,Patrik Jonsson
Extreme Programming Explained/Kent Beck
Applying UML and Patterns,2nd Ed/Craig Larman
The Java Programming Languages, 3rd Ed/David Holmes,James Gosling,Ken Arnold
리눅스 완전분석으로 가는 길/박장수
Operating System Concept/Abraham Silberschatz
TCP/IP Illustrated Volume I,II,III/W.Richard Stevens
Advanced Programming in UNIX Environments/W.Richard Stevens
Understanding COM+/David S.Platt
Compilers: Principles,Techniques and Tools/Jeffrey D.Ullman
Numerical Reciples in C/William H.Press
The C++ Programming Language Special Ed/Bjarne Stroustrup
Effective STL/Scott Meyers
Professional Jini/Sing Li
C++ Primer/Stanley B.Lippman,Josee Lajoie
대용량 데이터베이스 시스템/이화식,조광원
Armchair Universe/A.K.Dewdney
Writing for Computer Science/Justin Zobel
The C Programming Language/Brian W.Kernighan,Dennis M.Ritchie
Bugs in Writing Revisted:A Guide to Debugging Your Prose/Lyn Dupre
The Design of The UNIX Operationg System/Maurice Bach
Building Business Objects/Peter eles,Oliver Sims
The Art of Computer Programming:Fundamental Algorithms/D.Knuth
Professional ATL COM Programming/Ricard Grimes
Pattern-Oriented Software Architecture, Volume 2/Douglas Schmidt
Inside Java2 Virtual Machine/Bill Venners
Understanding ActiveX/COM/David Chappell


개발자가 놓지지말아야할 책 베스트20

Fundamentals of Data Structues in C++/Ellis Horowitz,Dinesh Mehta
Computer Networks/Andrews.Tanenbaum
Modern C++ Design/Andrei Alexandrescu
Database System Concepts/Abraham Silberschatz,Henry F.Korth,S.Sudarshan
Modern Database Management/DaFred R.McFadden,Jeffrey A.Hoffer,Mary B.Prescott
Data Mining:Concepts and Techniques/Jiawei Han,Micheline Kamber
The Design and Implementation of the 4.4BSD Operating System/Marshall Kirk McKusick,Keith Bostic,Michael J.Karels
UNIX Power Tools/Jerry D.Peek,Tim O’Reilly,Mike Loukides
The Unix Programming Environment/Brian W.Kernighan,Rob Pike(Contributor),Robert Pike
The Cathedral & The Bazaar/Eric S.Raymond
The Society of MIND/M.Mmsky
Fundamentals of Object Oriented Design in UML/Meilir Page-Jones
Computer Organization and Design:The Hardware/Software Interface/David A. Patterson, John L. Hennessy
Design Web Usability The Practice of Simplicity/Jakob Nielsen
Introduction to Algorithms/Charles E.Leiserson,Ronald L.Rivest, Thomas H. Cormen
Introduction to the Team Software Process/Watts S.Humphrey,Marc Lovelace
Mythical Man Month/Frederick P.Brooks
The Psychology of Computer Programming/Gerald M.Weinberg
After the Gold Rush/Steve C McConnell
Structure and Interpretation of Computer Programs - 2nd Ed/Harold Abelson,Gerald Jay Sussman,Julie Sussman

2006년 11월 12일

메모리 할당에 대해

이 블로그는 John Bentley의 동명(Programming Perals)서적을 읽던도중 시작이 되었기에 여기에 실린 주옥같은 내용을 실어보도록 하자.

우리가 주로 접하는 32비트 범용레지스터 크기를 가진 프로세서들은 성능을 위해 메모리 접근를 최소화할뿐 아니라 한번에 많은 메모리정보를 얻기위해 4바이트의 배수이길 갈구한다.
때문에 컴파일러 역시 기본적으로 데이타를 4의 배수로 나열하려는 욕심을 가지고 있다.

컴파일러들은 최적화라는 사명아래 기본적으로 구조체 내에서 가장 크기가 큰 타입을 기준으로 삼는다.

예를 들어 int, char 멤버가 있다면 int를 기준으로 하여 4(x64 라면 8바이트)바이트로 팩 하려 든다. 혹자를 이를 낭비라고 하지만, 이는 성능을 위한 컴파일러의 선택이며 #pragma pack 키워드를 통해 프로그래머가 강제할 수 있도록 지원하고 있다.

노련한 프로그래머라면 구조체 멤버의 순서 바꾸기를 통해서 팩없이 해결할수도 있을 것이다. - 그냥 성능보다는 편안함을 추구한다면 구조체를 1바이트로 팩할수도 있다. 소켓 통신용 프로토콜을 디자인하는경우 흔히 사용한다.



테스트 결과 매우 유동적인 메모리 할당 방식을 확인할수있다.
필자의 PC에서는 12바이트 구조체를 연속으로 new 했을경우 리턴된 포인터의 간격이 56바이트나 차이났다.



결국 12 바이트를 할당해 달라고 했는데 내부적으로 malloc 를 사용해 56바이트나 할당해 44바이트를 낭비하고 있다. 왜 이런 낭비를 부추길까?

디버거를 통해 확인해 보면 malloc()이 호출될때 마다 리턴되는 주소의 앞에 데이타를 변경하는것을 볼수있다. malloc()에 의해 리턴되는 포인터의 위치가 데이타 바디라면 그 앞쪽에는 우리가 모르는 헤더정보가 있는 셈이다.


리턴된 포인터의 16바이트 앞쪽을 조사해 보면 0x00000c 필드도 찾아볼수있다. 즉 12바이트를 할당했다는것을 내부적으로 기록하고 있다. - delete 할때 이 정보가 필요할 것이다!

또한 CPU의 빠른 접근을 위해서 4의 배수로 할당하는것이 현명하므로 리턴되는 주소값이 항상 8바이트의 배수인것을 확인해 볼수있다. 결국 내부적으로 malloc()을 호출하면서 리턴되는 주소의 앞에 특정정보를 기록하기 위해 항상 어느정도의 공간을 남겨놓아야 한다.

자세히 살펴보면 추가되는 공간이 최소한 48바이트며, 그 보다 크거나 작다면 8바이트의 배수를 맞추기 위해 패딩을 실시하는것을 볼수있다. 결국 1바이트를 new 하더라도 내부적으로 48바이트를 할당하게 된다. - 컴파일러에 따라 다를수있다

- TCP/IP 프로토콜에서도 이런 패딩 기법을 사용한다. 만약 자신이 어떠한 프로토콜을 디자인해야 한다면 하드웨어의 사랑을 받기위해서라도 이런 팩, 정렬, 패딩을 고려해야 한다.

2006년 11월 11일

어떤 자료구조를 사용하느냐가 성능을 좌우한다

C++ 를 사용하는 프로그래머들이 한결같이 감탄해 마지 않는 것은 바로 표준으로 지원되고있는 STL(Standard Template Library)일것이다. 바로 신이내려주신(?) 선물인 템플릿을 통해서 말이다.

어찌됐듯 STL의 대표적인 컨테이너인 벡터(vector)에 대한 성능에 대해 논하고자 한다.
다들 알다시피 벡터는 동적배열의 추상화된 자료구조이다.

벡터는 메모리 공간에서 선형적으로 존재하며, 상수 시간에 가장 끝에 데이타를 삽입할수있고, O(1) 상수시간에 접근이 가능하다. 반면 가장 앞에 삽입이 거의(?) 불가능하고(가능더라도 O(N)이므로 왠만하면 deque를 사용하자) 중간에 데이타를 삽입,삭제할경우 그 뒤에 있는 녀석들(O(N))이 모두 이동되어야 한다.

자 이정도로 기억을 되살리고...

std::vector _vector;
for(int i...)
_vector.push_back(i);


우리는 위 코드에서 상수시간안에 데이타를 모두 삽입해 줄것이라는 실수를 하곤한다.
그러나 _vector.reserve()를 적절히 사용했을 경우만 해당된다.

알다시피 상수시간만에 가장끝 원소를 찾아갈수는 있지만 삽입을 위해서는 필연적인 메모리 공간이 요구되기 때문에 당연히 미리 할당이 되어있어야 한다.
그러나 기본생성자로 생성한 _vector는 과연 어느정도의 메모리 공간을 할당하는가?

VS2005에 포함된 STL 라이브러리의 경우 기본생성자가 할당해주는 공간은 0 바이트이다.
이말의 뜻은 매번 push_back() 호출시마다 메모리가 할당되는것인가?

특이하게도 위 테스트에서 6개의 원소를 삽입하는동안 매번 메모리가 할당되었다.
그러나 7개째를 삽입하게 되면 메모리 할당전략이 본색을 드러낸다. - 필요할 때마다 50% 씩 추가할당하는 모습을 지켜 볼수있다.


결국 대량의 데이타 삽입이 필요할 경우에는 _vector.reserve()를 꼭 사용해 불필요한 메모리 할당 오버헤드를 줄어야 한다는 것이다.


.NET , Java와 같이 동적배열을 지원하는 프레임웍의 경우도 별반다르지 않다.

그 이유는 Java의 Vector를 예로든 왼쪽의 도표에 나온 성능처럼, Vector 에 후위 삽입하는 경우 상수(O(1))에 근접한 성능을 보여주지만, 전위삽입의 경우 엄청난 성능하락(O(N*N))을 보여주고 있기 때문이다.

결국 표현하고자 하는 데이타가 어떤 컨테이너(벡터, 덱, 리스트,...)에 적합한지 충분히 고려해서 결정하는것이 필요하다는 것이다.





또다른 예를 들어보자.

집앞 운동장에 사는 개미들의 숫자 통계를 분석하라는 사장님의 지시(?)가 있었다. 백만*백만개의 좌표로 표현하도록 하라는 당부(!)와 함께 말이다.

몇일 생각해 보니 직교좌표계를 사용하면 알맞을것 같다. 해당 좌표계를 표시하는데 어떤 자료구조를 사용하겠는가? 간단히 생각해 보면 BYTE[백만][백만] 의 2차원배열로 처리하면 손쉬울것 같다.

백만은 약 2^20(1M) 이므로 2^40 = 약 1TBytes 메모리가 필요할 것이다.

이건 아니다. 뭔가 다른 방법이 필요해 보인다.
운동장을 아무리 살펴보아도 개미는 별로 없다. 결국 필요없는 공간이 많다는 것이다. 이런 공간을 표시하기 위해 비싼 메모리를 구매해 달라는 청구서를 쓸수도 없는 노릇이다.

결국 2차원의 배열구조가 아닌 다른방법이 필요한것이다. 결국 성능을 위해서 뿐만 아니라 밥줄을 위해서도 배열이 아닌 다른 자료구조가 필요한 셈이다.

당신이라면 어떤 선택을 하겠는가?

2006년 11월 10일

개발자의 필수품 - 폰트

소위 타이포그라피(typography)로 불리우며 시각적인 충족을 주는 다양한 인쇄물들을 접하곤 한다. 우리네 개발자들도 하루종일 모니터를 통해 각종 폰트들을 소비하는 소비자들이니 당연히 개인들마다 좋아하는 폰트가 있을것이다.

특히 개발을 해야하는 우리네 사람들은 고정폭을 가진 폰트를 매우 선호한다.
지금껏 고정폭을 사용하지 않는 개발자를 본적이 있다면 그 사람을 더이상 개발자라 부르지 말자. 폴그래엄 혹은 화가 아저씨라고 불르자...

모노스페이스 폰트가 뭔가요?

From Xerox:
Monospace fonts (Such as Courier or LetterGothic), or "fixed pitch" fonts, contain characters that all have the same character width, producing text that can be used to create forms, tabular material or documents that require exact text line lengths. An example of a fixed pitch font is Courier 12 pitch, which is a 10 point font that will print at exactly 12 characters per inch.


즉 고정폭이 주는 높은 가독성 때문일것이다. 주인장은 트루타입 폰트(벡터)보다는 비트맵 폰트(픽셀노가다^^)를 애용한다. 여기서 몇가지 폰트들을 소개하도록 하겠다. 지극히 개인적인 취향이니 http://www.lowing.org/fonts/ 같은 곳에서 소개하는 다른 폰트들을 얻을수있을 것이다.

ProggySmall
좌우폭의 비율이 참 아름답다. 깨끗한 여백의 미~


EnvyCodeA
약간 작지만 나름대로 가독성이 뛰어나다



CgMono
이녀석도 작지만 가독성은 알아준다



8x13ISO8859-1
Putty 로 알려진 통신프로그램에서 사용하 폰트로 꽤 이상적인 형태이다



ProFontWindows
올록볼록 화장지를 보는듯한 요상한 느낌을 주는 폰트이지만 아름답다

2006년 11월 8일

좀비들의 다구리 공격

왠 좀비?
1950년대의 괴수영화제목같은 소리지만, 불행하게도 최근들어 기업,정부,교육기관 그리고 개인 컴퓨터시스템들을 목표로 하는 대량의 네트웍 공격에 대해 익히 알것이다.

순식간에 여러분도 해커군대의 좀비 일병이 되지 말란 법이 없다.
최근 잘알려진 "좀비공격"을 전문용어로는 "분산서비스거부(DDoS:distributed denial-of-service)" 공격이라고 해서 무시무시한 것 같은데 알고보면 간단하다. 쉽게 예를 들면, 하루에 수백통씩 보내지는 스팸매일 때문에 편지함은 가득차 버려, 정작 중요한 메일을 받지 못하는 것과 같다. 게다가 발신지 주소도 다 다르니 차단하기도 난감하다. 이쯤되면 머리속에서는 태어나 배운 욕들이 일렬로서서 누가먼저 입밖으로 튀어나올지 다투기 일수다. (왠지 나의 모습같다 -_-; 그래서 구글 메일신을 영접했지만~~)

이쯤되면 분산(distributed)의 의미를 알 수 있을 것이다.
도대체 누가! 어디서! 어떡해! 알았길래 .... 아흑

영화에서 보여주는 좀비들의 공통점은 아무생각없이 살아있는 인간에게 무조건(?) 달려든다. 이와 마찬가지로 여기서 말하는 좀비란, 악의적인 해커가 몰래 숨겨놓은 프로그램에 감염된 호스트를 말한다. 평상시엔 멀쩡하다가도 어떤 신호를 받게되면 피가 신선한(?) 타켓을 향해 마구마구 달려들게 된다.

이런 분산공격이 통하는 이유로는 "엄청난 규모(scale)"과 "대역폭(bandwidth)"를 꼽을 수 있다.

많은 유저들이 사용하는 ADSL 회선의 경우 비대칭이지만, 상향(upstream)속도가 600Kb/s 정도 된다. 만약 500마리의 좀비들에게 명령을 내려 동작시키면 무려 500* 600kb/s= 300Mb/s 에 이르는 공격무기를 창조(?)할 수 있다. 이 수치는 전용선으로 잘알려진 1.544Mbps의 T1 라인 190여개, 혹은 45Mbps 의 T3라인 7개에 해당하는 막대한 트래픽이다.

만약 목표가 T1라인 하나(열개라도 마찬가지겠지만)만 사용하는 영세(?)한 곳이라면, 단한방의 공격으로 잿더미가 된다. 물론 모든 좀비들이 동일한 속도도 아니고 패킷크기도 서로 다르겠지만, 협동(?)의 힘은 무시할 수 없다. - 역시 백지장도 맞들면 낫다?


좀비가된 당신은 몰래 감염사실이 알려지게되며, 총사령관은(?) 이 좀비들의 주소를 향해 명령을 내리면 그만이다.

스티븐스교수가 한 학회에서 이 분산공격에 대한 위험성을 언급했다고 한다. 그런데 아이러니 하게 며칠후에 야후에 대해 바로 이 분산공격이 이루어졌다고 한다. - 출처 : 카더라통신^^

물론 TCP/IP 프로토콜이 보안에 대한 고려가 없이 설계된 탓도있지만, 지금까지도 분산공격에 대해서 뚜렷한 대응책이나 해결책은 존재하지 않기 때문에 더욱 문제가 된다. 더구나 툴들이 넘쳐나고 있으니...

개개인의 보안수준을 높이는 원초적인 수단이 최선이겠지만, 대체적으로 일반유저들의 보안의식은 그렇게 높지 않다. 게다가 일을 당하지 않고는 쉽게 와닿지 않기 때문에 문제는 더욱 심각하다. 그나마 최근 발표된 M$의 SP2는 많은 유저들의 좀비화(?)를 어느정도 예방하게 될 것같아 다행스럽긴하다.(?)

웹에 대한 추억들

미국의 베트남 참전으로 전세계가 어수선했던 1960년대 말, 미 국방성(U.S. Department of Defense)은 멀리 떨어진 컴퓨터들을 서로 연결해 네트워크를 꾸미는 실험적이며 모험적인 프로젝트를 지원하기위해 자금을 투입했다.

이 프로젝트를 주도한 ARPA(Advanced Research Projects Agency)의 이름을 따서, 이 실험적인 네트워크를 ARPANET이라 부르기 시작했다. 꽤 오랫동안 이 프로젝트는 단지 실험적인 수준에 머물러 있었으나 우연한 기회로 인해 지금의 인터넷으로 급속한 발전을 하게 된다.

미국 캘리포니아주(州)에 있는 캘리포니아 주립대학교(UC Berkeley)는 AT&T의 UNIX 라이센스 제공과 미 국방성의 지원으로 BSD UNIX를 개발했는데, 큰 특징으로는 통신을 위해 TCP/IP란 프로토콜 탑재하고 대학생들에게 무료로 나누어 주었다는 점이다.

이같은 유인책(?)이 통했던지 기존 ARPANET에 TCP/IP를 사용하는 BSD UNIX시스템들이 점차 늘어나고 결국 ARPANET을 장악(?)하게 된다. 때문에 TCP/IP가 ARPANET의 기본 네트웍 프로토콜로 채택되게 되었다.

Dennis Ritchie 와 Ken Thompson하면 떠오르는 단어는 해커, C언어 그리고 UNIX의 창시자이다. 이 둘은 Bell 연구소에 근무하며 MULTICS(모든 것을 다 잘한다는 뜻으로)란 운영체제를 만들고 게임을 제작해 즐겼다고 한다. - Space Wars 란 비디오게임 게임을 위해 강력한 운영체제가 필요함을 느끼고 있었고, Ritchie는 C언어를 개발해 이를 적용해 보고 싶었던 터라, 이 결과로 UNIX(하나만 잘하는)가 탄생하게 된다.
결국, 쉽게 이해할 수 있고 하드웨어를 강력하게 제어할 수 있는 효과적인 언어로 C언어가 자리잡게 되었다.
향후 UNIX는 다양한 아류(?)작들이 파생되었으며, IBM의 AIX, SUN의 Solaris, HP의 HP-UX등 유명한 상용판들과 PC를 위한 Linux가 그것이다. 자손들의 통합된 표준안을 위해 생긴 것이 바로 POSIX.

1970년대만 해도 ARPANET은 수백대의 호스트들이 연결된 작은 규모의 네트워크였고, 해당 호스트로 연결하기 위해서는 IP주소만 알면 간단(?)했다.
더구나 숫자암기에 약한 사람들을 위해 호스트이름으로 접속할 때 변환에 사용하라고, "호스트이름, IP주소" 가 담긴 텍스트파일을 만드는 노가다도 서슴치 않았다.
관리자야 짜증나겠지만 익명 FTP서버를 열어놓고 이 텍스트파일(hosts)을 다운받아가도록 했다.


서로 통신을 가능케 하려면 IP주소뿐 아니라 호스트이름을 등록받아서 텍스트파일에 추가해야되고, 이미 등록된 이름인지 매번 검사해야하고, 재신청하라고 알려줘야 하는 단순하지만 중요한 일을 누군가는 했을텐데... 아르바이트를 고용했을지도... ^^

결국 노가다에 가까운 "호스트이름, IP주소" 관리방법을 바꾸어야 했고, 1984년 이에 대한 해법을 RFC 882, 883을 통해 발표하게 된다. 이름하여 DNS(Domain Name Service).

Paul Mockapetris에 의해 설계된 DNS의 핵심은 중앙에서 관리하는 이 정보를 잘게 나누어 분산시키는 것이다. 이를 위해 계층적인 트리(tree)구조와 클라이언트,서버 구조등의 방법을 도입하게 된다.

인터넷에서 떠도는 패킷들중 DNS가 갖는 비중은 매우낮다.
결국 4byte IP주소를 받아오는 것이니 말이다. 하지만 DNS 서비스에 문제가 생길 경우 난리가 난다. 우리나라도 몇해전 인터넷 대란이라고 TV에서 시끄러웠다. 혹자는 "IP주소"를 달달 외워서 아무문제가 없었을 지도...

이름풀이 과정중 가장위에 위치한 루트 DNS서버들은 A루트서버부터 M루트서버까지 전세계적으로 13개가 퍼져있으나, 아쉽게 국내에는 없다. (일본에는 있지만...) 소식에 의하면 루트서버의 복제(mirror)를 들여온다고 하던데...

30바이트 전송하는데 1분이나 걸릴까?

TCP를 통해 작은 데이타 패킷을 보내야 하는 어플리케이션을 제작하려고 할 때, 발생할수있는 치명적인(?) 성능저하에 대해 이야기 해보자.

M$의 윈속을 사용할경우를 살펴보도록 하자.

윈속에서는 세그먼트를 수신하게 되면, 곧바로 응답을 주지 않고 일정시간(200ms이며 레지스트리값으로 변경가능)동안 보낼것이 없는지 대기하다가 ACK 세그먼트로 응답한다. 왜 그럴까?

바로 응답(delayed ACK)을 지연시켜 보내주기 위해서다.
지연된 응답이라는 것은 잘 받았다는 세그먼트를 곧바로 보내지 말고(TCP 헤더 플래그의 1비트를 SET한 세그먼트를 보내것), 좀(200ms 동안) 기다렸다가 내가 보낼 데이타가 생겼을때 ACK 임을 SET하여 같이 보내(이를 piggybacked ACK라 한다), 결국 세그먼트 두개가 아닌 한개로 응답과 전송을 모두 하려는 것이다.
효율적이지 않은가?

그러나 문제점은 바로 해당 어플리케이션이 작은 데이타를 보내고 응답을 받은후(ACK 세그먼트)에 다시 작은 데이타를 보내는 일을 수행하도록 디자인 되었다는데 있다.
응답이 200ms 정도 걸린다는 말은, 보내는 송신측에서 다음 데이타를 보내는데 최소 200ms 동안 대기되어야 함을 의미한다.
결국 1초동안 5번정도밖에 전송을 하지 못함을 의미한다.

자 계산해 보자. 해당 어플리케이션이 100바이트를 보내도록 디자인 되었다면, 아무리 빨라봐야 1초동안 500바이트를 전송한다는 것이다.

500bytes/sec 라니... 당장 시말서를 써야할지도 모른다.

자 정리를 해보도록 하자.

윈속의 경우 세그먼트를 수신하게 되면 곧바로 200ms 짜리 타이머가 가동된다.
상대방으로 전송할 세그먼트가 없으면 200ms 타이머가 만료된후 ACK세그먼트로 응답된다.
200ms 내에 상대방으로 전송할 세그먼트가 있으면 곧바로 ACK를 SET하고 응답 세그먼트를 보낸다. (piggy backed)

결국 이 delayed ACK란 것이 네트웍크에서 자잘한 세그먼트들이 넘치지 못하게 하는 일종의 SWS(Silly Window Syndrome) 현상을 예방하기위한 한 종류임을 알수 있다. 우리는 이미 Nagle 알고리즘역시 이와 비슷한 역할을 한다는 사실도 알고있다.

차이점은 Nagle 알고리즘은 송신측에서 적용되고, delayed ACK는 수신측에서 적용된다는 것이다.

게다가 소켓을 생성할때 Nagle 옵션(TCP_NODELAY)이 On 되었다면 상황은 더욱 악화된다. 왜냐하면, Nagle 알고리즘은 전송을 지연하는것이 목적이기 때문이다.

물론 송신할 데이터가 MTU 를 초과할경우, 잘받았다는 응답(ACK)이 없어도 해당 세그먼트를 즉시 송신하기는 하지만, 우리가 보내는 데이타는 MTU 크기를 훨씬 밑도는 100바이트가 아니던가...!!

결국 송신 데이타도 지연되고 수신데이타도 지연되는 이 같은 상황에서 우리가 디자인한 어플리케이션이 원하는 성능을 내줄거라는 희망은 사라지는 셈이다.

자 그럼 해당 어플리케이션이 왜 이런식으로 동작하는지 분석해 보도록 하자.

1. 네트웍 혼잡보다 처리 시간이 더 중요한 (time critical) 어플리케이션이라면 송신측에서 지연을 유발하는 Nagle 알고리즘의 사용을 자제해야한다.

2. 만약 1번 처럼 Nagle 알고리즘을 껏다면, 송신측은 MTU보다 작은 크기일 지라도 즉시 첫번째 세그먼트를 보내고, 두번째 세그먼트도 곧바로 전송하게 될것이다.

3. 수신측은 첫번째 세그먼트를 받은후 200ms 타이머를 가동시켜 delayed ACK를 시도하겠지만, 두번째 세그먼트가 곧바로 오기때문에 해당 타이머는 리셋(reset)되고 곧바로 응답 ACK 세그먼트를 보내주게 된다.

참고) MSDN: Design issues - Sending small data segments over TCP with Winsock
링크) http://support.microsoft.com/default.aspx?scid=kb;en-us;214397

소수 소수

http://www.mersenne.org 홈페이지를 방문해 보면 특이한 공지사항(?)이 하나 있다.

On December 15, 2005, Dr. Curtis Cooper and Dr. Steven Boone, professors at Central Missouri State University, discovered the 43rd Mersenne Prime, 2^30,402,457-1. The CMSU team is the most prolific contributor to the GIMPS project. The discovery is the largest known prime number.

대충 해석해 보면 작년 가을쯤에 미주리 주립대학교의 두 교수가 43번째 메르센 소수를 발견했다는 소식이다.

2^30,402,457-1 란 숫자를 10진수로 나열하면 무려 9,152,052 개나 된다고 한다. 이 수가 진짜 메르센 소수인지 검증하기 위해 5일동안 16 개의 Itanium2 1.5 GHz CPUs 를 사용했다고 하니 참말로 열정이 대단하다.

흠... 메르센 소수? 도대체 뭘까? 궁금증을 참지 못하고 구글신에게 물어보았다.

링크 참조 : http://ko.wikipedia.org/wiki/%EB%A9%94%EB%A5%B4%EC%84%BC_%EC%86%8C%EC%88%98

요약해 보면, n 이 소수일때 2^n -1 도 소수이면 해당 n은 메르센 소수가 된다는 것이다. 이걸 뒤집에 생각하면 2^n -1 이 소수가 아닐때도 있다는 것이다. 해당 값이 소수인지 어떡해 검사할까?

소수는 1과 자기 자신 이외에 나누어 지지 않는 수라는건 익히 알고있다. 간단히 생각나는데로 구현해 보면...

bool isPrimaryNumber(unsigned int n)
{
for (unsigned int i =2; i< n; ++i)
{
if (0 == (n%i) )
return false;
}
return true;
}

가 되지 않을까? 아니면 지금 까지 알려진 소수들을 인터넷 검색을 해서 구해온 후 몇번의 루프를 돌면 되지 않을까? unsigned int 면 통상 2^32 -1 이 최대값인데 그보다 큰 녀석들은?
어 그러고 보니 2^x -1 은 다들 소수인거 같네(메르센 소수는 아니지만) ? 정말 ?...

소수 하면 생각나는 영화가 있다.
바로 조디 포스터 주연의 Contact ... (이후 스포조심^^)
베가 행성에서 보내온 정체불명의 전파속에 숨은 과학의 언어 '소수'

1963년 미국 일리노이 대학에서는 23번째 메르센 소수를 발견하였는데 이를 기념하기 위하여 ‘211213-1은 소수이다’라고 새긴 우편 스탬프를 찍어 냈다고 한다. -
여러분들도 GIMPS 프로젝트에 일원이 되어 자신만의 역사적인 우표를 만드는 날이 오길 ^^..

WIN32 동기화 객체 자동으로 안되겠니?

Win32 API를 통해 멀티 쓰레드와 관련된 어플리케이션을 개발하다보면 동기화 때문에 가끔 작업하던 컴퓨터를 리셋해야 하는 경험을 하곤 한다.
이런 삽질을 피하려면 항상 임계영역(Critical Section)에 진입해 상호배제적인 작업을 마친후에는 사용중이던 동기화객체를 반드시 해제하여 데드락에 빠지지 말아야 한다.

허나 프로그램은 불완전한 인간이 하는일 어찌 실수치 않으리오 …
동기화 객체 해제하는 코드를 빠뜨리는 날에는 지금껏 알던 수많은 욕들이 입속에서 메아리치게 된다. 예전 본인의 모습이다...~_~;;

CRITICAL_SECTION 동기화 객체의 경우 LeaveCriticalSection()을 호출하지않고 뻘짓거리를 하고 있을 때 이 임계영역을 진입하려는 다른 녀석은 무한정 대기하게 된다 아마 한달정도면 데드락이 풀릴지도 모른다…(믿거나 말거나)

해서 이러한 동기화 객체들을 모아모아 좀더 쉽게 설계하자는 것이 이 글의 목표이다. 일단 금번 버전에서는 윈도우의 가장 흔힌 동기화 객체인 CRITICAL_SECTION, EVENT, MUTEX, SEMAPHORE 을 대상으로 하려한다.

(참고) 자세히 보면 대부분의 동기화 객체들은 첫번째 인자로 보안과 관련된 항목을 집어넣게 되어있다. 이 보안항목을 집어넣는 녀석들은 99% 커널 오브젝트다.


1. ISynchronize - 인터페이스의 설계
모든 동기화 객체는 ISynchronize 란 인터페이스를 상속받아 구현한다. 이 인터페이스를 통해 자동적으로 임계구역에 진입하고 탈출하도록 디자인할 계획이다.

#ifndef _SynchronizeH
#define _SynchronizeH

#include
namespace Comeric
{
class __declspec( dllexport ) ISynchronize
{
public:
virtual DWORD enter(DWORD dwMilliSeconds=INFINITE)=0;
virtual void leave()=0;
};
}
#endif


2. CRITICAL_SECTION
#ifndef _CriticalSectionH
#define _CriticalSectionH
#include
#include "Synchronize.h"

namespace Comeric
{
class __declspec( dllexport ) CriticalSection
: public ISynchronize
{
public:
virtual DWORD enter(DWORD dwMilliSeconds=INFINITE)
{
EnterCriticalSection(&_cs);
return WAIT_OBJECT_0;
}
virtual void leave()
{
LeaveCriticalSection(&_cs);
}
protected:
CRITICAL_SECTION _cs;
public:
CriticalSection()
{
InitializeCriticalSection(&_cs);
}
virtual ~CriticalSection()
{
DeleteCriticalSection(&_cs);
}
};
}
#endif


3. EVENT
#ifndef _EventH
#define _EventH

#include "Synchronize.h"

namespace Comeric
{
class __declspec( dllexport ) Event
: public ISynchronize
{
public:
virtual DWORD enter(DWORD dwMilliSeconds=INFINITE)
{
return WaitForSingleObject(_hEvent, dwMilliSeconds);
}
virtual void leave()
{
SetEvent(_hEvent);
}

protected:
HANDLE _hEvent;
public:
Event(std::wstring name = NULL)
{
if (NULL == OpenEvent(EVENT_ALL_ACCESS, FALSE, name.c_str()) )
_hEvent = CreateMutex(NULL, FALSE, /*auto reset*/ TRUE, name.c_str())

assert(_hEvent);
}
virtual ~Event()
{
CloseHandle(_hEvent);
}
};
}
#endif


4. MUTEX

#ifndef _MutexH
#define _MutexH
#include
#include
#include
#include "Synchronize.h"

namespace Comeric
{
class __declspec( dllexport ) Mutex
: public ISynchronize
{
public:
virtual DWORD enter(DWORD dwMilliSeconds=INFINITE)
{
return WaitForSingleObject(_hMutex, dwMilliSeconds);
}
virtual void leave()
{
BOOL ret=ReleaseMutex(_hMutex);
assert(ret != NULL);
}
protected:
HANDLE _hMutex;
public:
Mutex(std::wstring name = NULL)
{
if (NULL == OpenMutex(MUTEX_ALL_ACCESS, FALSE, name.c_str()) )
_hMutex = CreateMutex(NULL, TRUE, name.c_str());
assert(_hMutex);
}
virtual ~Mutex()
{
CloseHandle(_hMutex);
}
};
}
#endif


5. SEMAPHORE

#ifndef _SemaphoreH
#define _SemaphoreH
#include "Synchronize.h"

namespace Comeric
{
class __declspec( dllexport ) Semaphore
: public ISynchronize
{
public:
virtual DWORD enter(DWORD dwMilliSeconds=INFINITE)
{
return WaitForSingleObject(_hMutex, dwMilliSeconds);
}

virtual void leave()
{
BOOL ret= ReleaseSemaphore(_hSemaphore, 1 , NULL);
assert(ret != NULL);
}

protected:
HANDLE _hSemaphore;

public:
Semaphore(std::wstring name = NULL, LONG lInitialCount=1, LONG lMaximumCount=1) //default binary semaphore
{
if (NULL == OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, name.c_str()) )
_hSemaphore = CreateSemaphore(NULL, TRUE, name.c_str());

assert(_hSemaphore);
}
virtual ~Semaphore()
{
CloseHandle(_hSemaphore);
}
};
}
#endif


자 그럼 우리의 목표인 자동적인 임계구역 진입, 탈출을 적용해 보자. 이미 짐작했겠지만 모든 동기화 객체가 동일한 인터페이스를 지니고 있기 때문에 매우 쉽게 구현가능하다. 이런 녀석을 AutoSynchronize 라고 이름짓자.

6. AutoSynchronize
이 녀석을 만들기 위해 지금까지 삽질을 했단 말이다...~


#ifndef _AutoSynchronizeH
#define _AutoSynchronizeH

#include
#include "Synchronize.h"
namespace Comeric
{
class __declspec( dllexport ) AutoSynchronize
{
public:
AutoSynchronize(ISynchronize& iSynchronize)
: _pSynchronize(&iSynchronize)
{
_pSynchronize->enter();
}

virtual ~AutoSynchronize()
{
_pSynchronize->leave();
}
protected:
ISynchronize* _pSynchronize;
};
}
#endif

짐작했겠지만 이제 동기화 객체를 사용하는 것은 너무나 쉬워졌다.
AutoSynchronize 녀석의 생성,소멸자에서 자동으로 임계구역에 진입, 탈출을 해주니 말이다.
단순히 다음과 같이 시도하면 될것이다.

활용예

Comeric::CriticalSection cs;

쓰레드1,쓰레드2의 임계영역은 서로 동기화된다.
쓰레드 1
{
Comeric::AutoSynchronize as(cs); //as 는 스택변수이기 때문에 해당 구역이 존재하는한 동기화가 유지된다.
// 상호배타적인 작업

}

쓰레드 2
{
Comeric::AutoSynchronize as(cs); //as 는 스택변수이기 때문에 해당 구역이 존재하는한 동기화가 유지된다.
// 상호배타적인 작업들…
}

위에서 구현한 동기화 객체들에 대한 비교는 다음표를 참고하기 바란다.

Synchronization Objects Summary
Name
Relative speed Cross process Resource counting
Critical Section
Fast No No (Exclusive Access
Mutex
Slow Yes No (Exclusive Access)
Semaphore
Slow Yes Yes
Event
Slow Yes Yes*
Metered Section
Fast Yes Yes

처음 보는 Metered Section 은 CRITICAL_SECTION 처럼 빠르면서 프로세스간 동기화에 사용할수있는 방법으로 http://msdn2.microsoft.com/en-us/library/ms810428.aspx 에서 해당 구현을 참고할수있다.
이마저도 귀찮아할줄 알고 직접 Metered Section을 ISynchronize 인터페이스에 맞게
VS2005에서 확장 DLL 로 구현해 놓았다. - 다운로드는 이곳

- 친절한 주인장백

시리우스 라이브러리 홈페이지 오픈

현재 시리우스(Sirius) 라이브러리라는 제품을 개발하고 이를 소개하는 홈페이지를 오픈 하였습니다. 관심있는 분들의 많은 방문 요청드립니다. 앞으로 업데이트 소식및 변경사항은 스파이럴랩 홈페이지를 통해 진행할 예정입니다. 스파이럴랩 홈페이지 :  h...