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

댓글 없음:

댓글 쓰기

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

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