MMX 프로그래밍 소개
MMX나 SSE 등 SIMD 명령이라는게 있습니다. 정말정말 간단하게 생각하면 이런겁니다.
128비트 레지스터가 있습니다. 이름이 XMMS0 ~ XMMS15입니다. 64비트 범용 레지스터보다 2배가 크고 이걸 이용한 특별한 어셈블리 명령어들이 있습니다. 그 명령어들 중에서 정수 연산을 위한 명령어들을 MMS instruction set이라고 부르고 부동소수점 연산을 위한 명령어들을 SSE라고 부르는데 인텔이 이름을 지은 것입니다. AMD는 다른 이름이 있습니다.
여튼 그런 특별 명령들이 있는데 뭘하는 것들이냐. 이런 예에서 사용합니다.
32비트 칼라 이미지가 있습니다. 한 픽셀이 32비트입니다. 이 이미지의 밝기를 좀더 밝게 만들고 싶습니다. C로 만들면 어떻게해야할까요? 32비트 변수를 만들어서 한 픽셀을 읽어서 각 RGB값을 분리해서 값을 증가시키고 다시 32비트로 합쳐서 메모리에 쓰게됩니다.
uint32_t pixel = *ptr;
uint8_t r = get_r(pixel);
uint8_t g = get_g(pixel);
uint8_t b = get_b(pixel);
r++, g++, b++;
*ptr = set_rgb(r, g, b);
요즘 이미지가 몇백만 픽셀인가요? 몇백만번 루프를 돌겠네요. 이런 루프에 컴파일러가 최적화를 할 수 있을까요? 알고리즘을 개선할 수 있으세요?
예전에 그래픽이 화려하지 않을때는 이미지 데이터가 별로 없으니 이런 이미지 처리 능력이 별로 중요하지 않았습니다. 그러다 점점 더 그래픽이 화려한 게임이 나오면서 더 빠른 그래픽 처리가 필요해지고, 윈도우즈의 UI도 화려해질 필요가 생겼습니다.
그래서 SIMD라는게 나왔습니다. 한 레지스터가 128비트입니다. 32비트 픽셀 4개를 저장할 수 있습니다. 이때 메모리에서 32비트씩 4번 읽어서 128비트에 32비트 저장하고 비트 쉬프트를하고 또 저장하고 쉬프트하고 그런게 아닙니다. 바로 메모리에서 128비트를 한꺼번에 읽어와서 레지스터를 채웁니다.
또 ++ 연산, 어셈블리로 inc 연산을 바로 128비트 레지스터에 실행합니다. 내가 원하는 데이터 단위가 32비트라는것만 지정해주면 알아서 32비트 단위로 쪼개서 1씩 증가해놓습니다.
예를 들어서 128비트 레지스터에 0x12345678 이라는 32비트 픽셀 4개가 있다고 하겠습니다.
12 34 56 78 12 34 56 78 12 34 56 78 12 34 56 78
여기에다가 MMX 명령으로 어셈블리 프로그래밍을 하면 한번에 각 픽셀에 1씩 더할 수 있게됩니다.
12 34 56 79 12 34 56 79 12 34 56 79 12 34 56 79
4번에 나눠서 할일을 1번에 해버리니 속도가 4배까지는 아니더라도 2~3배는 빨라집니다. 이게 바로 MMX 명령입니다. 데이터가 부동소수점이라면 SSE 명령을 쓰면 똑같이 여러개 데이터를 한꺼번에 처리하게됩니다.
요즘은 GPU가 이런 일을 하지요. 하지만 GPU는 프로세서밖에 있는 장치입니다. 데이터를 주고받는데 시간이 더 걸립니다. CPU안에 이런 계산 장치를 두는게 더 빠릅니다.
이런 특수 명령들은 컴파일러가 알아서 사용하지 못합니다. 메모리를 계속 복사하거나 쓰기만하는 단순한 일은 컴파일러가 rep movs등을 쓰겠지만, 이런 특수한 상황은 자동으로 판단하기 어렵습니다. 개발자가 알아서 특수 명령을 쓰도록 프로그래밍을 해야합니다.
우리는 이미 이런 특수 명령을 쓰는 프로그램을 쓰고 있습니다. 이미지 처리 라이브러리들, 암호화 라이브러리들, 이미지 처리 어플들 다 이런 특수 명령을 쓰기때문에 그런 속도가 나오는 것입니다. 흑백 이미지의 밝기 조절을 직접 C로만 만들어보세요. 아무리 최신 프로세서라고해도 이미지가 커질수록 눈에 띄게 처리가 느려집니다. 우리가 그런 라이브러리 등을 만들 기회가 없어서 모를뿐 기술적으로 지배력있는 회사들, 프로젝트들은 하드웨어 성능을 모두 활용하기위해 할 수 있는 것은 다 합니다. 우리가 만약 그걸 못하거나 해야할 필요를 못느낀다면 우리가 하는 일들이 사실은 기술적으로 경쟁력이 부족할지도 모른다는 것이고, 우리를 어디 제 3국가의 외주 인력으로 교체해도 회사는 돌아간다는 이야기일 것입니다.
LISP이나 Ocaml, Haskel, 자바스크립트로 개발하는데 어셈블리가 필요하다는 말은 못들어봤습니다. 그런 고급 언어로 개발하시는 분야에 있으시면 신경안쓰셔도 되지요. C나 C++, 자바도 그럴것 같고요 시스템 프로그래밍을 하는 분야에 있으시면 최소한 디버깅할 때 디스어셈블 코드를 두려워하지않고 어디서 문제가 생겼는지를 밝혀낼 정도는 되야한다고 생각합니다. 아직은 시스템 프로그래밍이 사람보다는 기계에 가까운 수준이니까요. 언젠가는 AI가 대신 디버깅을 해줄지도 모르겠습니다만 그때는 또 AI를 디버깅할 사람이 필요하겠지요.