거짓 공유 (false sharing)

캐싱의 기본은 지역성에 근거하는데요, 이는 프로그래밍단의 최적화에서도 유명한 80-20법칙과도 일맥상통하는 이야기죠.

지역성(locality)은 아래 추정에 근거합니다.

  1. 지금 읽힌 데이터는 이후에도 자주 사용될 가능성이 높다.
  2. 지금 읽힌 데이터와 인접한 데이터는 이어서 사용될 가능성이 높다.

이는 코드 실행시 스택 처리를 통해 얻게 되는 장점과 유사합니다.

단일 코어가 아닌 멀티 코어 CPU는 데이터를 읽어올때, 캐시 라인 (cache line)이란 단위로 읽어옵니다.  캐시 라인이라 함은 지역성에 근거해 인접한 데이터를 미리 읽어옴으로써 속도향상을 노리는 것이지요.

하지만 이는 장점이자 독이 되기도 합니다.

멀티코어에서는 A스레드와 B스레드에서 인접 메모리를 접근할때, 캐시에 있던 내용을 메모리에 반영하려 시도합니다. 인접 메모리를 읽고 있는 상태이기에 병행 수행시 데이터의 유효성을 조금이라도 높이기 위해 메모리에 반영하는 과정에서 속도 저하가 발생하는 것이죠.

실제로 인접메모리일뿐 동시 접근이 일어나지 않는 코드라고 할지언정, 해당 코드가 어떻게 작성되었는지는 중요치 않습니다.  캐시 라인은 코드의 작성 여부까지 판단하고 동작하지 않기 때문에 (그렇게 할 수 가 없기에), 인접 메모리 접근만으로도 성능 손해를 보면서라도 데이터의 유효성을 높이고자 하는 판단을 내릴 수 밖에 없습니다.

convoying (무분별한 lock의 사용으로 멀티 스레드를 활용하지 못하고, 한개 스레드 동작시 다른 스레드들은 그 스레드가 unlock 할때 까지 대기 해야만 하는 상황) 보다야 낫겠지만, 멀티 코어가 일반화 되면서 이를 얼마나 잘 활용하는가가 화두가 되고 있는 이 시점에 메모리 거짓 공유로 인한 속도 저하는 반드시 염두에 두어야 하는 이슈입니다.

멀티코어 프로그래밍에서는, 자주 읽히는 데이터가 인접해 있다면, cache line 크기만큼의 간격을 두도록 하는 것이 좋습니다.  패딩(padding)을 이용해서 메모리를 손해보더라도 속도에서 이득을 보라는 얘기죠.

옛말에 “메모리 공간을 팔아 속도를 산다”는 말 처럼, 메모리와 속도는 반비례 그래프와 같다는 생각이 다시 한번 드네요.

참고 자료

데이터베이스 이용시 주의 사항

서버 개발시에 DBMS를 사용하지 않는 것은 불가능에 가깝습니다. 

특히나 트랜잭션을 C/C++ 레벨에서 구현하기란 보통 어려운게 아닙니다. DBMS에서는 시스템 단에서 지원해주기 때문에 손쉽게 트랜잭션을 사용 할 수 있죠. 데이터를 저장하고 불러 오는 것 역시 파일 시스템보다 DBMS가 훨씬 더 효율이 좋죠.

물론 그것도 잘 만들고, 잘 사용하고, 잘 관리했을때 이야기지만 말이죠.

