[EDIT] Abgetrennt aus Was wünscht ihr euch für C# 5?[/EDIT]
Schön wäre auch wenn C# bzw. .NET endlich mal in der Geschwindigkeit mit nativen Anwendungen konkurrieren könnte.
Hast du dazu einen aktuellen Benchmark?
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hast du dazu einen aktuellen Benchmark?
Das ist zwar kein richtiger benchmark, aber schon erstaunlich:
Ein bekannter hat mal eine funktion, die einen float mit 3 multipliziert in c++ geschrieben und über pinvoke importiert.
In etwa so sah der Test aus.
float pi=Math.Pi;
maldrei(0);//erste Aufrufe sind langsam
Stopwatch sw=new Stopwatch();
sw.Start();
sw.Stop();
sw.Reset();
{
sw.Start();
float result=3*pi;
sw.Stop();
//ausgabe
sw.Reset();
}
{
sw.Start();
float result=maldrei(pi);
sw.Stop();
//ausgabe
sw.Reset();
}
Ergebnis: Die von c++ importierte funktion hat grundsätzlich ca. 12% weniger Zeit gebraucht! (außer beim ersten aufruf)
Wenn es tatsächlich dieser Test war kann ich das nicht glauben. Der JITer würde erkenne dass 3 * pi konstant ist und es somit als Konstante ersetzen. Der C++-Aufrug muss jedoch durchgeführt werden. Somit muss irgendein Nebeneffekt vorhanden gewesen sein.
Muss mal schauen wenn ich Zeit werde ich mal einen Benchmark bezüglich numerischer Probleme mit C# vs. Fortran 95 anstellen. Schauen wie weit C# an den "Klassenprimus" herankommt.
Edit: offtopic (grau entfernt).
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Ich hatte ihm auch folgendes empfohlen, aber er meinte, es macht keinerlei Unterschied:
object refPi=(float)Math.Pi;
Gc.KeepAlive(refPi);
float pi=(float)refPi;
Ich glaube aber, er hatte die beiden klammern in anderer Reigenfolge. (erst c++ und dann c# test)
Muss mal schauen wenn ich Zeit werde ich mal einen Benchmark bezüglich numerischer Probleme mit C# vs. Fortran 95 anstellen.
Hab mal einen Vergleich von Fortran 95 (Compiler von Lahey Fujits v7.2) mit C# (.net 3.5 SP1) angestellt.
Verglichen wurde:*Matrix-Multiplikation 1000x1000 * 1000x1000, Datentyp double: In Fortran wurde der sprachintegrierte Befehl verwendet und in C# eine von mir optimierte Variante der Matitzenmultiplikation (mit C# gehts nicht mehr schneller 😁 **). In C# wurde ebenfalls eine paralle Variante der Matrixmultiplikation getestet. Für Fortran war dies nicht möglich da ich dafür keinen Compiler habe (diese sind sauteuer).
*N-dimensionale Monte-Carlo Integration von exp(-t^2/2) über [0,1] x [0,1] x ... x [0,1] wobei für die Dimension N := 10 gewählt wurde.
Im angehängten Bild der Vergleich. Die Ergebnisse wurden jeweil auf die schneller Sprache (Fortran) normiert.
Dass der Unterschied so extrem (C# 2,5...4x langsamer) ist hätte ich mir nicht gedacht.
Man darf dabei nicht vergessen dass das nur ein Test für eine isolierte Berechnung ist. In der Realität kommt noch mehr hinzu: Eingabe - Verarbeitung - Ausgabe. Hier wurde nur die Verarbeitung (vorhandener Daten) getestet. Ich glaube dass über den Gesamtzyklus C# die Nase vorn hat.
Als ich den Benchmark-Code von Fortran nach C# übersetzt habe vermisste ich Fortrans Tensor-Operatoren ziemlich. Zur Verdeutlichung:
In Fortran ist für die Initialisierung einer Matrix ein Befehl notwendig: matrix = 1
. In C# müssen beide Schleifen explizit programmiert werden. Wenn ich mich nicht täusche hab ich mir das schon gewünscht 😉
Edit: Test mit NGen hinzugefügt.
Edit: offtopic (grau entfernt).
mfG Gü
** PS - Edit: ich hob doch noch eine (leicht) schnellere Variante (für x64) gefunden und zwar indem eine Matrix transponiert wird und somit die Elemente als Skalarprodukt (Achtung auf Indizes) berechnet werden kann. Dies ermöglicht dann auch noch schnellere SIMD-Wege.
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Lass das mal auf der GPU laufen. Kann man in C# machen und ist, bei einer großen Matrix, bestimmt um einiges schneller(meine GPU schafft 566Gigaflops). Es gibt einn Microsoft Research Projekt Accelerator. Ist einfach zu verwenden und genau für sowas gedacht. 8)
Gruß pdelvo
Ich weiß. Accelarator (bzw. jede GPU) unterstützt (momentan) nur floats und für die Numerik sind nun mal doubles eher der Fall.
Außerdem wäre dann der Vergleich der Sprachen nicht mehr gegeben. Auf die GPU könnte ich auch mit Fortran zugreifen (wenns mit C geht gehts auch mit Fortran). Weiters könnte Fortran noch parallel getestet werden (da sollte die Zeit nochmals auf die 1/2. sinken) - wäre aber immer noch langsamer als die GPU. Abe das hat nichts mit mehr mit der Sprache zu tun. Der nächste Schritt wäre den Vergleich auf einem Hochleistungcluster durchzuführen 😉
Edit: offtopic (grau entfernt).
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hallo gfoidl,
wie oft hast du gemessen?
Hoffentlich ohne Debugger?
NGEN?!?
Gemessen wurde so:
Natürlich ohne Debugger - sonst macht ja keinen Sinn 😉1.Durchlauf ohne Zeitmessung (für laden aller Teile sowie JITen)
1.5 Durchläufe mit Zeitmessung und vor jedem Durchlauf eine GC durchgeführt (nur bei der C#-Variante)
1.Die Zeit der 5 Durchläufe wurde gemittelt.
NGen hab ich nicht getestet - glaube aber nicht dass das einen Vorteil bringt denn nach dem "Leerdurchlauf" liegt bei der C#-Varainte auch Maschinencode vor. NGen bringt AFAIK nur beim Startverhalten wesentlich was und das wurde in der Messung ja nicht berücksichtigt.
Weiters hab ich den Test auf 2 Rechnern durchgeführt (1x DuoCore, 1X SingleCore) und das Ergebnis für den jeweiligen Benchmark von dem Rechner genommen für den die insgesamt bessere Zeit erziehlt wurde (war bei der Matrixmultiplikation der SingleCore - obwohl ich glaube das ist deshalb weil ich auf diesem Rechner sonst nichts laufen habe und somit alle Ressourcen verwendet werden können - während beim DualCore-Rechner hab ich ein paar Instanzen von VS offen,....).[/color]
Edit: Ausgegraut für offtopic.
Edit: offtopic (grau entfernt).
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Die Zeit der 5 Durchläufe wurde gemittelt.
Wieso gemittelt?
IMHO wäre es sinnvoller, den besten zu verwenden.
NGen hab ich nicht getestet - glaube aber nicht dass das einen Vorteil bringt denn nach dem "Leerdurchlauf" liegt bei der C#-Varainte auch Maschinencode vor.
Nein, zumindest ab Version 3.5 soll NGEN anscheinend weitere Optimierungen vornehmen.
//EDIT:
Achso: 32bit oder 64bit?
Wieso gemittelt? IMHO wäre es sinnvoller, den besten zu verwenden.
Wenn schon müsste der max/min/avg und die Standardabweichung angegeben werden um ein statischtische Repräsentation der Daten zu erhalten. Mir geht es aber eher um den qualitativen Vergleich und dazu reicht der Mittelwert. Bei den Unterschieden wird es kaum etwas ausmachen ob der min- oder avg-Wert genommen wird.
Nein, zumindest ab Version 3.5 soll NGEN anscheinend weitere Optimierungen vornehmen.
Hab das Diagramm aktualisert. NGen bringt nur minimal was - kann also fast vernachlässigt werden.
Achso: 32bit oder 64bit?
32 bit - ich hab keine Fortran 64 bit Compiler (und auch keinen 64 bit Rechner). Das Verhalten wird sich jedoch kaum ändern wenn sowohl Fortran als auch C# auf 64 bit läuft.
Edit: offtopic (grau entfernt).
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hmm - verdammt: Mir fällt nichts mehr ein, was du falsch gemacht haben könntest 😁
Die Unterschiede sind tatsächlich mehr als deutlich.
Interessant wäre ein Vergleich mit C++ - Fortran scheint ja sehr optimiert für numerische Berechnungen zu sein.
Mir fällt nichts mehr ein, was du falsch gemacht haben könntest
Ich glaub da gibts auch nicht viel. Der Code beider Varianten wurde auch manuell optimiert. Bei C# wurde vor allem auf die Range-Check-Elimination geachtet (die bringt sehr viel - dadurch kann die Schleife bis 20x schneller werden!).
Die Unterschiede sind tatsächlich mehr als deutlich.
Dass es so gravierend ist hätte ich mir auch nicht gedacht.
Da merkt man den Unterschied zwischen statischer Kompilierung bei der mehr Möglichkeiten zur Optimierung (Algebraic simplifications, Transform array element to simple variable, Local Instruction scheduling, Address calculation optimization, Array optimization, Loop unrolling/interchanging, Inlining mathematical functions, Stack optimization - um nur ein paar Schlagworte zu nenen) gegenüber dynamischer Kompilierung mit dem JITer der für die Optimierung ein recht simple Heuristik verwendet.
Warum die NGen-Variante ebenfalls relativ schlecht abschneidet wundert mich. Da könnten doch die oben genannten Optimierungen durchgeführt werden.
Interessant wäre ein Vergleich mit C++ - Fortran scheint ja sehr optimiert für numerische Berechnungen zu sein.
Fortran steht ja für "Formula translation". Die Sprache kann relativ wenig dafür dies aber sauschnell - und das sind vor allem Tensoroperationen die zum Großteil Sprachbestandteil sind.
C++ kann ich nicht und daher kann ich auch keinen Vergleich geben. Ich kann nur soviel dazu sagen dass die meisten numerischen Programme in Fortran geschrieben sind bzw. mit Fortran erstellt Libraries verwenden. Wenn du Vergleiche im Internet findest muss darauf geachtet werden ob der Test mit den Sprachmitteln oder eine Numerik-Libraray durchgeführt wurde. Es wird nämlich gerne Standard-Fortran mit einer optimierten C-Library verglichen. Das gefinkelte dabei ist dass diese C-Libraries oft eine in Assembler optimierte Fortran-Library verwenden (das wird im Test aber nicht erwähnt damit C++ besser da steht).
Diese optimierten Fortran-Libraries sind geschwindigkeitsmäßig der Hammer. Ich hab mal meinen in Fortran geschrieben Code zur Lösung von linearen Gleichungssystemen (ich glaube ich hab die Gauß-Seidel-Iteration verwendet) gegen dieselbe Variante aus den Scientific Subroutine Library antreten lassen und wurde kläglich geschlagen (ich glaube es war so um Faktor 20).
Eine Interessante Anmerkung zu C++ vs. Fortran gibt: Fortran 90 for Science Students
Dennoch - wie bereits vorhin mal erwähnt - hat C# einige Vorteile und ist nicht so langsam wenn der "Betrachtungsradius" vergrößert wird.
Geht es darum Daten zu besorgen (zB von einer Datenbank) und diese danach grafisch darzustellen liegt C# klar im Vorteil. Wenn somit der gesamte Zyklus (in einer realen Anwendung) geprüft wird kann ich mir gut vorstellen dass C# "schneller" ist. Das ist auch der Grund warum ich vor ca. 1,5 Jahren von Fortran zu C# gewechselt bin.
Vom eigentlichen Thema sind wird ziemlich weit abgerutscht. Vielleicht kann ein Moderator ein eigenes Thema daraus machen.
Edit: offtopic (grau entfernt) - Dank an den Moderator.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Vom eigentlichen Thema sind wird ziemlich weit abgerutscht. Vielleicht kann ein Moderator ein eigenes Thema daraus machen.
Ja, ich glaube das wäre sinnvoll.
Da fällt mir ein: Gibt es nicht auch einen Fortrancompiler, der IL-Code erzeugt?
Zum Vergleich mit C++:
Kannst du eventuell deinen C# mal hier posten? Dann findet sich sicherlich jemand, der den nach C++ übersetzen kann - bei rein mathematischem Code sollte das ja nicht so riesige Unterschiede machen.
Dann würde man nämlich mal sehen, wie viel sich - alleine durch Compileroptimierungen - bei diesem speziellen Algorithmus sich noch "aus C# rausholen" lassen würde.
Evtl. hat hier ja auch irgendwo jemand einen Intel-C++-Compiler rumliegen; der soll ja nochmal besser als der MS Compiler optimieren (stand zumindest mal in der c't).
Gibt es nicht auch einen Fortrancompiler, der IL-Code erzeugt?
Ja die gibt es. Die Geschwindigkeitsvorteile sind dann aber weg denn der JITer ist der "kritische" Compiler und der ist dann derselbe.
Was ich beim Test (aus Faulheit) nicht berücksichtigt habe ist dass der Fortran-Teil auch noch Konsolenausgaben durchführt. Hab den Test nochmals gemacht
Kannst du eventuell deinen C# mal hier posten?
Ja kann ich. Wobei ich noch anmerken will dass ich eine ganze Sammlung von Fortran-Benchmarks habe und aus denen hab ich den Klassiker (Matritzenmultiplikation) und den kürzesten genommen (Monte-Carlo-Integration). Alle anderen waren mir zu aufwändig um diese zu übersetzen (2000+ Zeilen 😦).
Klassendiagramm angehängt - Achtung die Schnittstelle beeinflusst die Messung nicht!
namespace Benchmark
{
public interface IBenchmark
{
void Run();
long Milliseconds { get; }
string Name { get; }
}
}
namespace Benchmark
{
class Program
{
static void Main(string[] args)
{
List<IBenchmark> benchmarks = new List<IBenchmark>
{
new Matmul(),
new MonteCarlo()
};
foreach (IBenchmark benchmark in benchmarks)
{
GC.Collect();
GC.WaitForPendingFinalizers();
benchmark.Run();
Console.WriteLine(
"{0}\t{1}",
benchmark.Name,
benchmark.Milliseconds);
}
Console.ReadKey();
}
}
}
Matritzenmultiplikation
Die Elemente der Matrix werden im Speicher sequentiell angeordnet. Dabei gibt es zwei Möglichkeiten für die Speicherung:*splatenweise (Spaltenvektoren)
*zeilenweise (Zeilenvektoren)
In Fortran und verwandten Sprache wie Matlab wird die Matrix in Form von Spaltenvektoren gespeichert während C-artige Sprachen Zeilenvektoren speichern. C# speichert eine Matrix die mit T[,] erstellt wird ebenfalls zeilenweise. Diese Variante der rectangular Arrays ist jedoch langsamer als die Variante mit jagged Arrays. Der Grund liegt darin dass es eigene IL-Anweisungen für zero-based one dimensional Arrays gibt und diese effizient* umgesetzt werden. Beim rectangular Array erfolgt der Zugriff auf die Elemente mit Methoden die ein object zurückgeben. Daher kommt das Castingproblem eventuell auch noch dazu.
Spaltenvektor oder Zeilenvektor?
Ich halte mich dabei an die Herangehesnweise von Fortran nämlich die spaltenweise Speicherung. Warum? Zum einem kann eine Matrix verallgemeinert als Tensor 2. Stufe betrachtet werden. Ein Tensor 0. Stufe wäre ein Skalar (also eine Zahl) und ein Tensor 1. Stufe ein (Spalten-) Vektor. Somit halte ich mich an den Tensor-Begriff. Der Tensor 1. Stufe ist bewusst nicht als Zeilenvektor defniert denn in der Mathematik werden Spaltenvektoren statistisch gesehen häufiger benötigt als Zeilenvektoren - aber das ist eine andere Geschichte.
Nun ist es mit jagged Arrays möglich eine Matrix ebenfalls spaltenweise zu speichern. Dabei ändert sich nur die Reihenfolge der Indizes - dies kann jedoch über einen Indexer der die Position berechnet nach außen hin wurscht sein. Für die meisten Problem könnte das auch egal sein denn jede Matrix-Operation kann von zeilen- auf spaltenweise umgeschrieben werden. Bei komplizierten Formeln wie sie zB in der Simulation von kompressiblen Medien auftritt kann das aber zu einem ziemlichen Unterfange ausarten. Daher ist es praktisch die gegeben Formeln effizient zu Verwenden indem eine Matrix Spaltenweise gespeichert wird.
Ich hab mal getestes ob bei der Matrixmultiplikaton ein Unterschied zwischen Zeilen-/Spaltenvektor-Variante besteht und die Speicherung mit Spaltenvektor erwies sich auch hier als performanter.
* ist im Vergleich zu Fortran relativ 😉
Für den Test werden zwei 1000x1000 Matritzen (die mit zufälligen Werten gefüllt sind) multipliziert.
using System;
using System.Diagnostics;
using gfoidl.Parallelization; // hab das TPL nicht verwendet sondern meine eigene denn die ist (bisher) schneller
namespace Benchmark
{
/// <summary>
/// Matmul 1000x1000 * 1000x1000.
/// </summary>
public class Matmul : IBenchmark
{
const int N = 1000;
private double[][] _a, _b, _c;
//---------------------------------------------------------------------
#region IBenchmark Member
public void Run()
{
CreateMatrices();
// JITen:
DoMatmul(_a, _b, _c);
// Test:
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 5; i++)
DoMatmul(_a, _b, _c);
sw.Stop();
_milliSeconds = (long)(sw.ElapsedMilliseconds * 0.2);
}
//---------------------------------------------------------------------
private long _milliSeconds;
public long Milliseconds
{
get { return _milliSeconds; }
}
//---------------------------------------------------------------------
public string Name
{
get { return "Matmul"; }
}
#endregion
//---------------------------------------------------------------------
private void CreateMatrices()
{
_a = new double[N][];
_b = new double[N][];
_c = new double[N][];
Random rnd = new Random();
for (int j = 0; j < N; j++)
{
_a[j] = new double[N];
_b[j] = new double[N];
_c[j] = new double[N];
for (int i = 0; i < N; i++)
{
_a[j][i] = rnd.NextDouble();
_b[j][i] = rnd.NextDouble();
}
}
}
//---------------------------------------------------------------------
private void DoMatmul(double[][] a, double[][] b, double[][] c)
{
for (int j = 0; j < b.Length; j++)
{
//Parallel.For(0, b.Length, j =>
//{
// Cachen damit die Speicherzugriffe optimiert werden können
// und der Compiler Range-Check-Eliminatin anwenden kann.
// Das bringt einen großen Performance-Vorteil (v.a. im
// Vergleich zur klassischen Variante (unten)).
double[] bColj = b[j];
double[] cColj = c[j];
for (int k = 0; k < a.Length; k++)
{
// Cachen:
double[] aColk = a[k];
double b_kj = bColj[k];
for (int i = 0; i < aColk.Length; i++)
cColj[i] += aColk[i] * b_kj;
}
}
//});
// Klassische Implementierung der Matrixmultiplikation:
//for (int i = 0; i < a.Length; i++)
// for (int j = 0; j < b.Length; j++)
// {
// double tmp = 0;
// for (int k = 0; k < a.Length; k++)
// tmp += a[i][k] * b[k][j];
// c[i][j] = tmp;
// }
}
}
}
Range-Check-Elimination (RCE) wird vom Compiler dann angewandt wenn das Array im lokalen Stack der Methode ist denn somit kann der Compiler verfolgen wie/ob das Array geändert wird und dementsprechend muss nicht bei jedem Zugriff eine Prüfung der Indexgrenzen durchgeführt werden. Somit kann mit RCE der Zugriff der Elemente in einer Schleife laut meinen Untersuchungen doppelt so schnell erfolgen. Das fällt allerdings weniger ins Gewicht wenn der Schleifenrumpf aufwändig ist.
Zum Vergleich der verwendete Fortran-Code (man beachte wie wenig Code für die Erstellung und Multiplikation der Matritzen notwendig ist):
program matrix
implicit none
real(8), allocatable :: a(:,:), b(:,:), c(:,:)
integer :: N = 1000, i
real :: t0, t1
allocate( a(N,N), b(N,N), c(N,N) )
call random_seed()
call random_number(a)
call random_number(b)
call cpu_time(t0)
do i = 1, 5
c = matmul(a,b)
end do
call cpu_time(t1)
write(*,'(a, f10.3)') ' Time for matmul = ', (t1 - t0) / 5
end program matrix
Monte-Carlo Integration
Es wird eine N-dimensionale Integration der Funktion f(x) = exp(-x^2/2) in den Grenzen [0,1] x [0,1] x ... x [0,1] durchgeführt. N := 10.
Da ich mir den Test nicht ausgedacht habe und die Originalfassung in Fortran eine Lizenzinformation enthält kommt diesmal der Fortran-Code zuerst. In der Originalfassung sind sogar noch die Konsolenausgaben enthalten!
program monte_carlo_integration
!
! peform an N dimensional integration of exp(-t**2/2)
! over the square [0,1] x [0,1] x ... x [0,1]
! for N dimensions using a Monte Carlo scheme.
!
!
!=========================================================================
!
! This code is part of the Quetzal Computational Associates Fortran 90
! Benchmark Suite.
!
! Copyright (c) 1994 by Quetzal Computational Associates.
!
! Permission to copy and use without fee all or part of the
! publications or computer codes in this suite is granted provided
! that copies are not made or distributed for direct commercial
! advantage, this copyright notice and the title of the codes
! or publications and the fact that they originated in this
! benchmark suite appear, and notice is given that copying is
! by permission of Quetzal Computational Associates. For
! publications that have been published in journals, the name
! of the publication and date should appear. To copy otherwise,
! or to republish, requires a fee and/or specific permission.
! Authors of codes in this benchmark suite are exempt from the terms of
! this copyright notice for their codes only. Address any
! questions about this copyright notice to:
!
! John K. Prentice
! Quetzal Computational Associates
! 3200 Carlisle N.E.
! Albuquerque, NM 87110-1664
! USA
!
! Phone: 505-889-4543
! Fax: 505-889-4598
! E-mail: [EMAIL]quetzal@aip.org[/EMAIL]
!
!=========================================================================
!
implicit none
integer, parameter :: problem_dimension = 10
integer, parameter :: number_of_trys = 6
!
integer :: k, number_of_integration_points
real :: integral, exact_answer
real, dimension(:,:), allocatable :: integration_points
real, dimension(:), allocatable :: argument
real :: exact_answer_in_1d = 0.85562439
integer, dimension(number_of_trys) :: try_size
real :: t0, t1
!
data try_size/9000, 100, 60000, 423, 100000, 2533/
call cpu_time(t0)
!
! loop over different trys
!
do k = 1, number_of_trys
number_of_integration_points = try_size(k)
!
! allocate memory
!
allocate (integration_points(number_of_integration_points,problem_dimension))
allocate (argument(number_of_integration_points))
!
! randomly calculate integration points
!
call gleich
!
! do Monte Carlo integration
!
argument = sum(0.5*integration_points**2,dim=2)
integral = sum(exp(-argument)) / real(number_of_integration_points)
!
exact_answer = exact_answer_in_1d**problem_dimension
print *, ' '
print *, 'n = ', problem_dimension, ' number of points = ', &
& number_of_integration_points
print *, 'exact answer = ', exact_answer
print *, 'Monte Carlo answer = ', integral
!
deallocate (integration_points)
deallocate (argument)
!
end do
call cpu_time(t1)
write(*,'(a, f10.3)') ' Time for montecarlo = ', t1 - t0
!
contains
!
subroutine gleich
!
! this routine by t. t. warnock, cray research analyist at lanl
! F77 version written about 1980
!
!
implicit none
!
real, dimension(number_of_integration_points,problem_dimension) :: bottom, top, radish_value
integer, dimension(number_of_integration_points,problem_dimension) :: radix_value, spin_value, &
& quo, errors
integer :: kount
!
integer, dimension(25) :: radix, spin
real, dimension(25) :: radish
!
data radix/2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,&
& 61, 67, 71, 73, 79, 83, 89, 97/
data radish/2., 3., 5., 7., 11., 13., 17., 19., 23., 29., 31., 37., 41., &
& 43., 47., 53., 59., 61., 67., 71., 73., 79., 83., 89., 97./
data spin/1, 2, 1, 5, 3, 8, 2, 7, 18, 11, 18, 3, 17, 24, 40, 15, 40, 49, &
& 12, 30, 40, 70, 9, 39, 82/
!
top = 0.
bottom = 1.
do kount = 1,number_of_integration_points
radish_value(kount,1:problem_dimension) = radish(1:problem_dimension)
spin_value(kount,1:problem_dimension) = spin(1:problem_dimension)
radix_value(kount,1:problem_dimension) = radix(1:problem_dimension)
quo(kount,1:problem_dimension) = kount
enddo
!
10 where (quo /= 0)
top = top * radish_value + mod(quo * spin_value, radix_value)
bottom = bottom * radish_value
quo = quo / radix_value
errors = 1
elsewhere
errors = 0
end where
if (sum(errors) > 0) go to 10
!
integration_points = top/bottom
!
end subroutine gleich
!
end program monte_carlo_integration
Mein Portierung dessen nach C# schaut wie folgt aus (wiederum beachte man wie wenig Code für manche Operationen in Fortran notwendig ist und in C# geschleift wird). Es wurden wiederum jagged Array verwendet und auf Range-Check-Eliminatin geachtet (um 20% schneller als ohne RCE).
Wegen der Lizenzinfo bei Fortran: Hab ich jetzt die Lizenz für die C#-Variante? 😉
using System;
using System.Diagnostics;
namespace Benchmark
{
/// <summary>
/// N dimension integration of exp(-t^2 / 2) over the square
/// [0,1] x [0,1] x ... x [0,1]
/// for 10 Dimensions using Monte Carlo scheme.
/// </summary>
public class MonteCarlo : IBenchmark
{
private const int PROBLEM_DIMENSION = 10;
private const int NUMBER_OF_TRIES = 6;
private int _numberOfIntegrationPoints;
private double[][] _integrationPoints;
//---------------------------------------------------------------------
#region IBenchmark Member
public void Run()
{
// JITen:
DoMonteCarlo();
// Test:
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 5; i++)
DoMonteCarlo();
sw.Stop();
_milliSeconds = (long)(sw.ElapsedMilliseconds * 0.2);
}
//---------------------------------------------------------------------
private long _milliSeconds;
public long Milliseconds
{
get { return _milliSeconds; }
}
//---------------------------------------------------------------------
public string Name
{
get { return "Monte Carlo"; }
}
#endregion
//---------------------------------------------------------------------
private double DoMonteCarlo()
{
double errorOfIntegral=double.MaxValue;
double integral = 0d;
double exactAnswerIn1d = 0.8562439;
int[] trySize = { 9000, 100, 60000, 423, 100000, 2533 };
for (int k = 0; k < NUMBER_OF_TRIES; k++)
{
_numberOfIntegrationPoints = trySize[k];
double[] argument = new double[_numberOfIntegrationPoints];
_integrationPoints = new double[_numberOfIntegrationPoints][];
// Lokale Kopie für Range-Check-Elimination:
double[][] localIntegrationPoints = _integrationPoints;
for (int i = 0; i < localIntegrationPoints.Length; i++)
localIntegrationPoints[i] = new double[PROBLEM_DIMENSION];
// Integrationspunkte berechnen:
Gleich();
for (int j = 0; j < argument.Length; j++)
{
double[] integrationPointsColj = localIntegrationPoints[j];
for (int i = 0; i < integrationPointsColj.Length; i++)
argument[j] +=
0.5 * integrationPointsColj[i] * integrationPointsColj[i];
integral += Math.Exp(
-argument[j]) /
_numberOfIntegrationPoints;
}
double exactAnswert = Math.Pow(exactAnswerIn1d, PROBLEM_DIMENSION);
errorOfIntegral = exactAnswert-integral;
}
// Verhindert dass der Compiler die Variablen wegoptimiert:
return errorOfIntegral;
}
//---------------------------------------------------------------------
private void Gleich()
{
// Range-Check-Elimination beachten!
double[][] bottom = new double[_numberOfIntegrationPoints][];
for (int i = 0; i < bottom.Length; i++)
{
bottom[i] = new double[PROBLEM_DIMENSION];
double[] local = bottom[i];
for (int j = 0; j < local.Length; j++)
local[j] = 1;
}
double[][] top = new double[_numberOfIntegrationPoints][];
for (int i = 0; i < top.Length; i++)
top[i] = new double[PROBLEM_DIMENSION];
double[][] radishValue = new double[_numberOfIntegrationPoints][];
for (int i = 0; i < radishValue.Length; i++)
radishValue[i] = new double[PROBLEM_DIMENSION];
int[][] radixValue = new int[_numberOfIntegrationPoints][];
for (int i = 0; i < radixValue.Length; i++)
radixValue[i] = new int[PROBLEM_DIMENSION];
int[][] spinValue = new int[_numberOfIntegrationPoints][];
for (int i = 0; i < spinValue.Length; i++)
spinValue[i] = new int[PROBLEM_DIMENSION];
int[][] quo = new int[_numberOfIntegrationPoints][];
for (int i = 0; i < quo.Length; i++)
quo[i] = new int[PROBLEM_DIMENSION];
int[][] errors = new int[_numberOfIntegrationPoints][];
for (int i = 0; i < errors.Length; i++)
errors[i] = new int[PROBLEM_DIMENSION];
int[] radix = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 };
int[] spin = { 1, 2, 1, 5, 3, 8, 2, 7, 18, 11, 18, 3, 17, 24, 40, 15, 40, 49, 12, 30, 40, 70, 9, 39, 82 };
double[] radish = { 2d, 3d, 5d, 7d, 11d, 13d, 17d, 19d, 23d, 29d, 31d, 37d, 41d, 43d, 47d, 53d, 59d, 61d, 67d, 71d, 73d, 79d, 83d, 89d, 97d };
for (int kount = 0; kount < _numberOfIntegrationPoints; kount++)
for (int i = 0; i < PROBLEM_DIMENSION; i++)
{
radishValue[kount][i] = radish[i];
spinValue[kount][i] = spin[i];
radixValue[kount][i] = radix[i];
quo[kount][i] = kount;
}
int sumErrors = 0;
do
{
sumErrors = 0;
for (int j = 0; j < errors.Length; j++)
{
int[] errorsColj = errors[j];
int[] quoColj = quo[j];
for (int i = 0; i < errorsColj.Length; i++)
{
if (quoColj[i] != 0)
{
top[j][i] = top[j][i] * radishValue[j][i] + (quo[j][i] * spinValue[j][i] % radixValue[j][i]);
bottom[j][i] *= radishValue[j][i];
quoColj[i] /= radixValue[j][i];
errorsColj[i] = 1;
}
else
errorsColj[i] = 0;
sumErrors += errorsColj[i];
}
}
} while (sumErrors > 0);
for (int j = 0; j < bottom.Length; j++)
{
double[] topColj = top[j];
double[] bottomColj = bottom[j];
double[] integrationPointsColj = _integrationPoints[j];
for (int i = 0; i < topColj.Length; i++)
integrationPointsColj[i] =
topColj[i] /
bottomColj[i];
}
}
}
}
Bin neugierig ob das jemand nach C++ übersetzt.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Die C#-Variante kann sich ja jeder bei Interesse selst kompiliren. Die Fortran-Varianten Matmul.exe (CRC32: F3F4A745) und MonteCarlo.exe (49D0FB7E) sind als Anhang zu groß (wegen der Optimierung: Loop unrolling und inling) können aber von meinem SkyDrive heruntergeladen werden.
Zur Fortran-Version ist noch anzumerken dass die Standardoptimierung verwendet wurde. Durch Fine-Tuning der Parameter ließe sich da sicher noch was rausholen. Aber was solls: Der alte Großmeister hat den jungen Alleskönner um Ecken geschlagen 😁
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hallo,
rein interessehalber: wie schneidet eigentlich der Fortran-Compiler der Gnu Compiler Collection gegenüber Lahey-Fortran ab? Kommt der annähernd ran (rein qualitativ, unabhängig von dem konkreten Problem)?
Hallo,
ist zwar schon eine Zeit lang her aber ich soweit ich mich erinnern kann übersetzt der Gnu Fortran Compiler denn Quellcode zuerst nach C und kompiliert diesen dann. Qualitativ hab ich beide nie wirklich verglichen weil für mich der G95 (wegen des Zwischenschrittes zu C) kein Fortran-Compiler ist. Vom Bedienkomfort des Lahey-Werkzeugs ganz zu schweigen (VS-Integration, sogar mit Intellisense 😉).
Der obige Vergleich wurde mit Gnu 95 Fortran Compiler - Versionsinfo: G95 (GCC 4.0.3 (g95 0.90!) Jul 27 2006) - wiederholt. Ergebnis angehängt.
Daraus ist ersichtlich dass der Lahey Compiler wirklich schnell ist und sein Geld auch Wert ist. C# schneidet im Vergleich zum Gnu Compiler gar nicht mehr so schlecht ab und ist bei der Monte Carlo Integration dem sogar überlegen.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hallo,
ist zwar schon eine Zeit lang her aber ich soweit ich mich erinnern kann übersetzt der Gnu Fortran Compiler denn Quellcode zuerst nach C und kompiliert diesen dann.
Soweit ich weiß, trifft das auf die aktuellen gfortran und g95 nicht zu.
@gfoidl
Dein Benchmark ist nicht aussagekräftig, denn du vergleichst Äpfel mit Birnen.
Auf der einen Seite verwendest du den sicher hochoptimierten matmul-Befehl von Fortran und auf der anderen Seite eine naive C# Implementierung.
Dein Benchmark ist nicht aussagekräftig, denn du vergleichst Äpfel mit Birnen.
Auf der einen Seite verwendest du den sicher hochoptimierten matmul-Befehl von Fortran und auf der anderen Seite eine naive C# Implementierung.
Das sehe ich nicht so. Es wird die Sprache Fortran mit der Sprache C#/.net verglichen. Warum sollte also der Befehl matmul
, der Sprachbestandteil von Fortran ist, nicht im Vergleich verwendet werden dürfen? 🤔 Das wäre ja fast so als ob bestimmte Datentypen nicht verwendet werden dürfen weil es in einer anderen Sprache kein Äquivalent gibt.
eine naive C# Implementierung.
Naiv würde ich auch nicht unbedingt sagen denn diese Variante ist (wahrscheinlich) die schnellste Matrix-Multiplikation die mit C# machbar ist (Parallelisierung mal außen vorgelassen).
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hi gfoidl,
was mir spontan als Speed-Up einfallen würde ist schonmal die Umkehrun der Array-Lauf-Richtung.
for (int k = 0; k < a.Length; k++)
// wird zu
for (int k = a.Length-1; k >=0 ; k--)
Spart immer eine Instruktion (weil Vergleich mit der 0 ein Befehl ist, während Vergleich mit andere Zahl immer zwei benötigt)
Das macht der C#-Compiler meines Wissens nicht (Der C/C++-Compiler im VS allerdings schon)
Desweiteren könnte man anstatt der "naiven" Implementierung auch den Algorithmus von Strassen verwenden. Das sollte bei 1000x1000 Matrizen auch schneller sein.
Wie die Fortran-Methode intern arbeitet wissen wir ja nicht.
beste Grüße
zommi
Hab in Fortran für die Matrix-Multiplikaton mal den Befehl durch Schleifen (j,i,k) ersetzt. Die Zeit ist immer noch in der selben Größenordnung.
Desweiteren könnte man anstatt der "naiven" Implementierung auch den Algorithmus von Strassen verwenden.
Der Strassen-Algorithmus ist mir sehr wohl bekannt. Der (kleine) Vorteil von O(n2,8 ) gegenüber O(n3) kann in C# durch Verwendung von Parallelisierung mehr als wettgemacht werden. Daher verwende ich den Strassen in der Praxis nicht (mehr).
was mir spontan als Speed-Up einfallen würde ist schonmal die Umkehrun der Array-Lauf-Richtung.
Das ist eine gute Idee die ich sicher testen werden. Allerdings hat der von dir gepostete Code einen Fehler in Hinsicht Optimierung der das alles wieder zunichte machen würde.
for (int k = a.Length-1; k >=0 ; k--)
Durch a.Length -1 führt kann keine Range-Check-Elimination vom JITer durchgeführt werden. Daher muss für jeden Zugriff die Indexgrenzen geprüft werden und ist somit langsamer.
Spart immer eine Instruktion (weil Vergleich mit der 0 ein Befehl ist, während Vergleich mit andere Zahl immer zwei benötigt)
Das macht der C#-Compiler meines Wissens nicht
Auch das werde ich herausfinden 😉
Bis später.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
was mir spontan als Speed-Up einfallen würde ist schonmal die Umkehrun der Array-Lauf-Richtung.
Es war leider nur die Idee gut. Durch das Fehlen der Range-Check-Elimination steigt die Laufzeit der Schleifen wie im folgenden Beispielcode auf ~2x.
using System;
using System.Diagnostics;
namespace Array___Schleifenrichtung
{
class Program
{
static void Main(string[] args)
{
const int N = 100000;
int[] array = new int[N];
Stopwatch sw = new Stopwatch();
// JITen:
AccessForward(array);
AccessBackward(array);
// Test:
sw.Reset();
sw.Start();
AccessForward(array);
sw.Stop();
Console.WriteLine(sw.ElapsedTicks);
sw.Reset();
sw.Start();
AccessBackward(array);
sw.Stop();
Console.WriteLine(sw.ElapsedTicks);
}
//---------------------------------------------------------------------
static void AccessForward(int[] array)
{
// Array ist für die Methode auf dem lokalem Stack daher
// kann Range-Check-Elimination durchgeführt werden.
for (int i = 0; i < array.Length; i++)
array[i] = i;
}
//---------------------------------------------------------------------
static void AccessBackward(int[] array)
{
// Array ist für die Methode auf dem lokalem Stack daher
// kann Range-Check-Elimination durchgeführt werden wenn da
// nicht array.Length - 1 stünde ;)
for (int i = array.Length -1; i >= 0; i--)
array[i] = i;
}
}
}
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Vorwärts- / Rückwertsschleife
Spart immer eine Instruktion (weil Vergleich mit der 0 ein Befehl ist, während Vergleich mit andere Zahl immer zwei benötigt)
Das macht der C#-Compiler meines Wissens nicht (Der C/C++-Compiler im VS allerdings schon)
Mit dieser Aussgage hast du recht.
Hier der Vergleich des geJITeten Codes (Auszug):
Vorwärts-Schleife (leerer Rumpf):
00000000 push ebp
00000001 mov ebp,esp
00000003 xor eax,eax ; Zähler auf 0 setzen
00000005 test ecx,ecx ; Flags des Schleifenende bitweise ANDen
00000007 jle 0000000E ; Wenn test <= 0 ans Ende der Methode springen
00000009 inc eax ; Zähler inkrementieren
0000000a cmp eax,ecx ; Zähler mit dem Schleifenende vergleichen
0000000c jl 00000009 ; Wenn Zähler kleiner Schleifenende -> weitere Iteration
0000000e pop ebp ; Rücksprung aus der Methode
Rückwärts-Schleife
00000000 push ebp
00000001 mov ebp,esp
00000003 dec ecx ; n - 1 durchführen
00000004 mov eax,ecx ; n - 1 der Zählvariable zuweisen
00000006 test eax,eax ; Flags des Zählers bitweise ANDen
00000008 jl 0000000D ; Wenn test? < 0 ans Ende der Methode springen
0000000a dec eax ; Zähler dekrementieren
0000000b jns 0000000A ; Weitere Iteration wenn Zähler >= 0 (no sign).
0000000d pop ebp ; Rücksprung aus der Methode
Für die Forwärtsschleife werden 3 Befehle benötigt:1.Zähler inkrementieren 1.Zähler mit dem Schleifenende vergleichen 1.Wenn der Zähler < Schleifenende ist Sprung zum Beginn des Schleifenrumpfs (jump less)
Bei der Rückwärtsschleife werden 2 Befehle benötigt:1.Zähler dekrementieren 1.Wenn der Zähler positiv Sprung zum Beginn des Schleifenrumpfs (jump no sign)
Range-Check-Elimination (RCE)
[MethodImpl(MethodImplOptions.NoInlining)]
static void ForwardLoop(int[] arr)
{
for (int i = 0; i < arr.Length; i++)
arr[i] = i;
}
[MethodImpl(MethodImplOptions.NoInlining)]
static void BackwardLoop(int[] arr)
{
for (int i = arr.Length - 1; i >= 0; i--)
arr[i] = i;
}
Ergibt als Maschinencode:
Vorwärtsschleife -> Range-Check-Elimination wird angewandt
00000000 push ebp
00000001 mov ebp,esp
00000003 xor edx,edx
00000005 mov eax,dword ptr [ecx+4]
00000008 test eax,eax
0000000a jle 00000015
0000000c mov dword ptr [ecx+edx*4+8],edx
00000010 inc edx
00000011 cmp eax,edx
00000013 jg 0000000C
00000015 pop ebp
00000016 ret
Vorwärtsschleife -> keine Range-Check-Elimination
00000000 push ebp
00000001 mov ebp,esp
00000003 push esi
00000004 mov esi,dword ptr [ecx+4]
00000007 xor eax,eax
00000009 test esi,esi
0000000b jle 0000001D
0000000d mov edx,dword ptr [ecx+4]
00000010 cmp eax,edx
00000012 jae 00000020
00000014 mov dword ptr [ecx+eax*4+8],eax
00000018 inc eax
00000019 cmp eax,esi
0000001b jl 00000010
0000001d pop esi
0000001e pop ebp
0000001f ret
00000020 call 792AC2B4
00000025 int 3
D.h. 12 Befehle bei RCE gegenüber 19 Befehlen mit RCE. Bei großen Schleifen (viele Iterationen) macht das sehr wohl bemerkbar.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hi gfoidl,
mhh...bei mir kommt komplett anderer ASM-Code raus:
[MethodImpl(MethodImplOptions.NoInlining)]
static void ForwardLoop(int[] arr)
{
for (int i = 0; i < arr.Length; i++)
00000000 push ebp
00000001 mov ebp,esp
00000003 sub esp,8
00000006 mov dword ptr [ebp-8],ecx
00000009 cmp dword ptr ds:[00139244h],0
00000010 je 00000017
00000012 call 64379D79
00000017 xor edx,edx
00000019 mov dword ptr [ebp-4],edx
0000001c xor edx,edx
0000001e mov dword ptr [ebp-4],edx
00000021 nop
00000022 jmp 0000003E
arr[i] = i;
00000024 mov eax,dword ptr [ebp-4]
00000027 mov edx,dword ptr [ebp-8]
0000002a cmp eax,dword ptr [edx+4]
0000002d jb 00000034
0000002f call 6437B9B4
00000034 mov ecx,dword ptr [ebp-4]
00000037 mov dword ptr [edx+eax*4+8],ecx
for (int i = 0; i < arr.Length; i++)
0000003b inc dword ptr [ebp-4]
0000003e mov eax,dword ptr [ebp-4]
00000041 mov edx,dword ptr [ebp-8]
00000044 cmp eax,dword ptr [edx+4]
00000047 jl 00000024
}
00000049 nop
0000004a mov esp,ebp
0000004c pop ebp
0000004d ret
viel länger als deiner. Der Laufindex i wird auch stets auf dem Stack gespeichert. Warum nur? Wieso kompiliert deiner besseren Code? Womit kompilierst du?
Ich hab VisualStudio 2008 (SP1) und .Net 3.5 SP1.
beste Grüße
zommi
Hi zommi,
Der Laufindex i wird auch stets auf dem Stack gespeichert. Warum nur? Wieso kompiliert deiner besseren Code? Womit kompilierst du?
Bist du sicher dass dein Maschinencode nicht mit angehängtem Debugger erstellt wird? Wäre dies der Fall so werden vom JITer keine Optimierungen durchgeführt.
Im CLR-Debugger muss bei den Optionen unter Debuggen*Nur meinen Code aktivieren (nur verwaltet) - deaktiviert sein *JIT-Optimierung beim Laden von Module unterdrücken (nur verwaltet) - deaktiviert sein
Womit kompilierst du? Ich hab VisualStudio 2008 (SP1) und .Net 3.5 SP1.
VS 2008 - also der csc.exe der bei .net 3.5 SP1 dabei ist - 32 bit.
Edit: Und natürlich in der Releas-Konfiguration 😉
Edit: Wenn ich den die obigen Optionen aktiviere bekomme ich den selben Code wie du. D.h. dein Code ist deshalb länger weil der JITer keine Optimierungen durchgeführt hat (wegen Debugger).
mfG Gü
Speedski, Assembler, Disassembler, Maschinencode
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Bin neugierig ob das jemand nach C++ übersetzt.
Servus,
zur Belebung des Thread nehme ich die Einladung an 😁
ich habe deinen C#-Code genommen und mehr oder wenig in C++ übersetzt. Aus Faulheit habe ich kein 100% C++ (siehe Cast, Array, ...) verwendet. Zudem spendierte ich dem Beispiel einen Memory-Pool. Dieser macht dem Programmierer das Leben einfacher, da dieser eventuelle Speicherleaks verhindert 😁
Ich habe auf keinerlei Optimierungsmaßnahmen, sowie eventuelle Verschlechterungen, geachtet (range check, Schleife Vor- oder Rückwärts, ...). Einfach abgetippt eben. Hoffentlich ohne Fehler. Abschreiben war noch nie meine Stärke 😉
Bei mir (Fedora + KDE) benötigt das Programm etwa 7,1MB RAM und eine CPU-Zeit (effektiv Zeit, nicht verstrichene Zeit) von ??? Microsekunden. Jedenfalls lange. Vielleicht ein Schleifen-Fehler? Oder ist eine Ausführungszeit von > 30 min normal?
Achaj, die Code-Stücke sind alles andere als schön und stellt ein schlechtes C++ Beispiel dar. Ich wollte so wenig Hilfsklassen, Struct und Makros verwenden und das ist dabei raus gekommen.
Installation:
Edit: Code entfernt (siehe weiter unten)
Servus,
Vielleicht ein Schleifen-Fehler? Oder ist eine Ausführungszeit von > 30 min normal?
Eher ein Fehler. Die Fortran-Programme brauchen ca. 1 Sekunde 😉
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Eher ein Fehler. Die Fortran-Programme brauchen ca. 1 Sekunde 😉
Hab den Code nochmal überflogen und einen Fehler gefunden. Dieser war in der gleich() Methode 🙂 Den Pool hab ich entfernt und auf malloc / free umgestellt. Also mit Board-Mitteln gelösst. Du findest ihn im oberen Post.
Bei mir dauert der Test (GCC) ~2,0 s. Kannst du die C++ Implementation bei dir mit VC und dem ICC (Intel-Compiler) testen? Letzterer ist für nicht komerzielle Projekte kostenlos.
Kannst du die C++ Implementation bei dir mit VC und dem ICC (Intel-Compiler) testen?
Könnte ich schon, aber das kannst du ja auch 😁
Wenn es um den Vergleich mit Fortran geht ist weiter oben der Link zu den Programmen.
mfG Gü
Edit: Link hinzugefügt
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Dann werd ich wohl meine alte XP CD suchen gehen. Werds höchstwahrscheinlich morgen aufsetzen und einen Vergleich druchführen.
Denn ICC gibts anscheinend nur für Linux zum kostenlosen Download 👍 Dafür ist ein Fortran-Compiler mit dabei 🙂
Wie gut kennst du dich mit dem VC und der Express-Version aus? Ist der erstellte Code 100% gleichwertig?
Wie gut kennst du dich mit dem VC und der Express-Version aus? Ist der erstellte Code 100% gleichwertig?
Mit C/C++ gar nicht. Aber bei C# ist der Kompiler Bestandteil des Frameworks und somit unabhängig von der Visual Studio-Version => der Code bzw. das Kompilat ist ident.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
@gfoidl Wieso multiplizierst du die vergangenen Millisekunden mit 0.2?
in Run()
sw.Stop();
_milliSeconds = (long)(sw.ElapsedMilliseconds * 0.2);
Da wäre C# und Mono deutlich schneller als C++ 🤔
Ich kenne mich in Fortran nicht aus, aber teilst du hier die Zeit durch 5?
Leider unterstützt der Intel Compiler kein SELinux 🙁 Einen Eingriff in mein privates System möchte ich nicht vornehmen und verzichte auf einen direkt Vergleich.
System: (die Angaben müssten ausreichen)
Der Vergleich von Fortran, C#, C++ und Java verlief wie erwartet. Allerdings überraschte mich der GCC. Im direkten Vergleich mit dem VC (Visual Studio Prof. 2008 ) konnte $MS$ den freien Compiler nie das Wasser reichen. Zuvor hätte ich den VC weiter vorne eingeschätzt. Microsoft arbeitet mit Intel eng zusammen, was einen enormen Vorteil bedeutet. Hmm egal.
Als leidenschaftlicher Java-Fan habe ich die C#-Implementierung nach Java portiert und die Range-Check-Elimination entfernt. Ohne weitere Optimierungen zeigen sich die stärken von Java (aktuelle Version: 1.6 SE).
Der Code von allen könnte natürlich noch optimiert werden, aber das ist nicht der Sinn hier. Falls mir der ein oder andere Fehler unterlaufen ist, sagt bitte was. Niemand ist unfehlbar 🙂
Sofern gfoidl nichts dagegen hat, stelle ich den Code unter dieser Adresse zum kostenlosen Download zur Verfügung. Über einen Bericht des ICC (Intel Compiler) im Vergleich des GCC würde ich mich sehr freuen. Der ICC ist für Linux + nicht kommerzielles Projekt für C++ und Fortran + Bibliotheken kostenlos zu downloaden.
Anmerkung: Der VC definiert bereits einen Type _int32, weswegen die typedef auskommentiert werden müssen.
Gruß,
Thomas
Wieso multiplizierst du die vergangenen Millisekunden mit 0.2?
Weil ich 5 Messungen durchführe und dann den arithmetischen Mittelwert berechne. Eine Einzelmessung ist statistisch nicht vertretbar. Es könnte auch / 5 dastehen (aber ich hab mir irgendwie angewöhnt Divisionen durch eine Konstante durch eine Multiplikationen zu erstetzen, der C#- und der JIT-Kompiler erledigen dies nicht).
Ich kenne mich in Fortran nicht aus, aber teilst du hier die Zeit durch 5?
Bei der Matritzenmultiplikation ja, bei der Monte-Carlo-Integration werde eh mehrere Durchläufe ausgeführt und die Gesamtzeit ausgeben (das ist genauso vergleichbar wie das arithmetische Mittel).
Microsoft arbeitet mit Intel eng zusammen, was einen enormen Vorteil bedeutet.
Da GNU OpenSource ist wird dafür der Kompiler von div. Fachkräften weiterentwickelt. Deshalb sind die Produkte auch nicht schlecht 😉
Ich kenn mich mit JAVA und C++ nicht aus aber die Vergleich sind interessant. Es wird 5x gemessen und die Gesamtzeit verwendet - irgendwas passt da nicht. Es kann jedoch sein dass ich was übersehen habe (da ich mich da nicht auskenne).
Weiters sollte auch die C++-Version mit der Standardoptimierung (wie Fortran) kompiliert werden. Oder wir kompilieren alles mit maximaler Optimierung 😉
Sofern gfoidl nichts dagegen hat, stelle ich den Code unter dieser Adresse zum kostenlosen Download zur Verfügung.
Finde ich nicht nötig denn die gesamte Information ist hier bei myCSharp "erhältlich" - ein Link auf dieses Thema genügt. Zum anderen ist er schon zum Download angeboten und es ist keinerlei Hinweis auf den Urheber/die Quelle ersichtlich. Daher bitte den Download wieder entfernen - zumindest der Teile die ich begesteuert habe.
Hab mir jetzt deinen Blog-Beitrag durchgelesen und das hat mich umgestimmt 😉 Bitte dennoch folgeden Punkte ergänzen:*Verlinke hier in deinem Beitrag zu deinem Blog-Artikel. *Ersetze im Blog-Beitrag den Namen 'gfoidl' durch 'Günther Foidl'. *Füge in den Codes die ich beigesteuert habe bitte meinen Namen (Günther Foidl) und als Quelle myCSharp.de hinzu (eventuell auch ein Link zu diesem Thema).
BTW: Könntest du deinen geposteten C++-Code etwas schrumpfen (zumindest die leeren Code-Teile) damit das Thema etwas leichter zu lesen ist)?
Intel DualCore (4 Kerne)
Gibt es DualCore auch mit 4 Kernen 😉
Edit: Info wegen Veröffentlichung geändert.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Ich kenn mich mit JAVA und C++ nicht aus aber die Vergleich sind interessant. Es wird 5x gemessen und die Gesamtzeit verwendet - irgendwas passt da nicht. Es kann jedoch sein dass ich was übersehen habe (da ich mich da nicht auskenne).
Weiters sollte auch die C++-Version mit der Standardoptimierung (wie Fortran) kompiliert werden. Oder wir kompilieren alles mit maximaler Optimierung 😉
Ich lasse bei Java und der C++-Variante die for-Schleife durchlaufen und nehme davor und zum Schluß die Zeit. Diese wird ausgegeben. Für das Ergebniss spielt es keine Rolle, ob die Zeit im Gesamten oder auf einen Durchlauf betrachtet wird. Daher spare ich mir das und ging davon aus, dass du ebenso vorgegangen bist. Am Schluss war ich dann etwas verwirrt, aber trotzdem die richtige Schlüsse daraus gezogen.
Die Optimierungseinstellungen des GCC machen in der Zeit fast keinen Unterschied. So gut wie jedes Release wird mit -o1, -o2, oder -o3 kompiliert. Lediglich -o0 verlangsamt den Test um den Faktor 1,7. Dafür ist den Benchmark zu klein. Aber diese Einstellung wird nur zum Debuggen benutzt.
Der C++-Code lässt sich auch noch verfeinern, stellt somit nicht das Maximum dar.
Java ist von Syntax her ähnlich wie C#. Die Annotations beginnen mit @...(), C# [...()]. Es gibt nur ein final, kein const oder readonly. "final" lässt sich zum Teil mit Reflection umgehen.
Hier noch der Blog-Eintrag, den Günther Foidl angesprochen hat.
Gruß,
Thomas
Sehr schöne Zusammenfassung auf dem Blog 👍 (und sogar meine Namen verlinkt 😉)
Java ist von Syntax her ähnlich wie C#.
Soviel hab ich schon kapiert 👅
Noch ein kleiner Tipp:
Die 3D-Diagramme schauen schön aus sind aber vom "Informationsgehalt" nicht so gut wie die (klassischen) 2D-Diagramme. Da die Information auf einem 2D-Medium (Papier, Bildschirm, ...) wiedergeben wird ist das einfach so. In wissenschaftlichen Publikationen finden sich deshalb auch hauptsächlich 2D-Diagramme (für Darstellungen solcher Art wie wir es hier haben). Dort wo die grafische Aufmachung wichtiger ist als die Information schauen die Verhältnisse anders aus 😉
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Java ist von Syntax her ähnlich wie C#.
Soviel hab ich schon kapiert 👅Noch ein kleiner Tipp:
Die 3D-Diagramme schauen schön aus sind aber vom "Informationsgehalt" nicht so gut wie die (klassischen) 2D-Diagramme. Da die Information auf einem 2D-Medium (Papier, Bildschirm, ...) wiedergeben wird ist das einfach so.){gray}
Genau das wollte ich mit dem 3D-Diagramm verhindern. Ich stimme dir 100% zu und setze diese 3D-Dinger so gut wie nie ein. Ich möchte die Verhältnisse PI mal Daumen darstellen und nicht das exakte Ergebniss. Aber danke für den Tipp.
Der Thread ist wirklich sehr interessant. Ich habe mich auch mit der Thematik beschäftigt, und ich werde letztlich die Vorteile beider Sprachen verwenden, indem ich zeitkritische Berechnungen auslagere und damit Fortran-DLLs "beauftrage".
Erste Tests haben gezeigt, dass die Performancevorteile sehr groß sind. Nachteil: Man muss einen teuren Compiler kaufen...