Orbit Benchmark

Wie ich schon sagte werde ich wahrscheinlich die Highscore-Liste erstellen wenn diese Seite hier noch voll ist.

Ich habe uebrigens noch ein Programm geschrieben mit einem neuen Algorithm. Wenn sich keiner dafuer interessiert moechte ich das hier einfach ins Forum stellen.;)
 
Geforces sind bei so kurzen Berechnungen deutlich schneller als Radeons. Ich gehe davon aus das die Geforces schneller auf 3D Takt umschalten und deswegen schneller sind bei diesem Benchmark.;)

Nope, Radeons brauchen nur ein paar optimierungen im code, um die register der vliw zu füllen (der armselige opencl compiler von amd schafft das net, wenn du den code nicht dafür vorbereitest). dann kannst du die brachiale rechenleistung der radeons nutzen...
nvidia karten profitieren von so Optimierungen auch (es werden operationen gespart), allerdings nicht so stark, da die theoretische rechenleistung auch bei nicht optimiertem code besser ausgeschöpft wird (besserer compiler, weniger theoretische rechenleistung).
bei optimiertem code ist eine radeon 5770 fast so schnell wie eine gtx 480 und die 2.7 tflops der radeon 5870 sind dann einsame spitze...


Mein Code ist sowohl fuer CPU und auch GPU sehr parallelisiert. Da die GPU eine sehr parallele Architektur hat ist sie meist schneller als die CPU.
Begenzt, C#.net ist eine schlechte basis für so einen vergleich (langsam durch cli)...
wenn du nativen maschinen code benutzt, und den ein ganz bisschen optimierst (sse) ist bei so einer kurzen berechnung der vorsprung der gpu dahin.
 
Zuletzt bearbeitet:
wenn du nativen maschinen code benutzt, und den ein ganz bisschen optimierst (sse) ist bei so einer kurzen berechnung der vorsprung der gpu dahin.
Naja, SSE kann max 4 Floats gleichzeitig verarbeiten, ne Geforce macht 32 Threads/Zyklus ("Warp") :ugly:
Und dann muss man sich im Falle von SSE noch mit so Sachen wie Memory ALignment rumquälen :devil:
 
Ich würde mich auch bereit erklären die Multi - GPU - Version zu testen.
Als GPUs würde ich HD4870 verwenden.

Dass waere super. Klawaryoshi hat sich auch bereit erklaert die Multi-GPU Version zu testen. Ich schreibe aber wahrscheinlich fruehestens eine nach den Weihnachtstagen, wenn ueberhaupt.;)

Nope, Radeons brauchen nur ein paar optimierungen im code, um die register der vliw zu füllen (der armselige opencl compiler von amd schafft das net, wenn du den code nicht dafür vorbereitest). dann kannst du die brachiale rechenleistung der radeons nutzen...

Wie sehen denn solche Optimierungen aus?;)

und die 2.7 tflops der radeon 5870 sind dann einsame spitze...

Dass hoert sich gut an.:D

Begenzt, C#.net ist eine schlechte basis für so einen vergleich (langsam durch cli)...
wenn du nativen maschinen code benutzt, und den ein ganz bisschen optimierst (sse) ist bei so einer kurzen berechnung der vorsprung der gpu dahin.

Wie genau koennte ich denn so einen Maschinen Code schreiben?;)

Naja, SSE kann max 4 Floats gleichzeitig verarbeiten, ne Geforce macht 32 Threads/Zyklus ("Warp") :ugly:
Und dann muss man sich im Falle von SSE noch mit so Sachen wie Memory ALignment rumquälen :devil:

Wie genau programmiert mann denn dass SSE voll ausgenutzt wird?;)

@all
Wie soll denn die Highscoreliste von der Struktur her aussehen?

Danke auch an alle die hier entweder Resultate posten oder mir Programmiertechnisch versuchen zu helfen.:daumen:
 
Wie sehen denn solche Optimierungen aus?;)
Register erhöhen, sodass du mehr operationen pro "thread" hast, am besten auch nach ausführort sortiert (z.b. erst alle operationen für die alu, dann alle für die fpu).
Abhängige Operationen sollten dabei vermieden werden (das packen die compiler einfach nicht).

lokale arbeitsbreite optimieren, so das immer für die vorhandene gpu die ideale arbeitsbreite gewählt wird. keine leerlaufenden threads.

