Laden...

Wie bekomme ich ein akkurates Messergebnis für die Dauer eines Screenshots?

Erstellt von PierreDole vor 4 Jahren Letzter Beitrag vor 4 Jahren 1.793 Views
P
PierreDole Themenstarter:in
74 Beiträge seit 2017
vor 4 Jahren
Wie bekomme ich ein akkurates Messergebnis für die Dauer eines Screenshots?

Moin,
ich wollte wissen, wie lange C# für die Erstellung eines Screenshot braucht. Dafür habe ich folgenden Test geschrieben:

 private long CreateScreenshot(int width, int height)
        {
            Bitmap bmp = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(bmp);

            this.stopwatch.Reset();
            this.stopwatch.Start();

            g.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(width, height));

            this.stopwatch.Stop();
            
            g.Dispose();

            return stopwatch.ElapsedMilliseconds;
        }

        private float Measurement(int width, int height)
        {
            float t = 0;

            for(int i = 0; i < 100; i++)
            {
                t += CreateScreenshot(width, height);
            }
            return t / 100;
        }

        private void StartButton_Click(object sender, RoutedEventArgs e)
        {
            this.TextOutput.Text = "1 x 1 - generated in: " + Measurement(1, 1) + "ms\n";
            this.TextOutput.Text += "1 x 2 - generated in: " + Measurement(1, 2) + "ms\n";
            this.TextOutput.Text += "10 x 10 - generated in: " + Measurement(10, 10) + "ms\n";
            this.TextOutput.Text += "100 x 100 - generated in: " + Measurement(100, 100) + "ms\n";
            this.TextOutput.Text += "1000 x 1000 - generated in: " + Measurement(1000, 1000) + "ms\n";
        }

Da die Länge schwankt, habe ich die Tests jeweils 100 Mal durchlaufen lassen und dann den Durchschnittswert ermittelt.

Das Ergebnis:
1 x 1 - generated in: 15,98ms
1 x 2 - generated in: 15,97ms
10 x 10 - generated in: 16ms
100 x 100 - generated in: 16ms
1000 x 1000 - generated in: 19,56ms

Das kam mir etwas zu langsam vor. Habe als nächstes Thread.Sleep(10); in die for-Schleife getan um zu schauen, ob sich was am Ergebnis ändert.


            for(int i = 0; i < 100; i++)
            {
                t += CreateScreenshot(width, height);
                Thread.Sleep(10);
            }

Ergebnis:
1 x 1 - generated in: 5,53ms
1 x 2 - generated in: 5,66ms
10 x 10 - generated in: 5,59ms
100 x 100 - generated in: 5,49ms
1000 x 1000 - generated in: 20,85ms

Ok, sieht schon besser aus. Wird es noch schneller, wenn ich den Thread 20ms schlafen lasse?

Ergebnis mit 20ms:
1 x 1 - generated in: 12,22ms
1 x 2 - generated in: 12,2ms
10 x 10 - generated in: 12,22ms
100 x 100 - generated in: 12,19ms
1000 x 1000 - generated in: 23,05ms

An diese Stelle war ich schon etwas überrascht, da ich mit gleichbleibenden oder gar kürzeren Zeiten gerechnet habe. Habe den Test natürlich mehrfach wiederholt und bis auf kleine Schwankungen in den Nachkommastellen, tat sich nichts.

Aber gut, schauen wir mal, wie es mit 5ms aussieht:
1 x 1 - generated in: 10,5ms
1 x 2 - generated in: 10,5ms
10 x 10 - generated in: 10,56ms
100 x 100 - generated in: 10,43ms
1000 x 1000 - generated in: 24,43ms

Schneller als mit 20ms, aber langsamer als mit 10ms. Die Frage aller Fragen lautet jetzt: Wie bekomme ich ein akkurates Meßergebnis hin? 😃

Und dann wäre noch eine Sache mit dem Garbage Collector. Nach ca 13 Sekunden war der Testdurchlauf vorbei. Auf dem Screenshot im Anhang sieht man, daß der GC erst nach ca 53 Sekunden aufgeräumt hat. Das sind 40 Sekunden. Ist das normal? Ich meine, da ist fast ein 1GB im Speicher, der nicht gebraucht wird und der GC braucht 40 Sekunden um aufzuräumen. Kommt mir etwas lang vor.

