Laden...

[erledigt] Performancevergleich C# - Fortran

Erstellt von gaussmath vor 13 Jahren Letzter Beitrag vor 13 Jahren 4.874 Views
G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren
[erledigt] Performancevergleich C# - Fortran

Warum ist dieser Fortran-Code (kompiliert mit den aktuellsten Intel Fortran Compiler, 32 bit)


Subroutine PERMUTATIONUD(PArray, SizePArray)
!DEC$ ATTRIBUTES DLLEXPORT :: PERMUTATIONUD
    implicit none
    integer :: SizePArray
    integer, dimension(SizePArray) :: PArray
    real :: T1, T2

    CALL CPU_TIME(T1)

    call PERMUT(PArray, SizePArray, SizePArray)
    CALL CPU_TIME(T2)

    write( *, * ) T2-T1


End Subroutine PERMUTATIONUD


Subroutine SWAPUD(PArray, FirstIndex, SecondIndex, SizePArray)

    implicit none
    integer :: FirstIndex, SecondIndex,SizePArray
    integer, dimension(SizePArray) :: PArray
    integer :: tmp

    tmp = PArray(FirstIndex)
    PArray(FirstIndex) = PArray(SecondIndex)
    PArray(SecondIndex) = tmp

End Subroutine SWAPUD
 
recursive Subroutine PERMUT(PArray, EndIndex, SizePArray)

    implicit none
    integer :: EndIndex, SizePArray,i
    integer, dimension(SizePArray) :: PArray

    if( EndIndex == 1 ) then
    !write( *, * ) PArray
    i=1

    else
    call PERMUT(PArray,EndIndex-1,SizePArray )

    do i = 1, EndIndex-1
    call SWAPUD(PArray,i,EndIndex,SizePArray)
    call PERMUT(PArray,EndIndex-1,SizePArray)
    call SWAPUD(PArray,i,EndIndex,SizePArray)
    end do

    end if

End Subroutine PERMUT

langsamer als dieser (kompiliert mit VS 2010, 32 bit)?


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace CSharpDllTest
{
    public class PERMUTATIONUD
    {
        public void PERMUTATIONUDMAIN(int[] arr, int SizeArray)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            permut(arr, SizeArray-1);
            sw.Stop();
            long _milliSeconds = (long)(sw.ElapsedMilliseconds);
            Console.WriteLine(_milliSeconds/1000.0);
           
        }

        private void permut(int[] arr, int EndIndex)
        {
            int i;
            if (EndIndex == 0)
                //PrintMethod(arr);
                i = 1;
            else
            {
                permut(arr, EndIndex - 1);
                for (i = 0; i < EndIndex; i++)
                {
                    this.swap(arr, i, EndIndex);
                    this.permut(arr, EndIndex - 1);
                    this.swap(arr, i, EndIndex);
                }
            }

        }

        private void swap(int[] arr, int FirstIndex, int SecondIndex)
        {
            int tmp = arr[FirstIndex];
            arr[FirstIndex] = arr[SecondIndex];
            arr[SecondIndex] = tmp;
        }

        private void PrintMethod(int[] arr)
        {
            string temp = "";
            foreach (var item in arr)
            {
                temp += item.ToString();
                temp += ", ";

            }
            Console.WriteLine(temp);
        }
    }
}

458 Beiträge seit 2007
vor 13 Jahren

be the hammer, not the nail!

G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren

Danke für die Antwort. Bei diesem Code verhält es sich allerdings genau anders herum! Fortran ist langsamer als C#. Deswegen habe ich diesen Thread erstellt, weil mich dieser Sachverhalt doch sehr verwundert.

Gelöschter Account
vor 13 Jahren

Weil die Bremse in diesem Fall vermutlich durch die Speicherallokierung definiert wird.
.NET hat ein intelligentes Speichermanagement. Fortran nicht. und wenn du dauernd häppchenweise Speicher in Fortran allokierst, dann dauert das lange... .NET macht das immer in großen Brocken.

Also ich bin jetzt nicht der Fortran experte aber der code schaut fast danach aus... und wenn du den C# code noch sauber aufziehen würdest und statt += den Stringbuilder nehmen würdest, dann wär der c# Code erstmal so richtig schnell.

edit: ok ich hab den code nur überflogen... meine antwort ist mist.. sry...

S
401 Beiträge seit 2008
vor 13 Jahren

Hallo,

ich habe deinen Code nur mal überflogen und gleich was aufgefallen. Als erstes ist die geringe/kleine Aufgabenstellung. Hier sollte man sehr Vorsichtig mit den gewonnen Ergebnissen umgehen.