다음은 게임 서버에서 DBMS를 이용할 때의 유의 사항입니다.

  1. 반드시 측정하라.
    • 쿼리 프로파일러 등을 통해서 DB 부하를 측정하라. 
    • 원하는 목표치를 수립하고, 그 목표치에 달성 할 수 있게끔 노력하라.
    • 목표치가 너무 높거나 낮을 수도 있지만, 목표치가 없이 무작정 빠르게보다 동기부여도 되고, 성취감도 생기기 때문에 반드시 목표치를 두자.
  2. 미리 테스트하라.
    • 반드시 테스트 하라. 그리고 미리 테스트하라.
    • 개발 과정에서 데이터를 임의로 생성하고, 측정하라.
    • 테스트할 데이터가, 실제 유저들이 쌓을 데이터와 비슷하다면 금상 첨화다.
    • 하지만 그렇지 않다하더라도 선행 테스트는 반드시 필요하다.
  3. 쿼리 호출 횟수를 줄이는 것도 좋은 튜닝 방법이다.
    • 물론 쿼리 자체가 느리다면, 쿼리 호출 횟수가 작아도 문제가 생길 수 있다.
    • 하지만, 쿼리가 아무리 빠르더라도 쿼리 호출 횟수 자체가 많다면 그것이 부하가 될 수도 있다.
  4. 같은 동작이 겹치는 상황을 줄여라.
    • 같은 테이블에 동시에 접근하는 것은, 쿼리 수행시간 증가의 요인중 하나다.
    • 단일 큐처리, 테이블 분산 등을 통해서 최대한 병렬 작업의 효율을 높이도록 하자.
  5. 커넥션이 많다고 좋은게 아니다.
    • 4번 항목과 연관성이 있는 내용으로, 커넥션이 많다고 능사가 아니다.
    • 같은 테이블에 접근하는 SP가 여러개 커넥션에서 몰리면 블러킹으로 효율이 저하될 가능성도 높아진다.
    • 진정한 병렬수행이 될 수 있도록 설계하고, 사용하라.
    • 잘 사용하는 것은 잘 만든 것 만큼이나 중요한 것이다.
  6. 적은량의 데이터만으로 테스트 하지 말아라.
    • 2번 항목에서 언급했듯이, 테스트할 데이터가 실제 유저들이 쌓을 데이터와 비슷하면 비슷할수록 실제 서비스와 유사한 측정과 개선이 가능해진다.
    • 실제 유저들이 쌓을 데이터가 아닌, 개발자와 QA 데이터 몇개만으로 테스트 하지 말아라. 그렇게하면 논리적 구현의 테스트는 될 지언정 효율에 대한 테스트는 불가능하다.
  7. 트랜잭션을 적극 활용하라.
    • 4,5번 항목과 조금 엇갈리는 내용이지만, 효율보다 예외처리가 더 중요하다.
    • 트랜잭션을 잡아야 하는 동작이 있다면 반드시 트랜잭션을 잡아라. 효율을 위해 번거로운 예외처리를 서버에서 처리하게 된다면, 그로 인해 손해 보는 것 (예외 상황을 처리하는 개발 과정, 예외 상황 처리를 테스트 하는 과정, 예외 상황을 정상 처리로 수행 가능하게끔 만들기 위한 노력, 만약에 예외 처리에 실패했을 경우에 겪는 피해)들이 너무 크다.
    • 트랜잭션을 잡게 되면 그만큼 효율은 떨어지지만, 논리적 오류로써 겪게 되는 문제들에서 해소 될 수 있다.

(서평) 스티브 잡스의 수퍼 업무력 - 애플의 그리고 잡스의 성공 비결

요새 내 주변에는 모두가 입을 모아 한 기기를 찬양하는 상황이 펼쳐지고 있다. 바로 아이폰이다!  아이팟 터치도 혁신적인 기기였지만, 아이폰은 그 이상이다. 3G망을 이용한 무선 네트웍 기능은 언제 어디서나 원하는 정보를 이라는 컨셉에 매우 잘 부합한다.

과연 이 것을 노린 것일까, 요행일까?

아마도 ‘그’라면 노렸을 지도 모른단 생각이 든다.

바로 ‘스티븐 잡스’다.

매킨토시를 만들어 낸 장본인으로, 새로운 것을 시도하고 실현 하는 것에 탁월한 재주가 있는 그의 ‘작품’들은 나오는 제품마다 큰 관심사가 되고 있다.

발매되자마자 매진 행렬을 보여주고 있는 아이패드가 그 반증이다.

