Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

61

05.11.2012, 23:04

Zitat

List<T> Container waren bei meinen Tests erheblich! langsamer als Arrays :/

Ja, ist momentan wirklich vollkommen überflüssig. Ich weiß nicht wofür die die Partikel nachher gebrauchen willst, aber dann könnte es eventuell wichtig sein, dass du neue Partikel einfach so hinzufügen kannst.

Wenn dir an Lowleveloptimierungen liegt, könntest du mal Unsafecode einbauen.
In C# leider sehr schwierig zu erreichen, aber du könntest auch noch versuchen mit SSE Performance rauszuholen. (entweder über Mono.Simd, interop-dll oder C++\CLI)

Zitat

Mit den Threads klappt nicht wie gewollt gerade

Ohne Code und einer präzisen Fehlerbeschreibung kann man da leider nicht viel weiterhelfen.

Zitat

Ist das nicht der richtige Ansatz?

Ansatz vielleicht schon, aber weshalb denn eine Warteschlange?
Eine einfaches Array oder List-Objekt würde es doch auch tun und wäre wesentlich flexibler.

62

06.11.2012, 08:29

Hi, danke für die Tipps!
Es scheint jetzt zu gelingen mit der Engine. Zumindest das Wurzel-ziehen, dass als Platzhalter für Physik-Berechnungen steht geht scheller wenn ich mehr als einen Thread initiiere.

Esrtmal wird die Engine in einem eigenen Thread gestartet:

C#-Quelltext

1
2
3
Engine Engine = new Engine();
Thread EngineThread = new Thread(new ThreadStart(Engine.Start));
EngineThread.Start();



Die Engineklasse sieht wie folgt aus.

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class Engine
    {
        const int InitThreads = 8;
        int FinishedThreads = 0;
        int Frame;
        bool RunEngine = false;

        private List<Thread> Threads;

        public void Start()//Startet die Engine
        {
            Threads = new List<Thread>();

            if (RunEngine)
                return;
            else
            {
                Frame = 0;
                RunEngine = true;
                EngineLoop();
            }
        }

        public void Cancel()//Schließt die Engine
        {
            RunEngine = false;
        }

        private void EngineLoop()
        {
            while (RunEngine)//Hier ist die Dauerschleife für die Engine
            {
                FinishedThreads = 0; //Kein Thread ist fertig
                for (int i = 0; i < InitThreads; i++)
                {
                    Thread temp = new Thread(PhysicalSimulation); //Alle threads starten
                    Threads.Add(temp);
                    temp.Name = String.Format("Thread [{0}]", i);
                    Console.WriteLine("START: {0}" , i.ToString());
                    temp.Start(i);
                }
                while (FinishedThreads < InitThreads) //Auf alle Threads warten
                    System.Threading.Thread.Sleep(1);

                Threads.Clear();//Frame  fertig, Liste muss sauber werden.

                Frame++;
                Console.WriteLine("==== Frame: {0} Finished ====", + Frame);
            }
            Console.WriteLine("Exit Engine");
        }


        private void PhysicalSimulation(Object o)//Wird später Partikelphysik berechnen
        {
            int n;
            n = (int)o;
            //CountSeparate[n] = 0;

            for (int i = 0; i < (10000000 / InitThreads); i++)
            {
                double d = Math.Sqrt(100000000000); //Irgendwas tun!
                //Die Wurzel wird hier noch ganz oft gezogen, damit es etwas dauert!
            }
            FinishedThreads++; //Fertige Threads hochzählen
            Console.WriteLine("    END: {0}" , n.ToString());
        }
    }

Die Ausgabe in der Console sieht wie folgt aus:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
START: 0
START: 1
START: 2
START: 3
END: 2
END: 1
START: 4
END: 0
START: 5
END: 3
START: 6
END: 4
START: 7
END: 5
END: 6
END: 7
==== Frame: 3 Finished ====

(usw...)

Sind in der Engine Klasse grobe Fehler? Sind überhaupt Fehler oder ist es alles perfekt :D
Was sagen die Programmierer dazu?

Ich würde sonst beginnen die Partikelsimulation zuzufügen.

LG
Bilder zu meinem Projekt: ParSim

Dieser Beitrag wurde bereits 11 mal editiert, zuletzt von »Horus« (06.11.2012, 08:52)


Sylence

Community-Fossil

Beiträge: 1 663

Beruf: Softwareentwickler

  • Private Nachricht senden

63

06.11.2012, 10:57

Du erzeugst jeden Frame alle Threads neu. Das ist unnötig und kostet Zeit.
Erstelle die Threads am Anfang und lass sie bis zum Ende des Programms laufen. Wenn sie etwas zu tun haben, dann machen sie das, ansonsten warten sie einfach darauf, dass sie etwas zu tun bekommen.
Einfacher wäre da natürlich, wenn du direkt den ThreadPool benutzt.

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

64

06.11.2012, 11:20

Sehr wichtige Antwort von Sylence.
Threads zu erzeugen ist sehr teuer und zerstört bei solch "kleinen" Problemen jeglichen Vorteil, den man durch die mehreren Threads hätte.
Außerdem solltest du dir dringend OpenMP anschauen, damit brauchst du dich nicht um die technischen Details zu kümmern. Visual C++ unterstützt OpenMP.

65

06.11.2012, 11:49

Zitat

Das ist unnötig und kostet Zeit.

Gott sei dank, ich hatte "schlimmeres" erwartet :D

Ich versuche das gleich als nächstes, die Threads warten zu lassen, statt sie neu zu erstellen

