요새 matrix 연산을 이용한 프로그램 조각 몇 가지를 짜보고 있는데, mmx 같은 SIMD instruction 을 사용하면 matrix 연산의 속도를 확 올릴 수 있지 않을까 싶은 생각이 들길래 inline asm 을 이용해서 간단한 mmx 코드를 만들어보았습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <stdio.h> short s1[4] = { 1, 2, 3, 4 }; short s2[4] = { 5, 6, 7, 8 }; short d[4]; int main( int argc, char** argv ){ d[0] = s1[0] * s2[0]; d[1] = s1[1] * s2[1]; d[2] = s1[2] * s2[2]; d[3] = s1[3] * s2[3]; fprintf( stderr, "c: %d %d %d %d\n", d[0], d[1], d[2], d[3] ); d[0] = d[1] = d[2] = d[3] = 0; asm("movq (s1), %mm0"); asm("movq (s2), %mm1"); asm("pmullw %mm1, %mm0"); asm("movq %mm0, (d)"); fprintf( stderr, "asm: %d %d %d %d\n", d[0], d[1], d[2], d[3] ); return 0; } |
위와 같은 코드를 작성하고, gcc mmx.c 를 통해 컴파일해서 돌려보니 간단히 성공 -_-v
c 코드를 사용할 경우 s1[0] load, s2[0] load, multiply, save to d[0] 와 같은 인스트럭션을 네 번 반복해서 실행하는 반면 mmx 를 사용할 경우 movq 를 통해 연속된 WORD 네 개를 mmx register 로 복사하고, pmullw 를 이용 4 개의 값을 한 인스트럭션에 연산을 하는 것을 통해 속도를 확 끌어올릴 수 있는거죠. 😉
다만 헷갈리는게 인텔의 메뉴얼에 나와있는 인자 순서와, AT&T 방식이 달라서 좀 헷갈리는군요.
- Intel: movq mm0, [s1]
- AT&T: movq (s1), %mm0
Intel 메뉴얼에서 설명하는 바에 의하면 첫번째 operland 가 destination 이 되고, 두번째 operland 가 destination 이 되는 반면 AT&T 방식에서는 거꾸로 첫번째 operland 가 src, 두번째 operland 가 dst 가 됩니다.
또한 주소값을 넘겨줄 때 intel 방식은 [] 로 감싸주면 되지만, AT&T 에서는 () 로 감싸줘야하고, 레지스터 이름 앞에 %를 붙여줘야 하는 규칙도 있어서 뭔가 대빵 귀찮네요. -_-;
참고로 gcc 에서 -masm=intel 옵션을 사용하면 intel 방식으로 어셈블리 명령어를 작성하는 것도 가능합니다.
p.s) movq 는 4개의 WORD 를 mmx register 로 복사하는 명령인데 –;; mm0 ~ mm7 식으로 64bit register name 을 써줘야 하는데 xmm0~xmm7 같은 sse 용 register 이름을 쓰는 바람에 잘못된 인스트럭션 사용이라고 계속 에러가나서 한참 헤맸네요;