16.807 Beiträge seit 2008
vor 4 Jahren

Aussagekräftig Performancewerte sind in jeder Sprache nur im Release Modus ohne Debugger möglich.

Und ja, das Verhalten des GC ist normal - und auch gut so.
Bitte schau dir mal an, wie er funktioniert; dann ist dir das recht schnell klar 😉

P
PierreDole Themenstarter:in
74 Beiträge seit 2017
vor 4 Jahren

In Release Modus ändern sich die Ergebnisse auch nur etwas in den Nachkommastellen.

Mir kam aber die Idee Thread.Sleep zu umgehen und das ganze mit einem Timer zu versuchen. Dieses Mal habe ich nur einen 1p x 1p Screenshot zum Testen genommen, da es sonst etwas zu lang dauern würde.

DispatcherTimer timer = new DispatcherTimer();
        float eTime = 0f;
        float eTimeMin = float.MaxValue;
        float eTimeMax = 0f;
        float timerTicks = 0f;

        public MainWindow()
        {
            InitializeComponent();

            timer.Interval = TimeSpan.FromMilliseconds(50);
            timer.Tick += OnTimerTick;
        }

        private long CreateScreenshot(int width, int height)
        {

            Bitmap bmp = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(bmp);

            this.stopwatch.Reset();
            this.stopwatch.Start();

            g.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(width, height));

            this.stopwatch.Stop();

            g.Dispose();

            return stopwatch.ElapsedMilliseconds;
        }

        private void StartButton_Click(object sender, RoutedEventArgs e)
        {
            timer.Start();
        }

        private void OnTimerTick(object sender, EventArgs e)
        {
            this.timerTicks++;

            float tmpTime = CreateScreenshot(1, 1);
            this.eTime += tmpTime;

            if(this.eTimeMin > tmpTime)
                this.eTimeMin = tmpTime;

            if(this.eTimeMax < tmpTime)
                this.eTimeMax = tmpTime;


            this.TextOutput.Text = "Minimum: " + this.eTimeMin + "ms\nAverage: " + Math.Round(this.eTime / this.timerTicks, 2) + 
                "ms\nMax: " + this.eTimeMax + "ms\nTimerTicks: " + this.timerTicks;

            if(timerTicks == 1000)
                timer.Stop();
        }

Die Ergebnisse sind jetzt alle fast gleich.

5ms:
Minimum: 1ms
Average: 9,8ms
Max: 19ms
TimerTicks: 1000

10ms:
Minimum: 1ms
Average: 9,74ms
Max: 19ms
TimerTicks: 1000

20ms:
Minimum: 1ms
Average: 9,94ms
Max: 19ms
TimerTicks: 1000

50ms:
Minimum: 1ms
Average: 9,91ms
Max: 20ms
TimerTicks: 1000

Und noch zum Schluß noch mit einem 100x100 Screenshot und im 10ms Intervall:
Minimum: 1ms
Average: 9,96ms
Max: 20ms
TimerTicks: 1000

Das ganze jetzt nochmal in C++. 😉 Und am liebsten auch noch im Assembler, den kann ich aber nicht. 😁

16.807 Beiträge seit 2008
vor 4 Jahren

Du wirst mit Timer keine genauen werte bekommen, weil Timer vom OS her schon selbst nicht genau sind.
Schau dir Mal Benchmark.NET an.

Damit validieren auch Microsoft und Open Source Projekte ihre Performance.

Es macht auch wenig Sinn C# mit C++ (also Sprachen) mit unterschiedlichen Bibliotheken zu testen.
Wenn dann musst Du unterschiedliche Bibliotheken in einer Umgebung testen oder immer die gleiche Bibliothek in allen Umgebungen.
So hast Du einen Mix aus allem.

Das Gesamtbild was Du da vor hast ist mir unklar.
Sprachen vergleicht man ja schließlich nicht nur im Bezug auf die Performance von einem Minifall.

Soll das mehr ne Übung sein, oder was soll Dein Fazit für ein Ziel haben?

P
PierreDole Themenstarter:in
74 Beiträge seit 2017
vor 4 Jahren

