대강 생각을 해보니 정말 mmx 를 이용해서 빠르게 연산을 하려면 위와 같이 하는게 가장 빠르겠군요. 다만 레지스터를 많이 쓰고 완전히 asm 코딩을 해야한다는 게 조금 귀찮겠군요. 😉
위의 다이아그램에 있는 과정을 통해 4×4 matrix * 4×4 matrix 의 한 row 씩을 계산해낼 수 있습니다. 대강 계산했을 때 3배 이상의 속도 향상이 있을거라고 예상되던데 과연~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
#include <stdio .h> // A matrix short s1[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, }; // Transpose(B matrix) short s2[16] = { 17, 21, 25, 29, 18, 22, 26, 30, 19, 23, 27, 31, 20, 24, 28, 32 }; // Destination matrix short d[16]; int j, i; int main( int argc, char** argv ){ __asm__("movq (s1), %mm0" ); __asm__("movq %mm0, %mm1" ); __asm__("movq %mm0, %mm2" ); __asm__("punpckhdq %mm2, %mm0" ); __asm__("punpckldq %mm2, %mm1" ); __asm__("movq %mm0, %mm6"); __asm__("movq %mm1, %mm7"); __asm__("movq (s2), %mm2" ); __asm__("mov $1, %eax" ); __asm__("movq s2(,%eax,8), %mm4"); __asm__("movq %mm2, %mm3" ); __asm__("punpckhdq %mm4, %mm2"); __asm__("punpckldq %mm4, %mm3"); __asm__("pmaddwd %mm2, %mm0"); __asm__("pmaddwd %mm3, %mm1"); __asm__("paddw %mm1, %mm0"); __asm__("movq %mm6, %mm1"); __asm__("movq %mm7, %mm2"); __asm__("mov $2, %eax" ); __asm__("movq s2(,%eax,8), %mm3" ); __asm__("mov $3, %eax" ); __asm__("movq s2(,%eax,8), %mm5"); __asm__("movq %mm3, %mm4" ); __asm__("punpckhdq %mm5, %mm3"); __asm__("punpckldq %mm5, %mm4"); __asm__("pmaddwd %mm3, %mm1"); __asm__("pmaddwd %mm4, %mm2"); __asm__("paddw %mm2, %mm1"); __asm__("packssdw %mm1, %mm0"); __asm__("movq %mm0, (d)"); for( j = 0 ; j < 4 ; j++ ){ for( i = 0 ; i < 4 ; i++ ){ fprintf( stderr, "\t%3d", d[j*4+i] ); } fprintf( stderr, "\n" ); } return 0; } |
코드로 옮기니 위와 같군요. 중간에 실수로 바이트오더를 헷갈려서 연산 결과가 뒤집혔었습니다. 정상적인 결과는 250 260 270 280 이 나와야 하는데 280 270 260 250 이 나와버리더군요. 아아 이거 다시 하고 싶은 작업이 아니네요;
흐흣 그래도 오랫만에 어셈블리 관련된 것들을 생각하고 있는데, 이것도 가끔 하니까 재밌네요. 근데 길어지면 할만하지 않다는거 -_-!
p.s) 전체 연산 코드를 보고 싶으시면 http://mytears.org/resources/mysrc/c/mmx.c 를 보시길 😉