mmx

요새 matrix 연산을 이용한 프로그램 조각 몇 가지를 짜보고 있는데, mmx 같은 SIMD instruction 을 사용하면 matrix 연산의 속도를 확 올릴 수 있지 않을까 싶은 생각이 들길래 inline asm 을 이용해서 간단한 mmx 코드를 만들어보았습니다.

위와 같은 코드를 작성하고, 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 이름을 쓰는 바람에 잘못된 인스트럭션 사용이라고 계속 에러가나서 한참 헤맸네요;

Published by

24 thoughts on “mmx”

  1. 오우.. mmx를 이용하면 matrix를 이렇게도 계산할 수 있군요!

    그런데 컴파일이 안되는 이유는 뭐지.. -_-
    cygwin에서는 안되는건가…

  2. 흠 -masm=att 로 해도 안되면 asm(“…”); 를 __asm__(“…”); 로 바꿔보세요~ linux / gcc-3.4.1 에서 테스트한 코드라 흐흐흐

    8×8 matrix 의 경우라면 8×8 개의 값들 각각을 구하는데 곱하기가 8번, + 가 7번 필요한데… * 8번을 pmullw 2 번으로 줄일 수 있으니 상당히 빨라질거 같아요~

    다만 mmx 로는 WORD 단위 정수 연산밖에 못하고, 32bit DOUBLE WORD 를 SIMD 로 계산하려면 SSE2 를 사용해야 하는데 (PMULUDQ 던가;; ) 이건 unsigned 라는 제한이 있네요. (overflow flag 같은걸 무시하면 똑같은 결과를 나으려나요;;; )

    AMD manual 은 가지고 있질 않아서 3D now 는 어떤지 잘 모르겠네요 캬캬캭

  3. 오우..
    그런 방법이.. ㅎㅎㅎ 나중에 다시 한 번 해봐야겠네요..
    그나저나..
    C코드를 ASM코드 수준까지 바라보고 코딩을 해야한다니..-_-;
    앞이 막막;;;

  4. 흐흐 asm 으로 짜면 아무래도 가독성이나 호환성이 떨어지니까 꼭 필요한 상황이 아니면 별로 쓸 일이 없을 거 같기는 해요~

  5. 이 글이 아니라 위에 있는 4×4 matrix 연산과 관련된 그림이라면 omni graffle 을 이용해서 그린 것입니다. (맥 전용 툴입니다.)

  6. vc 에서는 asm block 이라고 해서 아래와 같이 사용하시면 됩니다.

    __asm {
    movq mm0, [s1]
    movq mm1, [s2]
    pmullw mm0, mm1
    movq [d], mm0
    }

    보시면 위의 예제와 오퍼랜드 순서가 다른 것을 볼 수 있는데, 이 방식이 intel 방식이고 위에 글에 써있는 방식이 at&t 방식입니다. Visual C++ 에서는 intel 방식을 사용하고 있으니 위와 같이 수정을 해준거구요. (nasm 도 intel 방식을 따르고 있고, gcc 에서도 옵션을 통해 intel 방식 코드를 사용할 수 있습니다.)

    4×4 매트릭스 연산을 구현한 코드를 올려두었으니 한 번 참고해보시길 ;)

    http://mytears.org/resources/mysrc/asm/matrix_multiple/matrix_multiple_with_vc.c

    Visual C++ 에서 어셈블리를 이용하는 것에 대해 더 자세히 알고 싶으시다면 아래 링크를 참고하시면 됩니다.

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_core_Assembler_.28.Inline.29_.Topics.asp

  7. http://mytears.org/resources/mysrc/…_multiple/matrix_multiple_with_vc.c 여기 소스 중에서
    movq mm0, s1[ecx*8] 부분이 있는데 좀 더 자세히 설명해 주실수 있을까요?
    s1은 분명히 1부터 16까지의 값을 갖는데 s1[24]에 19라는 값이 있는게 이해가 안 됩니다.

  8. s2[24] 랑 헷갈리신거 아닌가요? s2 은 short 배열이기 때문에 각각의 값들은 2바이트씩을 차지하게 되므로, s2[24] 는 12번째 값을 가리키게 되니까 (숫자는 1부터가 아니라 0 부터 셉니다.) 19가 맞거든요.

    하여튼 좀 더 자세히 설명해달라고 하신 부분은… s1 이 가리키는 주소값 + ecx*8 서부터 8바이트를 복사해서 mm0 레지스터에 저장하는 명령입니다.

  9. 네~ 감사합니다.
    요즘 SSE라는 것을 보고 있는데 vc 6.0 버전에서는 실행이 안되더라구요. 인터넷을 검색해보니까 .net 버전에서부터 지원이 된다고 합니다. 그래서 리눅스에서 해보려고 하는데 리눅스에서는 SSE를 쓸수 있을까요?

  10. vc6.0에서도 SSE2까지 사용가능합니다. 기본적으로 지원하는건 아니고 서비스 팩과 processor pack이라는걸 설치해야 합니다.

  11. 디- // SSE 는 vc 에서 테스트해보질 않아서 관련해선 몰랐었는데 답변 감사합니다. :)

    제 시스템에는 visual studio servicepack 6 가 설치되어 있었는데… processor pack 은 servicepack 5 용밖에는 제공되질 않아서 다운그레이드해야 하는 불편함이 있네요. -_ㅜ

  12. 리눅스에서 8X8 matrix의 곱을 SSE2로 코딩을 하고 있는데 기존에 정태영님께서 쓰신 ecx, eax 레지스터를 쓸수가 없어서 rax, rcx 레지스터를 쓰려고 하는데 컴파일할때 rax, rcx 레지스터를 인식하지 못하는데 혹시 해결방안이 있을까요?

  13. ecx 랑 eax 를 쓸 수 없다는 게 무슨 말인지 모르겠네요. push, pop 을 이용하시면 문제가 되지 않을 듯 싶은데요. 그나저나 rax, rcx 가 뭐하는 레지스터인지 몰라서 어떻게 말을 해드릴 수가 없네요.

  14. ecx와 eax는 32비트라서 sse2의 64bit 또는 128bit을 처리하는데 한계가 있는것 같습니다. 그래서 찾은게 eca, eax와 동일한 기능이지만 64bit / 128bit 지원이 되는 rcx와 rax입니다. push와 pop을 쓰면 64bit / 128 bit을 사용하는데 무리가 없을까요?

  15. 아… 그런데 ecx 랑 eax 는 배열 인덱스 때문에 사용하는 레지스터들인데 구지 64bit 나 128bit 를 지원해야할 이유가 있을지…

  16. 문제는 eax가 32bit으로 설정되어있기 때문에 “movq mm0, s1(eax*8)” 를 “movq mm0, s1(eax*16)”으로 고치고 싶지만 에러가 나오기 때문에 rax와 rcx를 사용하려고 합니다.

  17. 그 부분이 문제라면 mov $2 %eax 와 같은 코드를 mov $4 %eax 처럼 미리 2를 곱해준 값을 로드하도록 바꾸면 훨씬 쉽게 해결할 수 있지 않을까요?

    그리고 *16을 하는거보니까 word 형 데이타 8개가 될 듯 싶은데, 그렇다면 movq 가 아니라 movdqu 를 사용하시고 mm 레지스터가 아닌 xmm 레지스터를 활용하세요. SSE2 가 지원된다면 128bit data 를 한번에 로드하는 것과 word 형 데이타 8개를 한꺼번에 계산하는 것 모두가 가능합니다. :)

    덧: 이상하게 제 athlon-xp + visual studio 6.0 + servicepack5 + processor pack 의 조합에서 movdqu 가 64bit 데이타 밖에 가져오질 못하는 이상한 현상을 보이고 있네요.

    pentium4 + linux + gcc 조합에선 메뉴얼에 나와있는 데로, 또 제가 의도한 대로 그대로 동작하는 걸로 보아 제대로 사용한 것은 맞는 듯 싶은데… vs.net 을 설치해서 테스트해보던지 해야겠네요. -_ㅜ

  18. athlon-xp 에서 sse2 를 지원되질 않아서 생긴 문제였네요. -_-;; visual studio express 를 설치하면서까지 시도를 해봤었는데 완전 뻘짓했군요.

    학교에 있는 펜티엄4 에서는 정상 작동합니다.

  19. 네 감사합니다. 바쁘실텐데 고민거리를 만들어 드렸었군요. 주말동안 시도해 보겠습니다.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">