개인적으로 스티븐 잡스와 관련된 서적은 ‘잡스처럼 일한다는 것’ (http://elky.tistory.com/203) 을 읽었었다.

잡스가 얼만큼 뛰어난 인물이고 뛰어난 리더였는지를 조명한 책이었는데, 이번책은 그의 사업적인 측면에 좀 더 집중한 느낌이 강했다.

그의 사업적인 판단이 어긋났을 때도 꽤나 많기에, 결과론 적인 이야기들이 된 것이지만, 원래 세상이란 그런 것 아닌가?

내 개인적인 애플의 제품들에 대한 느낌은, 혁신보다도 ‘섬세함’이다.

아이폰 이전에도 무선 인터넷은 있었고, 모바일 어플리케이션 들도 있었다. 어플리케이션의 네트웍 기능도, 모바일 운영체제도, SDK도 있었다. 앱스토어? 이 것도 사실 새로운 것이라 보기엔 무리지 않을까? 아이튠즈도 마찬가지고.

아이폰/아이팟 터치의 성공 비결은 기존에 존재하던 것 들을 사용자 관점에서 바라보고, 이를 쉽게 접할 수 있고 활용 할 수 있게 도와주는 것에 집중했다. 그렇다보니 기존에 어렵고 복잡한 과정으로 인해 알아도 사용하지 못하던 기능들이, 또 아이디어들이 실현되고 있기에 아이폰과 아이팟 터치가 사용자들에게 사랑받은 것이다.

‘사용자 중심’적인 잡스의 발상이야말로 분야를 막론하고 필요한 것이다. 사용자를 중심에 놓으면 어려워선 안되고 호기심을 자극해야 한다. 쉽게 접근하려면 섬세해야 한다. 섬세한 배려만이 사용자에게 편리함을 가져다 줄 수 있다.

우리가 놓치고 있는 2%. 우리는 너무 쉽게 보고 있는건 아닐까?

(서평) 누워서 읽는 퍼즐북 - 퍼즐의 쾌감

퍼즐이라 하면 많이들 어렵단 생각을 하곤한다.

나 역시 마찬가지였고, 임백준씨의 책이지만 퍼즐북이라는 제목을 보자마자 당황했으니 말 다했다.

임백준씨 책은 하나도 빼먹지 않은 나로써, 이번책도 놓칠 수 없다는 생각에 예약 구매했다.

평소에도 퍼즐에 관심이 많았던 임백준씨 답게 퍼즐북에도 재밌는 퍼즐이 많이 포함되어 있었다. ‘그리’ 어렵지 않은 퍼즐들을 부담스럽지 않게 재밌게 풀어내는 임백준씨의 글솜씨는 여전히 유효했다.

프로그래머지만 수학적인 재미를 크게 느끼지 못하는 나로썬, 퍼즐의 수학적 발상에도 좀 부족한 감이 있었는데, 그런 부분에서 적어도 ‘재미’있게 접근할 수 있는 계기가 된거 같아 아주 좋았다.

특히나 기억에 남는 퍼즐은 다리 건너기 문제가 기억에 남는다. 예전에 접했던 다리 건너기 문제보다 어려웠지만 그만큼 푸는 재미도 있었다.

7명의 죄수와 7가지 색의 모자의 경우는 앞에 나왔던 죄수 시리즈을 이해했으면 쉽게 풀 수 있는 문제였다.

100개의 수를 기억하는 방법은 발상의 전환이 유효했고.

다음책은 내년 이후에야 나오겠지만 앞으로의 책에서도 프로그래밍을 수학/퍼즐에 연관시켜 재밌는 글 많이 써주셨으면 좋겠다.

이번 책을 보니 아쉽게도 프로그래밍 실무 전선에서 조금 물러나셨다고 하는데, 그렇더라도 여러가지 측면에서의 프로그래밍적인 철학이나 발상은 꾸준히 국내 여러 프로그래머에게 전해주셨으면 하는 바램이다.

C++ 오류 발생시 원인 정리

예외가 발생하는 상황

  1. 0으로 나누기
    • 나누기를 직접 사용하지 말고, 매크로 함수로 만들어놓고 사용하는 것이 좋다. (0으로 나누기를 시도했을 경우 로그를 남기는 등의 동작) int nDivisor = 0; int nResult = rand() / nDivisor; //Divide zero.
  2. 잘못된 메모리 접근 (널포인터 접근 포함)
    • Access Violation 예외 발생
    • 주로 Access Violation이 발생하지만, 멀티 스레드라서 다른 스레드에서 같은 주소에 할당을 요청했고, 그 주소를 다시 한번 delete했을 경우는 증상이 엉뚱한 곳에서 나오기도 하니 반드시 SAFE_DELETE같은 매크로 함수 혹은 auto_ptr, shared_ptr 등의 스마트한 메모리 관리 객체를 사용하자.

바로 크래쉬 되는 상황

  1. 스택 오버플로우 (무한 재귀 또는, 스택 변수를 너무 크게 잡고 사용할 시에 발생)
    • 대책 : http://msdn.microsoft.com/ko-kr/library/aa289171(VS.71).aspx
    • 스택 오버 플로우 트랩 : http://support.microsoft.com/kb/315937
void StackOverflow(int depth)
{
    char blockdata[10000];
    printf("Overflow: %d\n", depth);
    StackOverflow(depth+1);
}
  1. 예외가 발생해 스택 되감기하는 도중 예외 발생.
    • 이런 경우가 흔치는 않은데, 안전하지 않은 코드 작성시에는 이렇게 되기도 한다. 딱히 대책은 없다. 너무 무책임한가 =_=;

멈춘 듯 보이는 상황

  1. 반복 수행되는 쓰레드에서, 익셉션에 계속 남는다. (원스레드식으로 돌릴 때)
    • 덤프 백만개 쌓이다가 하드 풀나기도 한다. 우울해지곤하지…=_=
    • 대책은 HardDisk Space 계산해서, 특정 수치 이상이면 서버 떨구는게 차라리 낫다. 
  2. 무한 루프
    • GPG3권에 있는 1.3의 C스타일 매크로의 새로운 가치를 찾아서에 나오는 while_limit 를 적극 활용하는 것이 어떨까 싶다.
    • 좋은 습관은 주로 위기 상황에서 효과를 발휘하는 법이다.
  3. 데드락
    • 실제로 멈춘상태. 주의 사항은, 데드락이 걸려있는 스레드랑 전혀 별도로 돌아가던 스레드는 여전히 계속 돌고 있다는 점이다. 이 얘기는 뭐냐하면, AliveCheck용 스레드를 따로 두었을 경우, HeartBeat가 제대로 전달되 데드락 걸린 상황을 탐지하기 어려울 수도 있다는 것이다.
  4. 멈춘 듯 보이는 현상을 해결하기 위해선, UserDump 등으로 FullDump 뜨는 것을 추천한다.
    • FullDump로 콜스택을 확인해보면, 어떤 스레드가 무한 루프에 빠졌거나, 데드락에 걸렸는지 등을 확인 할 수 있다.
    • 좀 더 확실히 하려면 Snapshot처럼 여러번 FullDump를 남겨 확인해보면 좀 더 확실해 질 것이다.

구분이 잘 안가는 상황 (인과 관계를 깨드리는 상황)

  1. 스택 조금만 덮어 씌웠을 때 * 대책 : http://msdn.microsoft.com/ko-kr/library/aa289171(VS.71).aspx

  2. 버퍼 오버플로우 (얼만큼 덮어 씌웠는지에 따라 다르다)
    • 버퍼 오버플로우 되는 상황이 나왔다는거 자체가 우울한 상황이다. 이 상황에서 원인을 찾길 바라는건 요행에 가깝다. 
    • 메모리 관련 함수는 되도록 자제하라. (memcpy, memset 등등…)
    • 네이티브 포인터도 자제하라.
  3. 자식 클래스로의 잘못된 다운 캐스트한 후, 자식 클래스의 함수 호출.
    • 해당 영역에 어떤 데이터가 있었는지에 따라 다르다. 자식 클래스의 함수에서 사용하는 데이터가, 자식 클래스 전용 멤버일 경우 잘못된 메모리 접근이 있을 것이고….혹은 메모리 관련 함수거나 하다면 더 이상한 증상을 보인다. 
    • 잘못된 다운 캐스트 자체가 정줄 놓은거지…진짜 확신한다해도 다운 캐스트 자체는 죄악. 허나 어쩔 수 없이 다운캐스트가 필요하다면  dynamic_cast를 사용하라. 근데 나는 이것도 좀 반대하는 입장.
    • 이런건 어떨까? http://www.gamedev.net/reference/programming/features/TypeSafeGenPtr/
  4. 잘못된 함수 포인터
    • 주의 해라. 뭐 별 수 있나…
  5. 삭제 중인 객체 혹은 삭제된 객체의 순수 가상 함수 호출
    • http://msdn.microsoft.com/en-us/library/t296ys27(VS.80).aspx
  6. CRT가 함수에 잘못된 인수를 전달 받았을 때에 불리는 함수
    • http://msdn.microsoft.com/en-us/library/a9yf33zb(VS.80).aspx