Ähm, ich kenne C# nicht zu 100% auswendig, aber du berechnest ja die Zeit-Differenz.
In C# nimmst du die Differenz von Millisekunden.
In Fortran nimmst du die Differenz von Microsekunden.
Achtung, Faktor 10^3 !

cpu_time gibt in der Regel die Zeit in Microsekunden zurück. Zumindestens nach meinen Kentnissstand und Buch 😃 Hier ein kleiner Auszug aus dem Fortran-Wiki als Beleg

If a time source is available, time will be reported with microsecond resolution. If no time source is available, time is set to -1.0.

Edit: Da habe ich wohl etwas übersehen. Ein erster Test zeigte mir, dass du die Millisekunden auf die Sekunden umrechnest.

Edit: Welche Compiler-Flags hast du beim Fortran-Compiler gesetzt? Debug, Release, Performance?

Gruß, Thomas

G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren

Edit: Welche Compiler-Flags hast du beim Fortran-Compiler gesetzt? Debug, Release, Performance?

Hi!

Ich verwende die Integration des Intel Fortran Compilers ins VS. Wie stellt man das dort ein?

G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren

Wenn ich übrigens eine reine Fortrananwendung erstelle und die DLL nicht in einer .NET-Anwendung aufrufe, ist der Performance deutlich schneller. Ungefähr 3 Mal so schnell wie der C#-Code.

Warum ist das so? Entsteht durch das Aufrufen der Fortran-DLL innerhalb der .NET-Anwendung ein höherer Speicherverwaltungsaufwand?

1.130 Beiträge seit 2007
vor 13 Jahren

Auch pinvokes werden gejittet. Hast du den ersten aufruf mit dem zweiten verglichen?

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren

Hast du den ersten aufruf mit dem zweiten verglichen?

Wie meinst du das?

1.130 Beiträge seit 2007
vor 13 Jahren

den test zweimal hintereinander durchgeführt, ohne den prozess neuzustarten. Der erste durchgang dauert in c# grundsätzlich länger als die folgenden.

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren

Ja, habe ich getan. C# schneidet immer besser ab. Nach dem zweiten, dritten Durchlauf sogar noch besser.

S
401 Beiträge seit 2008
vor 13 Jahren

Ich verwende die Integration des Intel Fortran Compilers ins VS. Wie stellt man das dort ein?

Ich benutze den Intel Compiler nur unter Linux. Daher kann ich dir keine Infos hierzu geben. Aber die Vermutung liegt nahe, dass du die Standardeinstellung Debug verwendest.

Bei einem sauberen Vergleich darfst du die Fortran-Anwendung nicht aus einer C#-Anwendung heraus starten.

Um das ganze nachvollziehen zu können, wäre es interessant zu wissen*welche Flags du beim compilieren gesetzt hast. *wie dein System aussieht *welche Ergebnisse (Werte) du bekommst

G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren

OK, ich formuliere einmal meine Zielstellung. Ich programmiere ein Planungstool in C#. Zu Optimierungszwecken möchte ich (numerische) Berechnungen nach Fortran-DLLen auslagern. Denn, kurzum, möchte man Rechnen und gigantische numerische Objekte "wälzen" ist Fortran schneller.

Die Frage ist nun, wie ich das praktisch umsetzen kann. Ich habe also eine kleine Beispiel-DLL in Fortran erstellt und rufe diese aus meiner C#-Anwendung heraus auf. Jetzt muss ich aber feststellen, dass das ganze dann langsamer wird.

Rufe ich dieselbe DLL aus einer Fortran-Anwendung heraus auf, benötigt der Durchlauf für n=11 1,2 Sekunden, aus C# heraus jedoch 5 Sekunden.

Woran liegt das? Ist es ein zusätzlicher Verwaltungsaufwand des .NET Frameworks? Wenn ja, was kann ich optimieren?

Die Hauptfrage ist aber, lohnt sich das Auslagern nach Fortran-Bibliotheken überhaupt? Mein bisherigen Versuche haben gezeigt, dass dies nicht der Fall ist.

Gelöschter Account
vor 13 Jahren

Ich denke, das für dich die optimale Lösung F# darstellt. Hast du darüber schon mal nachgedacht?

5.742 Beiträge seit 2007
vor 13 Jahren

Ich denke, das für dich die optimale Lösung F# darstellt.

Oder C++ (mixed managed und unmanaged) - da klappt die Integration in .NET (vergleichweise) einwandfrei.

Gelöschter Account
vor 13 Jahren

