C++Guns – RoboBlog

10.05.2018

C++ Guns: schlecht generiertes Assember von Fortran Code

Filed under: Allgemein — Tags: , — Thomas @ 13:05

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.

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress