Laden...

Regelmäßige Aktualisierung der Anzeige eines Graphen beschleunigen

Erstellt von ill_son vor 11 Jahren Letzter Beitrag vor 11 Jahren 2.708 Views
Thema geschlossen
I
ill_son Themenstarter:in
227 Beiträge seit 2009
vor 11 Jahren
Regelmäßige Aktualisierung der Anzeige eines Graphen beschleunigen

Hallo

Ich habe in meinem Projekt eine Datenübergabe zu realisieren. Ich habe ein Array double Data [4,64]
dessen Inhalt ich an vier Stations übergeben möchte, derart, dass Station i die Daten Data[i, ] erhält.
Im Moment mach ich das so:


private void DAQEventHandler(object sender, EventArgs<double[,]> e)
        {
            for (int i = 0; i < 4; i++)
            {
                if (TestStations[i].IsActive)
                {
                    for (int j = 0; j < e.Data.GetLength(1); j++) 
                        TestStations[i].AddData(e.Data[i,j]);
                }
            }
        }


Meine erste Frage wäre, ob man eine ganze Array-Dimension auf einmal übergeben kann also immer alle 64 Werte pro Station auf einen Ritt als eindimensionales Array (so wie in MatLab möglich. Ich weiß, das ist was ganz anderes), oder geht das nur per Schleife? Ich kann das Datenformat auch nicht ändern, weil die Messkarte die Daten so liefert.
Wenn nun alle Stations aktiv sind, läuft die Anwendung verständlicher Weise ziemlich ruckelig. Ich überlege gerade, wie ich die Datenübergabe in einen eigenen Thread packen könnte, vielleicht mit Background Worker. Wäre das sinnvoll? Kann mir da bitte jemand einen Denkanstoß geben?
Ich muss noch erwähnen, dass die Daten dann in einem Diagramm dagestellt werden, was sicher das eigentliche Probelm, weil rechenintensiv, ist.

Biste Grüße, Alex

Final no hay nada más

1.378 Beiträge seit 2006
vor 11 Jahren

Anstatt eines zweidimensionalen Arrays kannst du dir ja auch ein Array von einem Array halten und diese Array Elemente kannst du dann übergeben:


double[][] data...;

...AddData(data[i]) //so ungefähr.


Man darf dabei aber nicht vergessen dass durch übergabe des ganzen Arrays nur die Referenz auf jenes übergeben wird. D.h. wenn dieses sich später noch verändert, verändert es sich überall wo es übergeben wurde.

Lg, XXX

I
ill_son Themenstarter:in
227 Beiträge seit 2009
vor 11 Jahren

Danke für Deine Antwort. Wie gesagt, das Formal ist leider festgelegt, also double[ , ], weil die API des Messkartentreibers die Daten so liefert. Oder kann ich das konvertieren in das von die vorgeschlagene Format?

Grüße, Alex

Final no hay nada más

L
416 Beiträge seit 2008
vor 11 Jahren

Naja das Übergeben der Werte wird kaum der Flaschenhals sein.
Evtl. kannst du die Anzeige etwas entlasten indem du nur in bestimmten Zeitintervallen aktualisierst (falls die Werte in kurzen Abständen reinkommen).
Ansonsten würde ich mal durch Messungen herausfinden was genau lange dauert und dann dort ansetzen.

I
ill_son Themenstarter:in
227 Beiträge seit 2009
vor 11 Jahren

Hallo Lennart,

momentan lese ich mit 100 Hz Daten aus und schreibe immer 64 Werte. Also aller 64/100 Sekunden. Die Daten gehen in einen ZedGraph Graphen und das, wie gesagt, für vier Graphen. Ich denke, dort ist das Problem. Ich befürchte, dass ich nicht umhinkommen werde, etwas in einen weiteren Thread auszulagern. Zumindest sehe ich im Moment keine andere Lösung, bin aber auch kein Experte. Deshalb schreib ich ja hier. Vielleicht versuch ich auch mal die Datenblöcke zu vergrößern und seltener Daten neu zu schreiben.

Grüße, Alex

Final no hay nada más

P
157 Beiträge seit 2010
vor 11 Jahren

Kann den deine AddData-Methode überhaupt ein Array entgegen nehmen?

I
ill_son Themenstarter:in
227 Beiträge seit 2009
vor 11 Jahren

Im Augenblivk nicht, aber das kann ich ja ändern.

Final no hay nada más

P
157 Beiträge seit 2010
vor 11 Jahren

Naja über unsicheren Code ⚠ kann man die Elemente auf jedenfall herauskopieren in ein double[64] Array, bzw. den entsprechenden Pointer übergeben.

I
ill_son Themenstarter:in
227 Beiträge seit 2009
vor 11 Jahren

Ich würde das ganze lieber in einen anderen Thread auslagern, bevor ich anfange, mit unmanaged Code zumzubasteln, so tief stecke ich nicht in der Materie. Meine Überlegung wäre, in der Station-Klasse einen Background Worker zu implementieren und z.B, das ganze Daten-Array zu übergeben. Die richtigen Daten rauszusuchen kann ja die Station-Klasse dann selber machen. Und die ganze Darstellungsaktualisierung auch gleich noch im BW.
Wäre das in Ordnung so, oder doch besser anders?

Grüße, ALex

Final no hay nada más

P
157 Beiträge seit 2010
vor 11 Jahren

