소프트웨어/C&C++

The Lost Art of Structure Packing

충무로술겜마 2021. 6. 30. 05:32

저희 동국대학교 컴퓨터공학과 산하 C.A.P.S (Computer Aided Progressive Study) 소모임에서는 매년 신입생/집부 세미나를 통해 학술적 지식 공유와 친목을 도모하는 행사를 진행하고 있습니다. 아래의 자료는 제가 작년에 33.5기 부회장을 맡고 있을 때 진행했던 집부 세미나 내용입니다. 

안녕하십니까? CAPS 부회장이자 홈페이지관리부장을 맡고 있는 조양진입니다. 이번 세미나의 제목은 “The Lost Art of Structure Packing”입니다. 제목에서 아실 수 있듯이 구조체 패킹에 관련하여 설명드리고자 합니다.

많은 분들이 기초 프로그래밍과 프로그래밍 기초와 실습 과목에서 C언어의 구조체를 배우셨을 것입니다. 그렇다면 과연 우리는 구조체를 완벽하게 이해하고 있을까요? 다음의 구조체 X를 임의로 제가 만들어보았습니다. Char형 멤버가 7개가 있습니다.

그렇다면 구조체의 X7 바이트가 되겠지요.

그렇다면 이 구조체 Y는 몇 바이트일까요? 구조체 Ychar 형 멤버 1, double형 멤버 1, 그리고 int 형 멤버 한 개가 있습니다.

일반적인 생각으로는 그럼 char1바이트, double8 바이트, int4바이트이므로 구조체 Y13바이트가 되어야 합니다. 과연 그럴까요?

다음과 같은 짧은 메인 함수를 작성하여 구조체 Y의 크기를 알아보았습니다만 13바이트가 아닌 24바이트의 크기를 출력했습니다.

그렇다면 구조체 Y는 왜 24바이트일까요? C의 구조체에는 Structure Padding이 있습니다. 이 패딩이라는 것은 컴파일러가 관리하며, 메모리에 위치한 데이터를 순서대로 정렬하기 위해서 구조체 멤버 메모리 주소 사이에 empty bytes를 삽입합니다. 따라서 우리가 아까 13바이트라고 예측했던 구조체가 24바이트가 된 것입니다.

 어떻게 해서 24바이트가 되었는지 보여드리겠습니다. Char 멤버 뒤에 패딩으로 7 바이트가 들어가고, double형 멤버와 int형 멤버 뒤에 패딩으로 4바이트가 들어갑니다. 이것만 봐서는 왜 패딩이 들어갔는지 이해하지 못합니다.

아까 말씀드린 것처럼 구조체의 멤버는 자신의 크기의 배수로 정렬되어야 효율적인 CPU 사용이 가능합니다. Char 형의 경우 1의 배수, short 형은 2의 배수, int형은 4의 배수, double형은 8의 배수의 메모리 번지 주소에 위치해야 정렬되어 있다고 할 수 있습니다.

그렇다면 아까 구조체 X로 돌아가봅시다. 그땐 아무생각 없이 char7개니깐 7바이트라고 생각했었는데요, char형의 경우는 1의 배수였기에 그냥 아무 메모리 번지에 들어가도 괜찮기 때문에 7 바이트가 맞았던 것입니다. 만약에 다른 자료형 멤버가 들어가 있었다면 다른 결과가 나왔겠지요

그럼 다시 구조체 Y24바이트가 되는 마법에 대해 다시 한번 자세히 알아봅시다. 제가 손수 그려왔습니다. char형은 아무곳이나 들어가도 되지요, 0번지에 들어갑니다. double형은 아까 8의 배수의 위치에 들어가야 한다고 말씀드렸고, 그렇기 때문에 char형과 double형 사이에 7바이트의 패딩이 들어가 double형이 8의 배수의 위치하게 됩니다. 이후 int형은 16번지에 들어가는데, 이는 4의 배수이므로 문제가 되지 않습니다. 그리고 마지막 int 뒤에 들어가는 패딩은 언뜻보면 필요 없는 패딩이라고 생각하실 수도 있습니다. 그러나 만약 구조체 배열을 사용한다면 char형은 상관 없겠지만 doubleint의 위치가 다시 틀어지겠지요. 이렇게 24바이트가 이루어집니다.

