Problem beim Schreiben eines CPU Benchmarks
-
Hi
Ich hab im laufe der Projektwoche unserer Schule mal einen CPU Benchmark in C++/MFC geschrieben.
Das ganze wollte ich nun nach und nach erweitern. Da mir aber viele Dinge zu umständlich mit C++/MFC sind, wollte ich das ganze in C# schreiben und mich so auch in C# einarbeiten(bisher noch nie etwas damit gemacht).
Allerdings habe ich das Problem, dass die Ergebnisse bei 50% der Leute extrem schwanken, wenn der Benchmark ohne neustart der Applikation wiederholt wird.
Diese Schwankungen können sowohl unter Vista und XP auftreten(egal ob 32bit oder 64bit).
Bei der C++/MFC Version des Benchmarks treten diese Schwankungen nicht auf.Liegt das Ganze also allgemein an C#?
Wie gesagt bei 50% der Leute sind die Ergebnisse absolut gleich und bei anderen Schwanken sie extrem.
mfg
Foncehier der Downloadlink des Benchmarks in C#
http://rapidshare.com/files/98714098/Foncemark.exeund hier der Code
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; using System.Diagnostics; using System.Runtime.InteropServices; namespace Foncemark { public partial class Form1 : Form { Thread Thread1; Thread Thread2; Thread Thread3; Thread Thread4; Int32 ProgressbarStatus; Int32 ResultFpu; Int32 ResultAlu; Int32 ResultComplete; public Form1() { InitializeComponent(); this.ResultALU.Text = "No Result"; this.ResultFPU.Text = "No Result"; this.ResultTotal.Text = "No Result"; } public void BenchmarkFPU() { Double a, b; for (Int64 i = 0; i <= 1000 * 1000 * 3; i++) { a = 2 * Math.Sqrt(3.0); b = 3; do { a = 2 * a * b / (a + b); b = Math.Sqrt(a * b); } while (a != b); } } public void BenchmarkALU() { Int64 a = 3; Int64 b = 0; for (Int64 i = 1000 * 1000 * 3; i >= 0; i--) { a = a + b; a = (a & b) * 10 * (a | b) * i; b = a * a * a * i; a = (a | b) * 15 * i; for (Int64 j = 0; j <= 25; j++) { a = (++b - a) * ((10 * j + 35 * j) & i) * j; } b = a - (2 * b + 2 * a); a = (b * i - a) * 10 * (i - b); b = (a & i) * ((3 & i) * i); a = (b & i) * ((3 & i) * 25); } } private void Start_Click(object sender, EventArgs e) { this.Start.Enabled = false; this.ProgressBar.Minimum = 0; this.ProgressBar.Maximum = 4; this.BechmarkThread.RunWorkerAsync(); this.ValueStatusTimer.Start(); } private void Stop_Click(object sender, EventArgs e) { this.ValueStatusTimer.Stop(); this.Thread1.Abort(); this.Thread2.Abort(); this.Thread3.Abort(); this.Thread4.Abort(); } private void About_Click(object sender, EventArgs e) { } private void startToolStripMenuItem_Click(object sender, EventArgs e) { //Start_Click(); } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { ResultFpu = 0; ResultAlu = 0; ResultComplete = 0; ProgressbarStatus = 0; WaitHandle[] handles = new WaitHandle[4]; this.Thread1 = new Thread(new ThreadStart(BenchmarkFPU)); this.Thread2 = new Thread(new ThreadStart(BenchmarkFPU)); this.Thread3 = new Thread(new ThreadStart(BenchmarkFPU)); this.Thread4 = new Thread(new ThreadStart(BenchmarkFPU)); this.Thread1.Priority = ThreadPriority.Highest; this.Thread2.Priority = ThreadPriority.Highest; this.Thread3.Priority = ThreadPriority.Highest; this.Thread4.Priority = ThreadPriority.Highest; ProgressbarStatus = 1; HiPerfTimer pt = new HiPerfTimer(); pt.Start(); this.Thread1.Start(); this.Thread2.Start(); this.Thread3.Start(); this.Thread4.Start(); this.Thread1.Join(); this.Thread2.Join(); this.Thread3.Join(); this.Thread4.Join(); pt.Stop(); this.Thread1.Abort(); this.Thread2.Abort(); this.Thread3.Abort(); this.Thread4.Abort(); ResultFpu = Convert.ToInt32(10000 * (1 / pt.Duration)); ProgressbarStatus = 2; this.Thread1 = new Thread(new ThreadStart(BenchmarkALU)); this.Thread2 = new Thread(new ThreadStart(BenchmarkALU)); this.Thread3 = new Thread(new ThreadStart(BenchmarkALU)); this.Thread4 = new Thread(new ThreadStart(BenchmarkALU)); this.Thread1.Priority = ThreadPriority.Highest; this.Thread2.Priority = ThreadPriority.Highest; this.Thread3.Priority = ThreadPriority.Highest; this.Thread4.Priority = ThreadPriority.Highest; ProgressbarStatus = 3; pt.Start(); this.Thread1.Start(); this.Thread2.Start(); this.Thread3.Start(); this.Thread4.Start(); this.Thread1.Join(); this.Thread2.Join(); this.Thread3.Join(); this.Thread4.Join(); pt.Stop(); this.Thread1.Abort(); this.Thread2.Abort(); this.Thread3.Abort(); this.Thread4.Abort(); ResultAlu = Convert.ToInt32(10000 * (1 / pt.Duration)); ResultComplete = ResultAlu + ResultFpu; ProgressbarStatus = 4; } private void ValueStatusTimer_Tick(object sender, EventArgs e) { this.ProgressBar.Value = ProgressbarStatus; this.ResultALU.Text = Convert.ToString(ResultAlu); this.ResultFPU.Text = Convert.ToString(ResultFpu); if (ResultAlu > 0) { this.ResultTotal.Text = Convert.ToString(ResultAlu + ResultFpu); this.Start.Enabled = true; this.ValueStatusTimer.Stop(); } } } } class HiPerfTimer { [DllImport("Kernel32.dll")] private static extern bool QueryPerformanceCounter( out long lpPerformanceCount); [DllImport("Kernel32.dll")] private static extern bool QueryPerformanceFrequency( out long lpFrequency); private long startTime, stopTime; private long freq; // Constructor public HiPerfTimer() { startTime = 0; stopTime = 0; if (QueryPerformanceFrequency(out freq) == false) { // high-performance counter not supported throw new Win32Exception(); } } // Start the timer public void Start() { // lets do the waiting threads there work Thread.Sleep(0); QueryPerformanceCounter(out startTime); } // Stop the timer public void Stop() { QueryPerformanceCounter(out stopTime); } // Returns the duration of the timer (in seconds) public double Duration { get { return (double)(stopTime - startTime) / (double)freq; } } }
-
Wo dass schwanken herkommt weiß ich momentan auch net, aber ich würde am ehesten auf Stromsparmodi und damit heruntertakten des Prozessors tippen, das dauert einfach nen kurzen Moment bis der auf Maximaltakt ist, wenn er vorher vor sich hin schläft.
Aber allgemein gibt es einige Punkte wo du dran arbeiten könntest um nen genaueres Ergebniss zu bekommen.
Der Zeitmessung an sich ist recht genau, aber so wie du sie anwendest nicht. Du misst die Zeit außerhalb der Threads. Selbst wenn sie höchste Prio haben, geben die Threads Rechenzeit ab und du misst eine längere Zeit als die Zeit wo die Threads wirklich arbeiten. Auch dass mit 4 gleichzeitigen Threads ist überdenkenswert. Auf Multicoresystemen bekommt man damit eine genauere Auslastung der vielen Kerne, auf Einkernsystemen dagegen wird bei 4 so hochprioren Threads der Schedulingaufwand ungemein hoch und verfälscht dir die Ergebnisse ganz schön.
Dann ist dein Stop Button gut gemeint in der GUI, aber unbenutzbar wenn der Benchmark läuft
Zusammenängend mit dem Stop noch was zum Thread.Abort(). Die Funktion stoppt nicht die Threads zwangsläufig, es wird nur ne Exception im Thread geworfen der ihn evtl. zum beenden bringt! Besser wäre es in der Benchmarkfunktion ne Abbruchbedingung zu schaffen die man von außen auf wahr setzen kann, so dass die Funktion sich von alleine beendet.
Eine Anmerkung noch zum Schluss: Ich würde Bibliotheksfunktionen nicht benutzen. Da weißt du nicht wie diese implementiert sind, und ob du mit denen auch wirklich auch nur das Testest was du vorhattest zu testen.
Hoffe diese Anmerkungen helfen dir.
-
Ja das mit der Zeitmessung will ich vielleicht nochmal ändern.
Und danke für den hinweis zu dem Thread.Abort(), aber wie gesagt ist das erste Programm welches ich in C# schreibe.
Das auf zwei oder ein Kern CPUs der Schedulingaufwand höher ist ist klar und auch beabsichtigt. Skalieren tuen die Ergebnisse aber mit der anzahl der Kerne so wie ich es auch wollte. ist also nicht das Problem.Nun zu den Schwankungen...
Also an Stromspar Features liegt es auch nicht.
Sie treten bei mir auf egal ob ich C1E und EIST(hab nen Core2 Duo@3GHz) aktiviert oder deaktiviert habe. Selbiges gilt für Athlon64 Systeme(bei bekanntem getestet).
Ich weiß bei den Schwankungen auch einfach nicht mehr weiter.
Mich wunder halt das sie bei einigen Stabil sind und bei anderen nicht....EDIT:
So habe nun mal die Zeitmessung in die Threads verlagert und siehe da die Ergebnisse sind höher als vorher.
Allerdings habe ich nun das Problem, dass das Ganze nur beim ersten Durchlauf funktioniert. Danach bekomme ich keine Ergebnisse ausgegeben und die Progressbar wird schnell auf den Maximalwert gebracht.using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; using System.Diagnostics; using System.Runtime.InteropServices; namespace Foncemark { public partial class Form1 : Form { Thread Thread1; Thread Thread2; Thread Thread3; Thread Thread4; Int32 ProgressbarStatus; Int32 ResultFpu; Int32 ResultAlu; Int32 ResultComplete; public Form1() { InitializeComponent(); this.ProgressBar.Minimum = 0; this.ProgressBar.Maximum = 8; this.ResultALU.Text = "No Result"; this.ResultFPU.Text = "No Result"; this.ResultTotal.Text = "No Result"; } public void BenchmarkFPU() { Double a, b; HiPerfTimer pt = new HiPerfTimer(); pt.Start(); for (Int64 i = 0; i <= 1000 * 1000 * 3; i++) { a = 2 * Math.Sqrt(3.0); b = 3; do { a = 2 * a * b / (a + b); b = Math.Sqrt(a * b); } while (a != b); } pt.Stop(); Monitor.Enter(this); Thread.Sleep(50); ResultFpu += Convert.ToInt32(10000 * (1 / pt.Duration)); ProgressbarStatus ++; Monitor.Exit(this); } public void BenchmarkALU() { Int64 a = 3; Int64 b = 0; HiPerfTimer pt = new HiPerfTimer(); pt.Start(); for (Int64 i = 1000 * 1000 * 3; i >= 0; i--) { a = a + b; a = (a & b) * 10 * (a | b) * i; b = a * a * a * i; a = (a | b) * 15 * i; for (Int64 j = 0; j <= 25; j++) { a = (++b - a) * ((10 * j + 35 * j) & i) * j; } b = a - (2 * b + 2 * a); a = (b * i - a) * 10 * (i - b); b = (a & i) * ((3 & i) * i); a = (b & i) * ((3 & i) * 25); } pt.Stop(); Monitor.Enter(this); Thread.Sleep(50); ResultAlu += Convert.ToInt32(10000 * (1 / pt.Duration)); ProgressbarStatus ++; Monitor.Exit(this); } private void Start_Click(object sender, EventArgs e) { this.Start.Enabled = false; ResultFpu = 0; ResultAlu = 0; ResultComplete = 0; this.ProgressBar.Value = 0; this.BechmarkThread.RunWorkerAsync(); this.ValueStatusTimer.Start(); } private void startToolStripMenuItem_Click(object sender, EventArgs e) { this.Start.Enabled = false; ResultFpu = 0; ResultAlu = 0; ResultComplete = 0; this.ProgressBar.Value = 0; this.BechmarkThread.RunWorkerAsync(); this.ValueStatusTimer.Start(); } private void Stop_Click(object sender, EventArgs e) { this.ValueStatusTimer.Stop(); this.Thread1.Abort(); this.Thread2.Abort(); this.Thread3.Abort(); this.Thread4.Abort(); } private void About_Click(object sender, EventArgs e) { MessageBox.Show(" ---------------------------\n -=Foncemark=-\n\n Version 2.00\n ---------------------------", "About", MessageBoxButtons.OK); } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { this.Thread1 = new Thread(new ThreadStart(BenchmarkFPU)); this.Thread2 = new Thread(new ThreadStart(BenchmarkFPU)); this.Thread3 = new Thread(new ThreadStart(BenchmarkFPU)); this.Thread4 = new Thread(new ThreadStart(BenchmarkFPU)); this.Thread1.Priority = ThreadPriority.Normal; this.Thread2.Priority = ThreadPriority.Normal; this.Thread3.Priority = ThreadPriority.Normal; this.Thread4.Priority = ThreadPriority.Normal; this.Thread1.Start(); this.Thread2.Start(); this.Thread3.Start(); this.Thread4.Start(); this.Thread1.Join(); this.Thread2.Join(); this.Thread3.Join(); this.Thread4.Join(); this.Thread1.Abort(); this.Thread2.Abort(); this.Thread3.Abort(); this.Thread4.Abort(); this.Thread1 = new Thread(new ThreadStart(BenchmarkALU)); this.Thread2 = new Thread(new ThreadStart(BenchmarkALU)); this.Thread3 = new Thread(new ThreadStart(BenchmarkALU)); this.Thread4 = new Thread(new ThreadStart(BenchmarkALU)); this.Thread1.Priority = ThreadPriority.Normal; this.Thread2.Priority = ThreadPriority.Normal; this.Thread3.Priority = ThreadPriority.Normal; this.Thread4.Priority = ThreadPriority.Normal; this.Thread1.Start(); this.Thread2.Start(); this.Thread3.Start(); this.Thread4.Start(); this.Thread1.Join(); this.Thread2.Join(); this.Thread3.Join(); this.Thread4.Join(); this.Thread1.Abort(); this.Thread2.Abort(); this.Thread3.Abort(); this.Thread4.Abort(); } private void ValueStatusTimer_Tick(object sender, EventArgs e) { this.ProgressBar.Value = ProgressbarStatus; switch (ProgressbarStatus) { case 0: this.ResultFPU.Text = Convert.ToString(ResultFpu); this.ResultALU.Text = Convert.ToString(ResultAlu); this.ResultTotal.Text = Convert.ToString(ResultAlu + ResultFpu); break; case 4: this.ResultFPU.Text = Convert.ToString(ResultFpu); break; case 8: this.ResultALU.Text = Convert.ToString(ResultAlu); this.ResultTotal.Text = Convert.ToString(ResultAlu + ResultFpu); this.Start.Enabled = true; this.ValueStatusTimer.Stop(); break; } } } } class HiPerfTimer { [DllImport("Kernel32.dll")] private static extern bool QueryPerformanceCounter( out long lpPerformanceCount); [DllImport("Kernel32.dll")] private static extern bool QueryPerformanceFrequency( out long lpFrequency); private long startTime, stopTime; private long freq; // Constructor public HiPerfTimer() { startTime = 0; stopTime = 0; if (QueryPerformanceFrequency(out freq) == false) { // high-performance counter not supported throw new Win32Exception(); } } // Start the timer public void Start() { // lets do the waiting threads there work Thread.Sleep(0); QueryPerformanceCounter(out startTime); } // Stop the timer public void Stop() { QueryPerformanceCounter(out stopTime); } // Returns the duration of the timer (in seconds) public double Duration { get { return (double)(stopTime - startTime) / (double)freq; } } }
-
Schau Dir mal die Klasse an:
http://msdn2.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx
-
loks schrieb:
Schau Dir mal die Klasse an:
http://msdn2.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx
Also wenn ich den Text dort richtig verstanden habe ist die Klasse Stopwatch quasi der QueryPerformanceCounter den ich aus C++ kenne?
-
Fonce schrieb:
loks schrieb:
Schau Dir mal die Klasse an:
http://msdn2.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx
Also wenn ich den Text dort richtig verstanden habe ist die Klasse Stopwatch quasi der QueryPerformanceCounter den ich aus C++ kenne?
Ja, 1:1 übertragen.
-
Ok nutze nun die Stopwatch Klasse zur Zeitmessung.
Weiss bei diesem Problem allerdings nicht weiter.
So habe nun mal die Zeitmessung in die Threads verlagert und siehe da die Ergebnisse sind höher als vorher.
Allerdings habe ich nun das Problem, dass das Ganze nur beim ersten Durchlauf funktioniert. Danach bekomme ich keine Ergebnisse ausgegeben und die Progressbar wird schnell auf den Maximalwert gebracht.using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; using System.Diagnostics; using System.Runtime.InteropServices; namespace Foncemark { public partial class FoncemarkMain : Form { Thread Thread1; Thread Thread2; Thread Thread3; Thread Thread4; Int32 ProgressbarStatus; Int32 ResultFpu; Int32 ResultAlu; public FoncemarkMain() { InitializeComponent(); //Progressbar Settings this.ProgressBar.Minimum = 0; this.ProgressBar.Maximum = 8; this.ResultALU.Text = "No Result"; this.ResultFPU.Text = "No Result"; this.ResultTotal.Text = "No Result"; } public void BenchmarkFPU() { Double a, b; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); for (Int64 i = 0; i <= 1000 * 1000 * 3; i++) { a = 2 * Math.Sqrt(3.0); b = 3; do { a = 2 * a * b / (a + b); b = Math.Sqrt(a * b); } while (a != b); } stopWatch.Stop(); Monitor.Enter(this); Thread.Sleep(50); ResultFpu += Convert.ToInt32(stopWatch.ElapsedMilliseconds.ToString())/10; ProgressbarStatus ++; Monitor.Exit(this); } public void BenchmarkALU() { Int64 a = 3; Int64 b = 0; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); for (Int64 i = 1000 * 1000 * 3; i >= 0; i--) { a = a + b; a = (a & b) * 10 * (a | b) * i; b = a * a * a * i; a = (a | b) * 15 * i; for (Int64 j = 0; j <= 25; j++) { a = (++b - a) * ((10 * j + 35 * j) & i) * j; } b = a - (2 * b + 2 * a); a = (b * i - a) * 10 * (i - b); b = (a & i) * ((3 & i) * i); a = (b & i) * ((3 & i) * 25); } stopWatch.Stop(); Monitor.Enter(this); Thread.Sleep(10); ResultAlu += Convert.ToInt32(stopWatch.ElapsedMilliseconds.ToString())/10; ProgressbarStatus ++; Monitor.Exit(this); } private void Start_Click(object sender, EventArgs e) { this.Start.Enabled = false; ResultFpu = 0; ResultAlu = 0; this.ProgressBar.Value = 0; this.BechmarkThread.RunWorkerAsync(); this.ValueStatusTimer.Start(); } private void startToolStripMenuItem_Click(object sender, EventArgs e) { this.Start.Enabled = false; ResultFpu = 0; ResultAlu = 0; this.ProgressBar.Value = 0; this.BechmarkThread.RunWorkerAsync(); this.ValueStatusTimer.Start(); } private void Stop_Click(object sender, EventArgs e) { this.ValueStatusTimer.Stop(); this.Thread1.Abort(); this.Thread2.Abort(); this.Thread3.Abort(); this.Thread4.Abort(); } private void About_Click(object sender, EventArgs e) { } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { this.Thread1 = new Thread(new ThreadStart(BenchmarkFPU)); this.Thread2 = new Thread(new ThreadStart(BenchmarkFPU)); this.Thread3 = new Thread(new ThreadStart(BenchmarkFPU)); this.Thread4 = new Thread(new ThreadStart(BenchmarkFPU)); this.Thread1.Priority = ThreadPriority.Normal; this.Thread2.Priority = ThreadPriority.Normal; this.Thread3.Priority = ThreadPriority.Normal; this.Thread4.Priority = ThreadPriority.Normal; this.Thread1.Start(); this.Thread2.Start(); this.Thread3.Start(); this.Thread4.Start(); this.Thread1.Join(); this.Thread2.Join(); this.Thread3.Join(); this.Thread4.Join(); this.Thread1 = new Thread(new ThreadStart(BenchmarkALU)); this.Thread2 = new Thread(new ThreadStart(BenchmarkALU)); this.Thread3 = new Thread(new ThreadStart(BenchmarkALU)); this.Thread4 = new Thread(new ThreadStart(BenchmarkALU)); this.Thread1.Priority = ThreadPriority.Normal; this.Thread2.Priority = ThreadPriority.Normal; this.Thread3.Priority = ThreadPriority.Normal; this.Thread4.Priority = ThreadPriority.Normal; this.Thread1.Start(); this.Thread2.Start(); this.Thread3.Start(); this.Thread4.Start(); this.Thread1.Join(); this.Thread2.Join(); this.Thread3.Join(); this.Thread4.Join(); BechmarkThread.CancelAsync(); } private void ValueStatusTimer_Tick(object sender, EventArgs e) { this.ProgressBar.Value = ProgressbarStatus; switch (ProgressbarStatus) { case 0: this.ResultFPU.Text = Convert.ToString(ResultFpu); this.ResultALU.Text = Convert.ToString(ResultAlu); this.ResultTotal.Text = Convert.ToString(ResultAlu + ResultFpu); break; case 4: this.ResultFPU.Text = Convert.ToString(ResultFpu); break; case 8: this.ResultALU.Text = Convert.ToString(ResultAlu); this.ResultTotal.Text = Convert.ToString(ResultAlu + ResultFpu); this.Start.Enabled = true; this.ValueStatusTimer.Stop(); break; } } } }
-
Deine tollen "Benchmarks" werden alle einfach wegoptimiert
-
Fonce schrieb:
Allerdings habe ich nun das Problem, dass das Ganze nur beim ersten Durchlauf funktioniert. Danach bekomme ich keine Ergebnisse ausgegeben und die Progressbar wird schnell auf den Maximalwert gebracht.
Kann jedem passieren. Versuch's mal so:
private void Start_Click(object sender, EventArgs e) { this.Start.Enabled = false; this.ProgressBar.Minimum = 0; this.ProgressBar.Maximum = 8; this.ResultALU.Text = "No Result"; this.ResultFPU.Text = "No Result"; this.ResultTotal.Text = "No Result"; ProgressbarStatus = 0; ResultFpu = 0; ResultAlu = 0; this.ProgressBar.Value = 0; this.BechmarkThread.RunWorkerAsync(); this.ValueStatusTimer.Start(); }
-
danke nun funktioniert es endlich gescheit.
Werd diese version nunmal an diverse Leute zum testen schicken ob die ergebnisse bei denen mmer noch schwanken
bei mir tun sie es auf jeden fall nicht mehrdanke euch allen für die Hilfe
-
Eine Kleinigkeit noch: Statt Monitor.Enter/Exit kannst du lock verwenden, macht genau das gleiche, nur als Sprachkonstrukt statt als Blibliotheksfunktion.
-
Fonce schrieb:
Werd diese version nunmal an diverse Leute zum testen schicken ob die ergebnisse bei denen mmer noch schwanken, bei mir tun sie es auf jeden fall nicht mehr
Schwankungen wird es immer geben, auch auf dem selben System. Bei einer 10er-Testserie mit MIN-Total 7668 und MAX-Total 7910 betrug die Standardabweichung auf meinem System z.B. etwa 78 Zähler, was einer Abweichung von ungefähr 1% entspricht und somit nicht sonderlich relevant ist.