anzahl der threads pro recheneinheit verringern, mehr operationen in einem thread.
das ist allerdings nicht immer einfach, da wenn du zu viele operationen pro thread hast (im verhältnis zur threadanzahl), das alles wieder langsamer wird.

für das alles ist abermeist eine erhebliche unstrukturierung des codes nötig.
aber selbst ein tesla c2060 kann dadurch fast doppelt so schnell werden.
und der leistungszuwachs bei radeons ist gigantisch.
(gilt übrigens auch für vertex und pixelshader, nicht nur für gpgpu)


was du machen kannst:
schleifen ausrollen (faktor 8 sollte gut sein), abhänigkeiten auflösen und gleiche operationen zusammenschieben (nochbesser: verwende vektortypen).
achtung beim schleifenausrollen: #pragma unroll N sortiert nicht nach operationen, aber grade das führt zu einem enormen leistungszuwachs.
sry, weiß grad nicht, wie ichs verständlicher erklären soll... ich mach mal ein beispiel:

aus:
int *a; float *b;
for(int i = 0; i < n; i++)
{
a *= 2; //alu
b *= 2; //fpu
}
wird z.B. (4 fach ausgerollt)
int *a, i;
float *b;
for (i = 0; i < n-4; i+=4)
{
a *=2; //alu
a[i+1] *= 2;
a[i+2] *= 2;
a[i+3] *= 2;

b *=2; //fpu
b[i+1] *= 2;
b[i+2] *= 2;
b[i+3] *= 2;
}
for(int j = i * 4; j < n; j++ )
{
a *= 2; //alu
b *= 2; //fpu
}
Wenn der Code unabhänig ist (a ist unabhänig von b) ist das sehr einfach. ich kenne deinen code nicht, aber sobald die berechnungen abhängig voneinander sind, was sehr häufig der fall ist, kann das sehr kompliziert werden, den code für die alu und die fpu zu trennen.

noch besser wäre vektorisierter code (ählich wie sse), da du damit dem compiler gleiche operationen aufzwingst:
int4 *a, i;
float4 *b;
for (i = 0; i < n / 4; i++)
{
a * = 2; // alu
b *=2; //fpu
}
for(int j = i * 4; j < n; j++ )
{
a *= 2; //alu
b *= 2; //fpu
}
(das jetzt idealerweise nochmal ausrollen ;))
wenn du die daten gleich als vector an die gpu überträgst, erhöhst du auch die bandbreite


operationen in einem thread pro einheit erhöhen, würde ich dir nicht empfehlen, das kann die ausführzeit zwar massiv verbessern, aber dafür musst du sehr genau über die hardware bescheid wissen. stark vereinfacht: 10 operationen auf 10000 threads ist schlechter als 100 operationen auf 1000 threads, solange du weniger als 1000 recheneinheiten hast. ganz so pauschal erreicht man leider nur wenig geschwindigkeitszuwachs.

welche optimierung am meinsten bringt, bzw. welche enventuell sogar nachteile bringt, hängt stark vom eingesetzten code ab.
auch verringern der anzahl an variablen bringt geschwindigkeitsvorteile,
local arrays statt private variablen kann vorteile birngen, muss aber nicht...


Wie genau koennte ich denn so einen Maschinen Code schreiben?;)
Zunächst einmal: C#.net (CLR) ist wie java nicht in maschinencode compilert, sondern wird nur in einen "zwischencode" übersetzt. die entgültige compilierung in maschinencode erfolgt zur laufzeit (just in time). Das kostet ausführzeit.
Maschinencode bekommst du, indem du z.B c/c++ oder fortran verwendest (mit visual studio geht das aber nur wenn du dann consolenprogramme erzeugst, denn visual c++ ist auch CLI (qt als gui wäre eine maschinencode alternative).
SSE ist vektorisierter Code, du kannst damit statt einem 32 bit datentypen, einen 128 bit dateintypen (oder 4 32bit datentypen) in einem taktzyklus ausführen. normal führst du eine operation auf eine variable pro takt aus. mit sse können bis zu 4 sein.
sse bekommst du in erster linie durch inline assambler bzw. durch die c++ headerfile xmmintrin.h (muss auch z.T. mit einem compilerflag aktiviert werden (gnu: -msse, -msse2 usw.)).
es gibt auch compiler, die automatisch sse code erzeugen können, meist aber nur für schleifen, was nur ein kleiner teil der möglichkeiten von sse ist.
das effizienteste ist, wenn man die performance kritischen stellen in assembler schreibst... nur das will man nicht ;)
Wenn du maximale effizenz willst, musst du dich aber auch mit der x86 architektur auskennen, da das alles sehr hardware nah ist.