그렇다면 이 구조체 Z의 크기는 어떨까요? 이 구조체 Z는 아까 보여드린 구조체 Y에서 int형과 double 형의 위치만 바꾼 구조체입니다. 멤버의 순서만 바뀐 구조체니깐 24바이트일까요?

아니오. 사실은 24바이트가 아닌 16바이트입니다.

16바이트가 어떻게 구성되어 있는지 보여드리겠습니다. Char형 다음 패딩으로 3byte가 들어가 int형의 위치가 정렬되고, 이후 double형도 제 자리에 딱 들어가게 됩니다.

이렇듯이 우리 눈에는 거의 동일해보이는 두 구조체가 8바이트나 되는 큰 차이를 보입니다. 요즘 컴퓨터에서 8바이트 낭비하는거 따위가 어때서라고 묻기엔, 임베디드 시스템과 최적화 문제 앞에서는 상당히 큰 문제로 다가올 수 있을것입니다. 따라서 진정한 프로-프로그래머가 되기 위해서는 이런 사소한 부분까지 신경쓸 수 있으면 좋지 않을까요?

여태 설명드린 내용은 컴파일러가 cpu를 효율적으로 사용할 수 있게끔 해주는 패딩에 대해 알아봤는데요, 이번 세미나 발표의 제목은 the lost art of structure packing 이었습니다. 그렇다면 packing은 무엇일까요?

패킹이란 컴파일러가 패딩을 하지 않도록 하는 것입니다. Gcc 컴파일러에서는 attribute를 사용하고, visual c++ 컴파일러에서는 #pragma pack을 사용합니다.

MSDN에서는 default packing 사이즈가 8 바이트라고 합니다.

간혹 32비트 OS에서는 default packing size4바이트라고 하는 이야기가 있는데, 이는 낭설이며 OS가 아닌 컴파일러가 결정합니다. Default packing size8 바이트인 이유는 현재 기본 타입 중 가장 큰 타입이 8 바이트이기 때문이며, 추후에 int128 같은 자료형을 쓰게된다면 바뀔수도 있겠지요.

조금 고차원적인 생각을 해봅시다. 만약에 구조체를 TCP IP 소켓 통신으로 다른 아키텍쳐, 혹은 다른 OS의 클라이언트에 보낸다면 어떻게 될까요? 두 환경의 정렬제한 및 패딩의 크기가 서로 다를 수 있으므로 개발자는 예상치 못한 오류와 마주칠 것입니다. 따라서 울며 겨자먹기로 구조체를 패킹해서 보낼 순 있지만, 이는 성능 저하 혹은 불가능한 경우도 존재합니다. 따라서 가장 바람직한 방법은, 미리 프로토컬을 정해 serialization이라는 직렬화 과정을 통해 보내거나, 이전의 사례 및 라이브러리를 참고하는 것이 추천됩니다.

이번 세미나를 준비하며 참고했던 문헌입니다.

http://www.catb.org/esr/structure-packing/

 

The Lost Art of Structure Packing

This page is about a technique for reducing the memory footprint of programs in compiled languages with C-like structures - manually repacking these declarations for reduced size. To read it, you will require basic knowledge of the C programming language.

www.catb.org

https://stackoverflow.com/questions/4306186/structure-padding-and-packing

 

Structure padding and packing

Consider: struct mystruct_A { char a; int b; char c; } x; struct mystruct_B { int b; char a; } y; The sizes of the structures are 12 and 8 respectively. Are these structures padd...

stackoverflow.com

https://fresh2refresh.com/c-programming/c-structure-padding/

 

Structure padding in C

structure padding in C - Learn about structure padding in C. Know what is structure padding in C with example programs with output.

fresh2refresh.com

https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-6.0/aa273913(v=vs.60)?redirectedfrom=MSDN 

 

pack

pack 09/15/2006 2 minutes to read In this article --> #pragma pack( [ n] ) Specifies packing alignment for structure and union members. Whereas the packing alignment of structures and unions is set for an entire translation unit by the /Zp option, the pack

docs.microsoft.com

들어주셔서 감사합니다. 조양진이었습니다.