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.