F# eignet sich besser für die umsetung von algorithmen.

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Jack,

F# läuft aber genauso wie C# in der CLR, wogegen C++ nativ läuft. Das sagt natürlich nicht direkt aus, was schneller ist, ist aber ein Umstand, den man berücksichtigen muss. Außerdem ist der Einstieg in eine funktionale Sprache natürlich eine größere Klippe als in eine andere objektorientierte Sprache (auch wenn C++ genau genommen eine hybride Sprache ist, prozedural/objektorientiert).

Aber bevor wir relativ abstrakt spekulieren, sollten wir sowieso erstmal abwarten, was gaussmath sagt. 😃

herbivore

S
401 Beiträge seit 2008
vor 13 Jahren

Die Hauptfrage ist aber, lohnt sich das Auslagern nach Fortran-Bibliotheken überhaupt? Mein bisherigen Versuche haben gezeigt, dass dies nicht der Fall ist.

In der ersten Umsetzung mit Sicherheit nicht. Wenn keine Erfahrungswerte aus anderen, vergleichbaren Projekte vorliegt sollte man erst alles einmal in der bevorzugten Sprache implementieren. Das scheint bei dir C# zu sein. Erst wenn man während der ersten Test's feststellt, dass eine Optimierung auf Zeit notwendig bzw. wünschenswert ist, kann man sich Gedanken über Auslagerungen machen.

Bei deinem Beispiel kann C# durchaus schneller sein als Fortran. Du berechnest kar nichts, sondern schiebst nur ein paar Int-Werte hin und her. Somit kann Fortran nicht die Stärken mit hoch optimierten Code z.B. mit SSE ausspielen.
Mich würde es interessieren, wie es bei einer hoch ausgelasteten .NET-Plattform aussieht. Ich kann mir vorstellen, dass dann die Geschw. von .NET in die Hose fällt.

Gruß, Thomas

G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren

Danke nochmal für eure Antworten. F# wäre eine Alternative. Da dies aber ebenfalls auf dem CLR-Konzept aufsetzt, sehe ich da keine Performancevorteile.

@Siassei: Meine Anwendung ist extrem zeitkritisch. Das ist eine wichtige a priori Erkenntnis. Daher versuche ich Berechnungen auszulagern, um die Laufzeit zu optimieren.

Es sei aber nochmal betont, dass C# auch auf mein Beispiel bezogen nicht schneller ist. Programmiere ich eine reine FORTRAN-Anwendung, ist diese mit derselben DLL rund zweimal schneller. Der Flaschenhals entsteht beim Aufrufen der DLL innerhalb der C#-Anwendung.

Hier einmal der C#-Code zum Aufrufen der DLL:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using CSharpDllTest;

namespace FortranDllCallTest
{

    class Program
    {
        static void Main(string[] args)
        {
            int[] arr = {1,2,3,4,5,6,7,8,9,10,11};

            int arraySize = arr.Length;


            //************Aufruf der CSharp DLL****************

            PERMUTATIONUD csharpdll = new PERMUTATIONUD();
            csharpdll.PERMUTATIONUDMAIN(arr, arr.Length);

            //************Aufruf der Fortan DLL****************

            FortranDllWrap.FPERMUTATIONUD(arr, ref arraySize);
            //FortranDllWrap.FPERMUTATIONUD();


            Console.ReadKey();
        }

    }

    public class FortranDllWrap
    {

        // CallingConvention.Cdecl must be used since the stack is

        // cleaned up by the caller in Intel Fortran by default

