Analog zum letzten Beispiel mit C++ hier die angefangene Funktion mit Fortran. Auf die Getter Funktionen und ctors habe ich erst mal verzichtet. In Fortran ist es ohnehin nicht möglich immer vereinheitlichten Code zu schreiben, und es ist auch so schon schlimm genug.
module geometrymodule implicit none type Point2D_t real(8) :: xp, yp contains procedure :: Point2Dminus generic :: operator(-) => Point2Dminus end type type Line2D_t type(Point2D_t) :: pt1, pt2 end type contains pure function Point2Dminus(this, point1) result(point2) implicit none class(Point2D_t), intent(in) :: this type(Point2D_t), intent(in) :: point1 type(Point2D_t) :: point2 point2%xp = this%xp - point1%xp point2%yp = this%yp - point1%yp end function end module function func(line1, line2) result(denominator) use geometrymodule implicit none type(Line2D_t), intent(in) :: line1, line2 type(Point2D_t) :: a, b real(8) :: denominator a = line1%pt2 - line1%pt1 b = line2%pt1 - line2%pt2 denominator = a%yp * b%xp - a%xp * b%yp end function
Und hier der Fortran Code. Zur Erinnerung: Wir brauchen fünf Subtraktionen, zwei Multiplikationen und vier explizite Kopier-Befehle in C++. In Fortran zähle ich nur vier Subtraktionen, dafür eine Addition, zwei Multiplikationen, 15 Kopier (MOV) Befehle, ein push/pop Paar für den Stack, zwei Funktionsaufrufe, mit Point2D_t sind es sogar vier, und vier leaq Aufrufe. LEA steht für Load Effective Address. Hell NO. Das war mit Optimierung O1.
Point2d_t: movq (%rdi), %rax movq 8(%rdi), %rdx movq %rax, (%rsi) movq %rdx, 8(%rsi) ret point2dminus: movq (%rdi), %rax movsd 8(%rax), %xmm1 movsd (%rax), %xmm0 subsd (%rsi), %xmm0 subsd 8(%rsi), %xmm1 ret func_: pushq %rbx subq $48, %rsp movq %rsi, %rbx movq Point2d_t, 24(%rsp) leaq 16(%rdi), %rax movq %rax, 16(%rsp) movq %rdi, %rsi leaq 16(%rsp), %rdi call point2dminus movsd %xmm0, (%rsp) movsd %xmm1, 8(%rsp) movq Point2d_t, 40(%rsp) movq %rbx, 32(%rsp) leaq 16(%rbx), %rsi leaq 32(%rsp), %rdi call point2dminus mulsd 8(%rsp), %xmm0 mulsd (%rsp), %xmm1 subsd %xmm1, %xmm0 addq $48, %rsp popq %rbx ret
Mit Optimierung O2 wird es besser. Auf einmal sind es sieben Subtraktionen, dafür keine Addition mehr. Es bleibt bei zwei Multiplikationen. 13 Kopier MOV Befehle. Der Stack, LEA und die Funktionsaufrufe fallen weg. Hmmm, aber die Funktionen Point2d_t und point2dminus sind immer noch da. Dead Code elimination funktioniert also nicht. Damit bleiben effektiv fünf Subtraktionen, zwei Multiplikationen und 6 Kopier/MOV übrig. Naja immerhin.
Point2d_t: movq (%rdi), %rax movq 8(%rdi), %rdx movq %rax, (%rsi) movq %rdx, 8(%rsi) ret point2dminus: movq (%rdi), %rax movsd 8(%rax), %xmm1 movsd (%rax), %xmm0 subsd 8(%rsi), %xmm1 subsd (%rsi), %xmm0 ret func_: movsd (%rsi), %xmm0 movapd %xmm0, %xmm1 movsd 24(%rdi), %xmm0 subsd 16(%rsi), %xmm1 subsd 8(%rdi), %xmm0 mulsd %xmm1, %xmm0 movsd 8(%rsi), %xmm1 movapd %xmm1, %xmm2 movsd 16(%rdi), %xmm1 subsd 24(%rsi), %xmm2 subsd (%rdi), %xmm1 mulsd %xmm2, %xmm1 subsd %xmm1, %xmm0 ret
Werden hingegen Getter Funktionen für Point2D x und y implementiert, um ein einheitliches Interface für alle geometrischen Datentypen bereit zu stellen, was in C++ immer ohne Overhead möglich ist, kommen wieder Funktionsaufrufe und LEA Instruktionen in den Code. Und die gehn auch nicht mehr weg, egal mit welcher Optimierungsstufe.