Mal sehen das mit dem Threadpool könnte was sein. Vielleicht implementiere ich sowas in der Art auch selbst. Ich könnte es genau an die Ansprüche anpassen.
Denn: für die Engine brauche ich später einen 2. Passus für jeden Frame:
1. Passus Physikberechnung der Partikel
2. Passus Kollisionserkennung

Bei manchen Prozessoren bräuchte ich dann 16 Threads von denen je 8 Aktiv sind, sowie natürlich den übergeordneten Engine Thread, der aber meist eh wartet.


Bei OpenMP frage ich mich gerade ob das für c# geeignet ist? Ich bin auf C# festgelegt, da ich eine Grafik nur mit XNA zustande bringe. :fie:

LG
Bilder zu meinem Projekt: ParSim

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

66

06.11.2012, 11:51

Achso, ich vergaß: du benutzt C#.
Dann gibt es diese nette Klasse für dich.

67

06.11.2012, 12:00

Oh das ist ja wirklich interessant. :)

Ich denke da, wo ich die Zugriffe nicht "sauber" aufteilen kann und es keine Fehler geben darf, also in der Funktion die die Partikel in das Grid "sortriert" werde ich die Parallel Class verwenden.
Ich nehme jetzt mal an, dass es etwas schneller geht, wenn ich aufteilen kann.

LG

EDID
@Sylence
Ich habe es doch mit dem Thread Pool gemacht. :thumbsup:

C#-Quelltext

1
2
3
4
5
6
FinishedThreads = 0;
for (int i = 0; i < InitThreads; i++)
    ThreadPool.QueueUserWorkItem(PhysicalSimulation, i);

while (FinishedThreads < InitThreads)//Warten dass alle fertig werden
    System.Threading.Thread.Sleep(1);
Bilder zu meinem Projekt: ParSim

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Horus« (06.11.2012, 12:38)


68

06.11.2012, 13:09

Es ist irgendwie komisch mit dem Threadpool. Ich habe die Zeit gestoppt...

Wenn ich nur einen Thread habe schwankt die Rechenzeit erheblich! Je nachdem ob ich die Maus bewege oder so.
Ich muss schon 16 Threads benutzen, damit es einigermaßen gleichmäßig wird.
Das Problem ist, dass der nächste Passus erst beginnen kann, wenn der langsamste Thread fertig ist.


Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Enginethread starting
START: 0
    END: 0
All Threads Finished in: 5.662
START: 0
    END: 0
All Threads Finished in: 5.647
START: 0
    END: 0
All Threads Finished in: 2.28
START: 0
    END: 0
All Threads Finished in: 5.678
START: 0
    END: 0
All Threads Finished in: 2.28
START: 0

Exit Signal recieved
    END: 0
All Threads Finished in: 5.725
Exit Engine


Vielleicht also doch keinen Thread Pool? :/

EDID: Gleichmäßiger und erheblich schneller ist die Methode:

C#-Quelltext

1
ThreadPool.UnsafeQueueUserWorkItem()
Bilder zu meinem Projekt: ParSim

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »Horus« (06.11.2012, 13:38)


69

06.11.2012, 18:37

Ich habe jetzt den aufwändigeren Passus der Engine der Engine als Multithread umgesetzt. Ich kann sagen die Leistung hat sich massiv(!) erhöht :thumbsup: Statt 50% CPU Auslastung bekomme ich jetzt 70% CPU Auslastung bei meinem Duocore.
Außerdem positv, es stürzt nichts ab und hängt sich nichts auf. Die Multithread Engine konnte ich fast 1:1 aus meinem Testprojekt übernehmen.

Allerdings scheint es auch Probleme zu geben. Vereinzelte Partikel zeigen Paranormale Activitäten :crazy: Außerdem habe ich bisher nur einen Passus als Multithread. Denjenigen zur Kollisionserkennung. :grumble:

LG
Bilder zu meinem Projekt: ParSim

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Horus« (06.11.2012, 18:48)


70

07.11.2012, 18:24

Ich habe die Tests fortgesetzt. Die CPU Auslastung wird nicht richtig angezeigt. Die Threads sind unsave, vlt. deswegen.

Ich habe herausgefunden, dass man richtig viele Threads nehmen muss um maximale Leistung zu bekommen. Ich teile die Aufgaben pro Frame in 256 unglaublich kleine Teile.

Der ThreadPool bekommt also für jeden Passus jedes Frames 256 UnsafeQueueUserWorkItems. :thumbup: Durch Interlocked und lock() kann ich Zugriffsfehler 100%ig verhindern. :vain:

Momentan will ich noch das Multithreading verbessern.

Ich habe 3 Mal folgende Passage im Hauptthread:

C#-Quelltext

1
2
while (FinishedThreads < Config.InitThreads)//Warten dass alle fertig werden //GEHT SO NICHT 
    System.Threading.Thread.Sleep(1);


Jetzt habe ich es mit System.Threading.CurrentThread.Suspend() probiert. Wollte den Hauptthread mit Abschluss des Letzten der 256 Teilthreads wider aktivieren.
Leider ist der Suspend befehl in C# 4.0 veraltet :grumble:

(Bei 6 ms pro Frame ist eine Sleep Time von ungünstigstens 3 Mal 1 ms ärgerlich!!!) ;(

Eine Alternative gibt es aber nicht so richtig oder? 8|

LG



PS: wenn ich noch einige Verbesserungen abgeschlossen habe lade ich das Projekt vielleicht mal hoch, wenn es interessiert. :)
Bilder zu meinem Projekt: ParSim

Werbeanzeige