        [DllImport("D:/Algorithmen/Fortran/Testcode/DllTest/DllTest/Debug/DllTest.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void FPERMUTATIONUD(int[] arr, ref int arraySize);

        //public static extern void FPERMUTATIONUD();
 
    }

}

Gelöschter Account
vor 13 Jahren

Da dies aber ebenfalls auf dem CLR-Konzept aufsetzt, sehe ich da keine Performancevorteile.

Da liegst du falsch. Diese Voreingenommenheit haben viele.... benutze die Forumssuche...

U
282 Beiträge seit 2008
vor 13 Jahren

eine Anwendung ist extrem zeitkritisch. Das ist eine wichtige a priori Erkenntnis.

Dann sehe ich eigentlich keinen Weg daran vorbei, alle Wesentlichen algorithmischen Berechnungen komplett in Fortran, C++ oder C zu schreiben. Du wirst mit C# nicht die Performance nativer Anwendungen hinbekommen.

Wir machen bei uns viel Numbercrunching, da ist dann eine native Sprache, am besten noch mit entsprechenden Bibliotheken (IMSL) kaum zu schlagen.

X
1.177 Beiträge seit 2006
vor 13 Jahren

huhu,

wollte noch nen Senf in den Raum werfen: CLR ist nicht "Per Definition langsamer"; F# ist auch nicht C#. und

We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified

Ich würde das so sehen: wenn Deine Anwendung in Fortram geschrieben deinen Anforderungen entspricht, dann behalte Fortram. Wenn Du in c# Vorteile siehst, dann nimm c#.

Anschliessend kannst du immernoch performance-kritische Teile in Fortram implementieren und aufrufen. Aber natürlich sollte der Aufruf nicht den Vorteil auffressen, nur weil die "optimierte" Fortram-Funktion nur aus 3 Zeilen und 2 Integers besteht^^

😃

Xynratron

Herr, schmeiss Hirn vom Himmel - Autsch!

Die Erfahrung zeigt immer wieder, dass viele Probleme sich in Luft auslösen, wenn man sich den nötigen Abstand bzw. Schlaf gönnt.

G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren

Also gut, ich werde höchstwahrscheinlich reinen Fortran-Code schreiben. Dann muss man eben die Schnittstelle zur Hauptanwendung an einer anderen Stelle geschaffen werden.

Aber noch einmal die konkrete Frage: Warum genau läuft der native Code langsamer, wenn eine .NET-Anwendung diese Bibliothek aufruft?

F
10.010 Beiträge seit 2004
vor 13 Jahren

Der native code läuft nicht langsamer.

Aber beim Aufruf muss .NET das ganze drum herum machen, also Stack aufbauen, ggf Parameter Konvertieren usw.
Und das dauert halt so seine zeit.

Deshalb lohnt auch das erstellen kleiner Routinen nicht, sondern der gesamte Algorithmus muss dann nativ sein, damit die sprünge nicht die zeit verballern.

G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren

Auch pinvokes werden gejittet. Hast du den ersten aufruf mit dem zweiten verglichen?

Eine etwas späte Reaktion auf diesen Beitrag. Wieso P-Invoke? Ich verwende doch "using System.Runtime.InteropServices;", also COM Interop?!

EDIT: Diese Frage hat sich erledigt!

G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren

Der native code läuft nicht langsamer.

Wird da nicht grundsätzlich noch der Fortran-Maschinencode in MSIL übersetzt, die dann wiederum gejittet wird?!

X
1.177 Beiträge seit 2006
vor 13 Jahren

nein, der kompilierte Fortram-Code wird nicht nochmal übersetzt. Sonst würden ja auch Systemaufrufe (z.B. für Windows-Messages, Paint-Aufforderung etc.) immer decompiliert.

😃

Xynratron

Herr, schmeiss Hirn vom Himmel - Autsch!

Die Erfahrung zeigt immer wieder, dass viele Probleme sich in Luft auslösen, wenn man sich den nötigen Abstand bzw. Schlaf gönnt.

G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren

nein, der kompilierte Fortram-Code wird nicht nochmal übersetzt. Sonst würden ja auch Systemaufrufe (z.B. für Windows-Messages, Paint-Aufforderung etc.) immer decompiliert.

Gut, aber warum läuft die FORTAN-DLL dann so viel langsamer?

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo gaussmath,

Gut, aber warum läuft die FORTAN-DLL dann so viel langsamer?

Wurde schon beantwortet:

Deshalb lohnt auch das erstellen kleiner Routinen nicht, sondern der gesamte Algorithmus muss dann nativ sein, damit die sprünge nicht die zeit verballern.

Wenn das nicht der Grund ist, hast du vermutlich falsch gemessen.

herbivore

G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren

Welche Sprünge sollen das sein? Hat jemand vielleicht irgendwelche Referenzen von Microsoft selbst, wo ich etwas über den Performanceverlust beim Aufrufen von nativem Code nachlesen kann?

Was bedeutet dann, dass P-Invokes gejittet werden?

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo gaussmath,

so schwer ist es doch nicht. Nativer Code wird direkt vom Prozessor ausgeführt. Wenn du eine Funktion in nativem Code hast, die die gesamte Berechnung durchführst, ist es vollkommen egal, ob du diese Funktion aus einem nativen Programm oder eine managed Programm heraus aufrufst. Der einzige Overhead der bei einem managed Programm entstehen kann, ist der für die einmalige Initialisierung der (anschließend gar nicht mehr benötigten) Laufzeitumgebung beim Start. Wenn du zu anderen Ergebnissen kommst, dann behaupte ich, hast du falsch gemessen oder anderen Fehler in deinem Versuchsaufbau (z.B. dass du die Fortran-DLL mit anderen Optionen/Optimierungen übersetzt hast als die Fortran-EXE).

Ein Unterschied in der Performance kommt möglicherweise dann ins Spiel, wenn es statt einer großen viele kleine Funktionen gibt, die immer nur einen kleinen Teil der Berechnung vornehmen und man diese Funktionen alle aus einem managed Programm heraus aufruft. Dann kommt eben der Overhead der Funktions_aufrufe_ dazu. Die Ausführung der Funktion läuft dann wieder nativ.

Davon abgesehen bedeutet managed nicht zwangsläufig langsam. Es gibt Fälle, in denen managed Code wegen der Möglichkeit zur Optimierung zur Laufzeit schneller läuft als statisch optimierter nativer Code.

Aber ich finde, das versteht sich alles von selbst. Können wir das Thema damit bitte abschließen?

herbivore

1.361 Beiträge seit 2007
vor 13 Jahren

Hat jemand vielleicht irgendwelche Referenzen von Microsoft selbst, wo ich etwas über den Performanceverlust beim Aufrufen von nativem Code nachlesen kann?

Beispielsweise bei An Overview of Managed/Unmanaged Code Interoperability oder Improving Interop Performance.

Die Texte beziehen sich zwar teilweise noch auf ältere CLRs, aber das Prinzip ist gleich, und dort steht:

Zitat von: msdn
Approximate overhead for a platform invoke call: 10 machine instructions (on an x86 processor)

bzw. wird durch das Schaubild Calling a Flat API: Step by Step das gut erläutert.

Wie aber FZelle und herbivore angesprochen haben, diesen konstanten Overhead hast du bei jedem Interop-Call. Alles dahinter läuft nativ in der DLL ab. Daher so wenig Interop-Calls wie möglich und nur große Funktionsblöcke auslagern.

beste Grüße
zommi

G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren

Hallo herbivore,

ich fühle mich ein wenig gescholten, aber nichts für ungut...

So einfach und klar ist die Sache nicht, weil

