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.