Bin über meinem Projekt zu Screen Scraping gekommen. Naja, das Ermitteln von Informationen mittels Screenshoting hat mein Programm ziemlich verlangsamt. Jetzt will einfach aus Neugier wissen, wie viel schneller es in C++ ginge, also quasi ob diese Methode überhaupt irgendwo taugt.
Vom Assembler weiß ich, daß er der Schnellste ist. Von daher, da ein Vergleichswert zu bekommen, wäre auch ganz nett.
Daß das ganz nicht 100% genau ist, ist nicht weiter schlimm. Es soll nur als ein Richtwert dienen.

16.807 Beiträge seit 2008
vor 4 Jahren

Also wenn Du den Äpfel-Birnen Vergleich weiter machen willst, dann nimm Benchmark.NET oder einen Profiler.
Nicht dieses Timer-gebastle. Weil so ein gebastle gibt .NET den Ruf an gewissen Dingen langsam zu sein.

CopyFromScreen (und damit GDI++) ist nicht gerade für seine Performance bekannt - war auch nicht das Hauptziel dieser Methode.
GDI++ wird über .NET, C++ oder Assembly immer gleichlangsam bleiben.

Daher hab ich vorher auch schon nach dem Fazit gefragt; weil hier ist das Fazit allein aufgrund Deiner herangehensweise einfach vorprogrammiert.
.NET wird den Vergleich verlieren - aber nicht weil es .NET ist, sondern weil Du halt nicht ordentlich vergleichst.

Das liegt aber nicht an .NET, sondern einfach an der Art und Weise wie Du den Screenshot/das Capturing machst.

Deine Frage sollte sein: wie erstellst Du in .NET sehr schnell ein Screen Capturing?
Und CopyFromScreen wird mit Sicherheit nicht Bestandteil der Antwort sein - sondern sehr wahrscheinlich ein Direktzugriff, wie man es auch in C++ machen würde.

5.657 Beiträge seit 2006
vor 4 Jahren

Vom Assembler weiß ich, daß er der Schnellste ist. Von daher, da ein Vergleichswert zu bekommen, wäre auch ganz nett.

Wenn du es weißt, dann hast du doch einen Vergleichswert, oder? Ich verstehe deine Beiträge alle nicht wirklich...

Wie auch immer du den Inhalt des Bildschirmspeichers kopierst, die eigentliche Arbeit wird durch den Grafiktreiber durchgeführt. Es kommt also mehr darauf an, wie effizient der Treiber und die Hardware arbeiten, als wie du diese Funktionen aufrufst.

Weeks of programming can save you hours of planning

P
PierreDole Themenstarter:in
74 Beiträge seit 2017
vor 4 Jahren

Wie gesagt, ich möchte herausfinden, ob die Methode mit Screenshots Informationen zu sammeln überhaupt praktikabel ist. Ich kann mir schon denken, daß es in C++ schneller geht, nur wie viel schneller? Reicht es, um diesen Ansatz zu verfolgen, oder würde er auch in C++ scheitern?

Benchmark.NET schaue ich mir gerne an.

Ob es andere Möglichkeiten gibt, in C# Screenshots zu erstellen, weiß ich nicht. Habe mir viele Beispiele angeschaut, da ich es nicht mit der Bitmap-Klasse machen wollte, fand aber nur diesen einen Weg.

16.807 Beiträge seit 2008
vor 4 Jahren

Ich kann mir schon denken, daß es in C++ schneller geht, nur wie viel schneller?

Das hat kein Mensch gesagt; und ist auch nicht zwangsläufig der Fall.
C++ ist nicht immer schneller. Les mein letzten Beitrag.

Alleine die technische Basis beider Sprachen (c++ vs. C#) lässt einen einfachen, simplen Vergleich von Performance gar nicht zu.
Befass Dich mal mit den Grundlagen dazu, weil Dein Vergleich ist einfach ziemlicher Käse 😃

Ob es andere Möglichkeiten gibt, in C# Screenshots zu erstellen, weiß ich nicht. Habe mir viele Beispiele angeschaut, da ich es nicht mit der Bitmap-Klasse machen wollte, fand aber nur diesen einen Weg. Google-Suche nach c# fast screencapture zeigt viele Wege ohne CopyFromScreen.