  1. ich die Fortran.exe und Fortran-DLL (so wie ich sie in der C#-Anwendung aufrufe) unter denselben Bedingungen bzw. mit denselben Einstellungen kompiliert habe.

  2. ich die DLL so konzipiert habe, dass diese nur EINMAL aufgerufen wird, so dass Overhead durch zu häufige Aufrufe ausgeschlossen werden kann. Der Subroutine "permut" wird indes mit O(n!) sehr häufig aufgerufen, das allerdings innerhalb des nativen Codes. Wie ich gemessen habe, wird doch aus meinen Codebeispielen heraus klar.

Mir ist unterm Strich noch überhaupt nicht klar, woher diese Laufzeitunterschiede kommen.

Grüße, gaussmath

1.361 Beiträge seit 2007
vor 13 Jahren

Hallo gaussmath,

sehe ich das richtig, dass du zum einen eine Fortran-DLL für die nutzung aus C# heraus erzeugt hast, aber für den puren Fortran-Test, diese DLL überhaupt nicht nutzt, sondern den Fortran Code direkt in eine EXE-kompilierst ?

In diesem Unterschied könnte sich ja alles begründen. Vielleicht wird beim Fortran Kompilieren in eine Exe etwas anderes getan, als beim Kompilieren in eine DLL. Du sagtest, du hättest es mit den selben Einstellungen getan... mh....

3 Dinge würde ich analysieren:*Schreibe ein C programm, was die Fortran-Dll nutzt und miss erneut. (das sollte in etwa genausolang dauern, wie dein C# program, denn C# verändert die Dll genausowenig wie C) *Schreibe ein Fortran-Programm was ebenfalls die Fortrag-DLL nutzt (und nicht den SourceCode direkt reinkompiliert bekommt) *Recherchiere, was beim Fortran-Export-C-DLL-Kompilieren alles passiert. Wie gibts du an, welche Methoden "exportiert" werden? Hast du da fälschlicherweise alle angegeben? Was sagt das Tool DependencyWalker, welche Funtktionen von der DLL exportiert werden. Werden manche deiner Compiler-Optionen beim DLL-Kompilieren nicht beachtet?

beste Grüße
zommi

G
gaussmath Themenstarter:in
45 Beiträge seit 2010
vor 13 Jahren

Ja, ihr habt recht, es muss am Compiler liegen. Jetzt heißt es, die Optionen zu checken...

Edit: In der Tat, es lag an den Compilereinstellungen!!! Damit hat sich das Thema erledigt. Ich bedanke mich für die rege Beteiligung. 😃