wenn du das in c# schneller machen willst: nutze die bitoperatoren (gilt auch für den opencl c code)...
 
Zuletzt bearbeitet:
Register erhöhen, sodass du mehr operationen pro "thread" hast, am besten auch nach ausführort sortiert (z.b. erst alle operationen für die alu, dann alle für die fpu).
Abhängige Operationen sollten dabei vermieden werden (das packen die compiler einfach nicht).

Die Daten sind aber leider in dieser Berechnung abhaengig voneinander.

lokale arbeitsbreite optimieren, so das immer für die vorhandene gpu die ideale arbeitsbreite gewählt wird. keine leerlaufenden threads.

Waehlt der Compiler ansonsten automatisch die lokale arbeitsbreite?

anzahl der threads pro recheneinheit verringern, mehr operationen in einem thread.
das ist allerdings nicht immer einfach, da wenn du zu viele operationen pro thread hast (im verhältnis zur threadanzahl), das alles wieder langsamer wird.

Macht der Compiler aus einem Kernel ein Thread pro Array Index? Oder pro jeder Operation in dem Kernel?

für das alles ist abermeist eine erhebliche unstrukturierung des codes nötig.
aber selbst ein tesla c2060 kann dadurch fast doppelt so schnell werden.
und der leistungszuwachs bei radeons ist gigantisch.
(gilt übrigens auch für vertex und pixelshader, nicht nur für gpgpu)

Mir waere das den Aufwand wert.

was du machen kannst:
schleifen ausrollen (faktor 8 sollte gut sein), abhänigkeiten auflösen und gleiche operationen zusammenschieben (nochbesser: verwende vektortypen).

Vektoren benutze ich bereits.;)

achtung beim schleifenausrollen: #pragma unroll N sortiert nicht nach operationen, aber grade das führt zu einem enormen leistungszuwachs.
sry, weiß grad nicht, wie ichs verständlicher erklären soll... ich mach mal ein beispiel:

aus:
int *a; float *b;
for(int i = 0; i < n; i++)
{
a *= 2; //alu
b *= 2; //fpu
}
wird z.B. (4 fach ausgerollt)
int *a, i;
float *b;
for (i = 0; i < n-4; i+=4)
{
a *=2; //alu
a[i+1] *= 2;
a[i+2] *= 2;
a[i+3] *= 2;

b *=2; //fpu
b[i+1] *= 2;
b[i+2] *= 2;
b[i+3] *= 2;
}
for(int j = i * 4; j < n; j++ )
{
a *= 2; //alu
b *= 2; //fpu
}
Wenn der Code unabhänig ist (a ist unabhänig von b) ist das sehr einfach. ich kenne deinen code nicht, aber sobald die berechnungen abhängig voneinander sind, was sehr häufig der fall ist, kann das sehr kompliziert werden, den code für die alu und die fpu zu trennen.


Leider benutze ich fast ausschliesslich floats. Ausserdem sind die einzelnen Daten voneinander abhaengig in meinem code.

noch besser wäre vektorisierter code (ählich wie sse), da du damit dem compiler gleiche operationen aufzwingst:
int4 *a, i;
float4 *b;
for (i = 0; i < n / 4; i++)
{
a * = 2; // alu
b *=2; //fpu
}
for(int j = i * 4; j < n; j++ )
{
a *= 2; //alu
b *= 2; //fpu
}
(das jetzt idealerweise nochmal ausrollen ;))
wenn du die daten gleich als vector an die gpu überträgst, erhöhst du auch die bandbreite


Ja, ich bewege bereits die Daten als Vektoren zur GPU.

operationen in einem thread pro einheit erhöhen, würde ich dir nicht empfehlen, das kann die ausführzeit zwar massiv verbessern, aber dafür musst du sehr genau über die hardware bescheid wissen. stark vereinfacht: 10 operationen auf 10000 threads ist schlechter als 100 operationen auf 1000 threads, solange du weniger als 1000 recheneinheiten hast. ganz so pauschal erreicht man leider nur wenig geschwindigkeitszuwachs.

Ich haette es lieber wenn der Code auf jeder GPU gut laeuft und nicht nur mit GPUs ueber 1000 Recheneinheiten.

welche optimierung am meinsten bringt, bzw. welche enventuell sogar nachteile bringt, hängt stark vom eingesetzten code ab.
auch verringern der anzahl an variablen bringt geschwindigkeitsvorteile,
local arrays statt private variablen kann vorteile birngen, muss aber nicht...

Da kann ich noch optimieren.

Zunächst einmal: C#.net (CLR) ist wie java nicht in maschinencode compilert, sondern wird nur in einen "zwischencode" übersetzt. die entgültige compilierung in maschinencode erfolgt zur laufzeit (just in time). Das kostet ausführzeit.
Maschinencode bekommst du, indem du z.B c/c++ oder fortran verwendest (mit visual studio geht das aber nur wenn du dann consolenprogramme erzeugst, denn visual c++ ist auch CLI (qt als gui wäre eine maschinencode alternative).
SSE ist vektorisierter Code, du kannst damit statt einem 32 bit datentypen, einen 128 bit dateintypen (oder 4 32bit datentypen) in einem taktzyklus ausführen. normal führst du eine operation auf eine variable pro takt aus. mit sse können bis zu 4 sein.
sse bekommst du in erster linie durch inline assambler bzw. durch die c++ headerfile xmmintrin.h (muss auch z.T. mit einem compilerflag aktiviert werden (gnu: -msse, -msse2 usw.)).
es gibt auch compiler, die automatisch sse code erzeugen können, meist aber nur für schleifen, was nur ein kleiner teil der möglichkeiten von sse ist.
das effizienteste ist, wenn man die performance kritischen stellen in assembler schreibst... nur das will man nicht ;)
Wenn du maximale effizenz willst, musst du dich aber auch mit der x86 architektur auskennen, da das alles sehr hardware nah ist.

wenn du das in c# schneller machen willst: nutze die bitoperatoren (gilt auch für den opencl c code)...

Dass ist mit leider etwas zu aufwendig da ich leider nur C# halbwegs gut kann.;)

Vielen Dank fuer deine Hilfe.:daumen:
 
Die Daten sind aber leider in dieser Berechnung abhaengig voneinander.
Du kannst trotzdem so weit wie möglich ausrollen (um faktor > 5).
Und du kannst versuchen, die abhängigkeiten zurückzurechnen.
Das kann zwar an manchen stellen zu doppeltberechnungen führen, wenn es gut ungesetzt wird, dann ist es aber trotzdem schneller, weil die hardware optimal ausgelastet ist.
Waehlt der Compiler ansonsten automatisch die lokale arbeitsbreite?
Also im khrono-opencl-c binding musst du immer eine lokale arbeitsbreite angeben. im schlechtestenfall 1.
du verwendest ein c# binding, richtig? dann kann es sein, dass das binding automatisch eine lokale worksize wählt. die ist dann wahrscheinlich die maximal mögliche, aber nicht umbedingt die beste.
sowohl radeon als auch geforce haben am liebsten eine lokale arbeitsbreite einer 2er potenz > 32.

Macht der Compiler aus einem Kernel ein Thread pro Array Index? Oder pro jeder Operation in dem Kernel?
auf der gpu kannst du einen "thread" nicht direkt mit einem programmthread auf der cpu vergleichen. aber auch auf der cpu ist ein "opencl-thread" nicht gleich einem programmthread
Also du hast eine globale und eine lokale arbeitsbreite.
z.B.
global work size = 512
lokal work size = 32
das heißt dann, das du 16 (global work size / lokal work size) gruppen hast.
elemente innerhalb einer gruppe werden auf der gleichen recheneinheit ausgeführt (simd). die verschiedenen gruppen werden auf verschiedenen recheneinheiten ausgeführt (in etwa ein programmthread).

Ich haette es lieber wenn der Code auf jeder GPU gut laeuft und nicht nur mit GPUs ueber 1000 Recheneinheiten.
hier hast du mich falsch verstanden. der code würde auf gpus unter 1000 recheneinheiten gut laufen, und auf gpus mit mehr als 1000 recheneinheiten werden dann einige recheneinheiten nicht genutzt. das kannst du aber durch abfrage der recheneinheiten und entsprechende threadanzahl verhindern (ideal: anzahl recheneinheiten = anzahl threads --> ideale ausnutzung der recheneinheiten und kein leistungsverlust durch threadverwaltung).

wenn du jetzt z.B. 1000 elemente auf 10 recheneinheiten verarbeiten willst,
ist es am einfachsten, wenn du eine (globale) arbeitsbreite von 1000 erzeugst, und die elemente alle einzeln verarbeitest. das ist aber relativ ineffizient.
es ist effizienter wenn du eine (globale) arbeitsbreite von 10 verwendest, und immer 100 elemente "zusammen" verarbeitest.

die arbeitsbreite sollte idealerweise gleich der anzahl an recheneinheiten sein. dann schafft es sogar der schlechteste compiler den code (fast) so gut zu compilieren, als wäre er "hart geschaltet" (d.h. in assembler genau auf die vorhandene hardware optimiert). das geht bei opencl, da "just in time" kompiliert wird, bedeutet aber dass man vorher abfragen muss, was für hardware grade verwendet wird... gibt einen haufen präprozessoranweisungen (dank jit keine zeitraubenden if abfragen nötig)... und ist selbst für einfachen code selten trivial.

Vektoren benutze ich bereits.;)
Ja, ich bewege bereits die Daten als Vektoren zur GPU.
muss jetzt nochmal nachfragen, weil sich das oben nicht so angehört hat, als du gesagt hast, deine daten wären abhängig. wahrscheinlich ich mich auch zu ungenau ausgenau ausgedrückt, sry. ich meine nicht die "c# vectoren" sondern ich meine die aus opencl. die haben nichts miteinander zu tun.
also charN, intN, floatN, usw im kernel bzw. cl_charN, cl_intN, cl_floatN im hostcode.
die verwendet man um code zu vektorisieren, also um ihn simd fähig zu machen. siehe unterstes beispiel oben.

Leider benutze ich fast ausschliesslich floats. Ausserdem sind die einzelnen Daten voneinander abhaengig in meinem code.
Das ist egal. Dann war nur das beispiel oben nicht ideal. Du musst nur versuchen, Operationen, die auf die gleich variable angewendet werden, zusammenzubringen. das tritt immer beim ausrollen von schleifen aus. Wenn du #pragma unroll verwendest, macht der compiler das grundsätzlich falsch (weil es häufig sehr schwierig ist, maschinell fast immer unmöglich, manchmal auch komplett unmöglich).
also wenn du 2 variablen a und b hast,
solltest du das so machen:
a*= 2;
a+=4;
b*= 2;
b+=4;

und nicht so:
a*= 2;
b*= 2;
a+=4;
b+=4;


Dass ist mit leider etwas zu aufwendig da ich leider nur C# halbwegs gut kann.;)
gut, wenn du kein c kannst, ist es wohl nicht sinnvoll mit sse anzufangen.

Wenn du ernsthaft optimieren willst, brauchst du aber reproduzierbare ergebnisse, dafür müsste der zufallsgenerator aus.
den kannst du am ende wieder aktivieren.
und du brachst möglichst viele karten zum vergleichen (da gibts im hier im forum ja genug).

MFG Sheeep
 
Zuletzt bearbeitet:
Wie gewünscht kommen hier Ergebnisse mit einer Sandy Bridge auf 5.0 GHz.

Ich habe mal eine Hand voll Ergebnisse angehängt.
 
Die Highscore Liste ist jetzt im Startpost zu sehen. Noch ein kleines Update meinerseits:
Danke @alle Teilnehmer.:daumen::)
 
@tysol
Bei dem Score mit 3,6GHz war der Speicher genauso eingestellt wie bei den 3,2GHz (CPUz)
2x2GB DDR2-1066 CL5 unganged (2x64Bit)

MfG
 
Hier, bitteschön. ;)

• Core i7 980X mit Standardtakt und einmal mit 4,2GHz
und
• GTX480 mit Standardtakt und einmal mit 945/1100/1890MHz.

Arbeitsspeicher war immer bei 800MHz, respektive 1600MHz.
 
Zurück