Verständlich, auch wenn das nicht viel wäre:
(hab bisher auch nicht mit unsicherem Code gearbeitet)


            unsafe
            {
                // d ist double[10,10]
                // fixed, andere Möglichkeit, um an p zu kommen, kenn ich nicht...
                // irgend etwas mit Marshal glaub ich...
                fixed (double* p = d)
                {
                    double* pUnfixed = p;
                                 // statt 10 -> i*array.GetLength(1)
                    pUnfixed+= 10;

                    double[] test = new double[10];                    
                    // in ein entsperechendes Array kopieren
                    Marshal.Copy(new IntPtr(pUnfixed),  test,0, 10);                    
                }
            }
        }

C
2.121 Beiträge seit 2010
vor 11 Jahren

Du sagst ja selber bereits im ersten Post

Ich muss noch erwähnen, dass die Daten dann in einem Diagramm dagestellt werden, was sicher das eigentliche Probelm, weil rechenintensiv, ist.

Dann ist die Art der Übergabe doch nebensächlich.
Ich würde mir überlegen ob wirklich eine Grafik nützlich ist, die sich 100 mal pro Sekunde ändert. Setz das Intervall für die AKtualisierung rauf, dann entlastest du das ganze spürbar.

1.552 Beiträge seit 2010
vor 11 Jahren

Hallo,

64 Werte pro Sekunde zu schreiben ist sicherlich nicht der Flaschenhals. Du wirst sicherlich auch keine Probleme haben, wenn du anstelle eines Arrays "normale" Komplexe Klassen erstellst. Du kannst ja mal versuchen eine Million Objekte dieser Klasse zu instanziieren und zu messen wie lange dies dauert. Auf jeden Fall keine Sekunde. Dadurch kannst du dir das fehleranfällig Arbeiten mit den Arrays sparen.

Chilic jedoch sagt das einzig richtige: Mit anderen Worten: Das menschliche Auge nimmt Animationen schon ab 17Hz wahr. Also sind meiner Meinung eine Aktualisierung der GUI alle 50ms sinnvoll.

Gruß,
Michael

Mein Blog
Meine WPF-Druckbibliothek: auf Wordpress, myCSharp

I
ill_son Themenstarter:in
227 Beiträge seit 2009
vor 11 Jahren

Hallo,

Danke für eure Antworten.
@ chilic: im Moment aktualisiere ich ja nur aller 0,64 Sekunden. Cer Code pro Station sieht etwa so aus (hab das Projekt jetzt nicht hier):

Es gibt ein GraphPane eines ZedGraphs, das eine Referenz einer PointPairListe enthält.
Pro event passiert 64 mal ein List.Add(doublex, double y).
Erst wenn alle Werte in der Liste sind, findet ein Refresh (Neuzeichnen) des Graphen. Man merkt jedenfalls deutlich, wie die Anwendung mit jeder aktiven Station ruckeliger wird.

@xxMUROxx: Ich werde morgen mal gucken, was genau das rechenintensive ist. Ich vermute das Neuzeichnen des Graphen.

@ PPK: Wenn es wirklich die Datenübergabe ist, werde ich Deinen Vorschlag mal testen.

Final no hay nada más

16.835 Beiträge seit 2008
vor 11 Jahren

Wieso zeichnest Du - wenn moeglich - nicht nur die geaenderten Punkte neu? Wird schneller sein.
Deine Zeiten mit 0,64 Sekunden wirst Du auch nicht perfekt einhalten koennen.
A) Windows kein Echtzeitsystem B) Alle Timer sind ungenau C) Braucht die Ausfuehrung auch ihre Zeit.

Wenn es ein zeitlicher Graph ist wuerde ich die neuen Punkte immer rechts anfuegen und links dann eben wieder rauswerfen.

I
ill_son Themenstarter:in
227 Beiträge seit 2009
vor 11 Jahren

Die Zeit ist nicht so wichtig. Sie wird außerdem von der Messkarte generiert. Die löst über den Treiber das Event aus, wenn die Daten erfasst sind. Das ist aber eigentlich nebensächlich.

Ich weiß nicht, ob ZedGraph dieses Feature bietet, nur die neuen Punkte anzuhängen. Im Prinzip mache ich das ja. Ich hänge die neuen Daten an die Liste an, deren Referenz der Graph enthält. Trotzdem muss man nach dem Anhängen der Daten mit Refresh() oder Invalidate() den Graphen neu zeichnen. Das geht meiner Meinng nach nicht anders. Wie gesagt, ich muss erst mal schauen, wo sich der Flaschenhals befindet.

Grüße, Alex

Final no hay nada más

C
2.121 Beiträge seit 2010
vor 11 Jahren

Was genau meinst du mit "ruckelig"?
Das komplette Neuzeichnen des Grafen besteht ja aus einer Ersetzung des alten Bilds durch das neue. Bereits das könnte ein Ruckeln auslösen, z.B. wenn erst mal der Hintergrund weiß übermalt wird und man dieses weiß kurz sieht, bis der Rest wieder drauf gemalt ist.

Hinweis von herbivore vor 11 Jahren

Wie gesagt, ich muss erst mal schauen, wo sich der Flaschenhals befindet.

Eben, und solange das nicht geklärt ist, macht es auch keinen Sinn weiter zu spekulieren. Solche Spekulationen sind es, die zu solch unnötig langen Threads führen, die am Ende keinem nutzen.

Ansonsten sei noch gesagt, dass die Anzeige und Aktualisierung in den GUI-Thread gehören und im Zweifel die anderen Operationen ausgelagert werden müssen. Jedenfalls ist es nicht sinnvoll und eigentlich auch nicht möglich, die GUI-Anzeige in Threads auszulagern, siehe [FAQ] Warum blockiert mein GUI? und [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke).

Thema geschlossen