{"id":3502,"date":"2018-08-30T20:41:10","date_gmt":"2018-08-30T19:41:10","guid":{"rendered":"http:\/\/roboblog.fatal-fury.de\/?p=3502"},"modified":"2018-08-30T20:44:27","modified_gmt":"2018-08-30T19:44:27","slug":"fortran-3x3-matmul-intrinsic-vs-hand-crafted","status":"publish","type":"post","link":"http:\/\/roboblog.fatal-fury.de\/?p=3502","title":{"rendered":"FORTRAN: 3x3 matmul intrinsic vs. hand crafted"},"content":{"rendered":"<p>Verglichen wird der erzeugte Assembler Code der internen Fortran matmul() Routine mit einer von Hand geschriebenen Matrix Multiplikation.<br \/>\nGetestet wird f\u00fcr 3x3 Matrizen mit zur Compilezeit bekannter Gr\u00f6\u00dfe. Sonst k\u00f6nnte man den vom FORTRAN Compiler erzeugten Assembler Code gar nicht mehr nachvollziehen.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nsubroutine intrinsicmatmul(a,b,c) \r\n  implicit none\r\n  real(8), intent(in) :: a(3,3), b(3,3)\r\n  real(8), intent(inout) :: c(3,3)\r\n  \r\n  c = matmul(a,b)\r\nend subroutine\r\n<\/pre>\n<p>Und hier von Hand<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nsubroutine handmatmul(a,b,c)\r\n  implicit none\r\n  real(8), intent(in) :: a(3,3), b(3,3)\r\n  real(8), intent(inout) :: c(3,3)    \r\n  integer :: i,j,k\r\n  \r\n  c(:,:) = 0\r\n  do i=1, 3\r\n    do j=1, 3\r\n      do k=1, 3\r\n        c(j,i) = c(j,i) + a(j,k) * b(k,i)\r\n      end do\r\n    end do\r\n  end do\r\nend subroutine\r\n<\/pre>\n<p>Getestet wird mit gfortran 7.3.0 -O1. Die Hand Version hat 13 Instruktionen mehr. Hiervon sind 6 Instruktionen f\u00fcr das Sichern und Wiederherstellen von Register. Der Rest geht wohl auf eine andere Art Adressberechnung drauf. Da die intrinsic <em>matmul<\/em> Routine selbst bei -O1 geinlint wird, ich sehe kein Funktionsaufruf im Assembler Code, wird sie wohl schneller sein.<br \/>\nTests mit Praxiscode zeigen aber, dass das nicht so sein muss. Es wurden mehrere Matrizen multipliziert.<\/p>\n<p>Es gilt: messen, messen messen. Bei dem n\u00e4chsten Compiler\/CPU kann es wieder anders sein.<\/p>\n<table>\n<tr>\n<td>\nintrinsic<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nintrinsicmatmul_:\r\n\tmovq\t$0, (%rdx)  \r\n\tmovq\t$0, 8(%rdx) \r\n\tmovq\t$0, 16(%rdx)\r\n\tmovq\t$0, 24(%rdx)\r\n\tmovq\t$0, 32(%rdx)\r\n\tmovq\t$0, 40(%rdx)\r\n\tmovq\t$0, 48(%rdx)\r\n\tmovq\t$0, 56(%rdx)\r\n\tmovq\t$0, 64(%rdx)\r\n\tleaq\t24(%rdx), %rcx\r\n\taddq\t$24, %rsi\r\n\tleaq\t96(%rdx), %r9\r\n\tleaq\t96(%rdi), %r10\r\n.L4:\r\n\tleaq\t24(%rdi), %rdx\r\n\tmovq\t%rsi, %r8\r\n.L3:\r\n\tmovsd\t-24(%r8), %xmm1\r\n\tmovl\t$0, %eax\r\n.L2:\r\n\taddq\t$1, %rax\r\n\tmovapd\t%xmm1, %xmm0\r\n\tmulsd\t-32(%rdx,%rax,8), %xmm0\r\n\taddsd\t-32(%rcx,%rax,8), %xmm0\r\n\tmovsd\t%xmm0, -32(%rcx,%rax,8)\r\n\tcmpq\t$3, %rax\r\n\tjne\t.L2\r\n\taddq\t$8, %r8\r\n\taddq\t$24, %rdx\r\n\tcmpq\t%r10, %rdx\r\n\tjne\t.L3\r\n\taddq\t$24, %rcx\r\n\taddq\t$24, %rsi\r\n\tcmpq\t%r9, %rcx\r\n\tjne\t.L4\r\n\trep ret\r\n<\/pre>\n<\/td>\n<td>\nHand<\/p>\n<pre class=\"brush: plain; highlight: [2,3,4,47,48,49]; title: ; notranslate\" title=\"\">\r\nhandmatmul_:\r\n\tpushq\t%r12               # -\r\n\tpushq\t%rbp               # |- Register r12, rbp, rbx f\u00fcr Laufvariablen i,j,k freimachen\r\n\tpushq\t%rbx               # -\r\n\tmovq\t$0, (%rdx)         # -\r\n\tmovq\t$0, 8(%rdx)        # |\r\n\tmovq\t$0, 16(%rdx)       # |\r\n\tmovq\t$0, 24(%rdx)       # |\r\n\tmovq\t$0, 32(%rdx)       # |- c(:,:) = 0\r\n\tmovq\t$0, 40(%rdx)       # |\r\n\tmovq\t$0, 48(%rdx)       # |\r\n\tmovq\t$0, 56(%rdx)       # |\r\n\tmovq\t$0, 64(%rdx)       # -\r\n\tleaq\t24(%rsi), %r9\r\n\tleaq\t96(%rsi), %r12\r\n\tmovq\t%rdx, %rcx\r\n\tsubq\t%rsi, %rcx\r\n\tleaq\t-24(%rcx), %rbp\r\n\tmovq\t%rcx, %rbx\r\n.L4:                               # i Schleife Anfang\r\n    leaq\t0(%rbp,%r9), %r8\r\n\tmovq\t%rdi, %r10\r\n\tleaq\t(%rbx,%r9), %rdx\r\n.L3:                               # j Schleife Anfang\r\n\tmovq\t%r8, %r11 \r\n\tmovsd\t(%r8), %xmm1       # c(i,j) nach xmm1 laden\r\n\tmovq\t%rsi, %rax\r\n\tmovq\t%r10, %rcx\r\n.L2:                               # k Schleife Anfang\r\n\tmovsd\t(%rcx), %xmm0      # -\r\n\tmulsd\t(%rax), %xmm0      # |- c(j,i) + a(j,k) * b(k,i)\r\n\taddsd\t%xmm0, %xmm1       # -\r\n\taddq\t$24, %rcx          # 3x double = 24Byte\r\n\taddq\t$8, %rax           # 1x double = 8byte. \r\n\tcmpq\t%r9, %rax          # rax wird gleichzeitig als Pointer und Laufvariable benutzt.\r\n                               # In r9 steht der Wert den rax erreicht, wenn die Schleife terminiert\r\n\tjne\t.L2                # k Schleife Ende\r\n\tmovsd\t%xmm1, (%r11)      # c(j,i) = xmm1 \r\n\taddq\t$8, %r8\r\n\taddq\t$8, %r10\r\n\tcmpq\t%rdx, %r8\r\n\tjne\t.L3                # j Schleife Ende\r\n\taddq\t$24, %rsi\r\n\taddq\t$24, %r9\r\n\tcmpq\t%r12, %r9\r\n\tjne\t.L4                # i Schleife Ende\r\n\tpopq\t%rbx               # -\r\n\tpopq\t%rbp               # |- Register aus dem Stack wieder herstellen\r\n\tpopq\t%r12               # -\r\n\tret \r\n<\/pre>\n<\/td>\n<\/tr>\n<\/table>\n<p>F\u00fcr Optimierung O2 gilt 35 Instruktionen f\u00fcr intrinsic und 43 f\u00fcr Hand.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Verglichen wird der erzeugte Assembler Code der internen Fortran matmul() Routine mit einer von Hand geschriebenen Matrix Multiplikation. Getestet wird f\u00fcr 3x3 Matrizen mit zur Compilezeit bekannter Gr\u00f6\u00dfe. Sonst k\u00f6nnte man den vom FORTRAN Compiler erzeugten Assembler Code gar nicht mehr nachvollziehen. subroutine intrinsicmatmul(a,b,c) implicit none real(8), intent(in) :: a(3,3), b(3,3) real(8), intent(inout) :: c(3,3) [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[30],"class_list":["post-3502","post","type-post","status-publish","format-standard","hentry","category-allgemein","tag-fortran"],"_links":{"self":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/3502","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3502"}],"version-history":[{"count":19,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/3502\/revisions"}],"predecessor-version":[{"id":3694,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/3502\/revisions\/3694"}],"wp:attachment":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3502"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3502"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3502"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}