Wie (noch) mehr Speed beim Threading

  • VB.NET

Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Wie (noch) mehr Speed beim Threading

    Dank eurer Hilfe scheinen meine Spielereien mit Permutationen und Threads/Events zu funktionieren.

    Aktuell startet meine Anwendung immer 16 Threads. Im Taskmanager sind es dann aber (aktuell) 26.
    Die CPU (16-Kern Threadripper 2950) wird aber nie mit mehr als 3-4% ausgelastet. Demnach wird
    ein großer Teil der CPU nicht wirklich ausgelastet? Warum ist das so und wie kann man das als
    Programmierer besser ausnutzen?
    Aktuelles Projekt: Z80 Disassembler für Schneider/Amstrad CPC :love:

    oobdoo schrieb:

    wie kann man das als Programmierer besser ausnutzen?
    Wenn das Problem es zulässt, arbeite mal mit Parallel.For() bzw. Parallel.Foreach(), da steuert das System die Auslastung der Kerne.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Bartosz schrieb:

    Kann ein Programm überhaupt 100% der CPU nutzen?
    Das kann schon deswegen nicht gehen, weil das System selbst was benötigt.
    Bei einer Messdatenauswertung mit einem generischen Algorithmus habe ich mal das System ganz schön ausgelastet, das waren weit über 50% und alle Kerne waren recht gleichmäßig bedient.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Die Frage ist doch eher, ob die CPU wirklich der limitierende Faktor ist?
    Ist ja nicht so, als gäb es da noch andere Dinge wie Arbeits-/Datenspeicher, Netzwerk und was auch immer mir gerade nicht einfällt.
    Ach Mist :D ich sollte lesen lernen, hab den Teil mit "Permutationen" überlesen.
    Hm denke das hängt dann stark von deiner Implementierung ab.
    Ich glaube, das Problem kann man so gar nicht allgemein beantworten. Es kommt auf deinen Code darauf an. Wenn er durchgehend auch der CPU arbeiten kann, sollte er eigentlich auch alles benutzen.

    Probier das doch einfach mal mit einem Beispiel selber aus. Hier wird meine CPU direkt zu 100% ausgelastet, also alles was gerade noch übrig ist der Anwendung zugewiesen:

    C#-Quellcode

    1. Parallel.For(0, Int32.MaxValue, (i, state) => {
    2. for (int j = 0; j < Int32.MaxValue; j++) {
    3. Math.Sqrt(i);
    4. }
    5. });


    Wenn das bei deinem Programm nicht der Fall ist, dann ist es vermutlich genauso wie @slice es bereits geschrieben hat. Die CPU wartet gerade auf irgendwas.

    oobdoo schrieb:

    Hab 64GB Hauptspeicher
    Wie sieht denn das auf Hardwareseite aus? Hast du mehrere RAM-Sticks (z.B. 2x32GB) und sie so eingebaut, dass Dual Channel möglich ist? Schlechter/kaputter/nicht optimal eingebauter RAM frisst Performance zum Frühstück.



    Ansonsten: Wenn du mit deiner Anwendung bei Berechnungen 100% Auslastung erreichen willst, musst du auch auf das Caching der CPU achten. „Wirklich“ auf den RAM zuzugreifen ist vergleichsweise teuer, daher versuchen CPUs einzelne Speicherbereiche (genannt „Cache Line“ – für x86 typisch 64 Bytes) im Cache vorzuhalten: Gallery of Processor Cache Effects
    Möglicherweise hat das einen Einfluss auf deinen Anwendungsfall.
    Mit freundlichen Grüßen,
    Thunderbolt
    Ich habe auf jeden Fall ein Problem mit meinem Code. Der Speicherverbraucht der verschalteten FOR-Schleifen geht schnell in die Gigabyte.

    Mein Rechner hat übrigens 2x32GB eingebaut. Mainboard ist ein TaiChi X399 von Asrock von 2019. Dei RAMs sind von Kingston. Bei der ganzen Konfiguration hatten damals einige User vom Forum64 drüber geschaut.
    Aktuelles Projekt: Z80 Disassembler für Schneider/Amstrad CPC :love:
    hihi - wir kommen wieder bei raus, was ich dir in deim anderen Thread auch schon schrieb: Willst du Performance, verbesser deinen Algo.
    Mit Paralellisierung kann man eine Vorgang-Geschwindigkeit vielleicht verdoppeln, oder gar verdreifachen - wennn der Vorgang dafür geeignet ist.
    Mit verbessertem Algo sind Faktoren von 100 oder 1000 denkbar.

    Grade Permutation ist ein sehr kniffliges Thema.
    Wusstest du, dass es "die Permutation" garnet gibt?
    Es sind nämlich allein in der Mathematik 4 verschiedene Standard-Arten zu permutieren definiert: Variation, Kombination, jeweils mit und ohne Wiederholung.
    Hinzu kommt noch ein wichtiger Spezialfall, ohne den die "Variation ohne Wiederholung" nicht effektiv lösbar ist.
    Weitere Spezialfälle lassen sich (glaub sogar beliebig viele) definieren, die keiner dieser Grundformen entsprechen.

    Aber solange du nur von Threading redest, kann man das Thema nicht angehen.

    Der verlinkte Artikel bezieht sich übrigens nur auf einen Fall, den Spezialfall: "Variation ohne Wiederholung: wähle alle aus n".
    Keine Ahnung, ob das die Permutation ist, die du brauchst.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:

    Willst du Performance, verbesser deinen Algo.

    Leichter gesagt als getan. :whistling:

    Ich programmiere möglicherweise länger wie andere hier im Forum als Lebensalter aufweisen können. Das hat aber nicht dazu geführt das ich bei den Antworten immer folgen kann. :(

    Zumindest beim riesigen Speicherverbrauch habe ich wohl die Ursache gefunden.

    Beispiel:

    VB.NET-Quellcode

    1. Sub Schleifen
    2. Dim s as String
    3. For i1=0 to 100
    4. For i2=0 to 100
    5. For i3=0 to 100
    6. s = Array2HashString(MeinArray, Hash.MD5)
    7. Next
    8. Next
    9. Next
    10. End Sub


    VB.NET-Quellcode

    1. Public NotInheritable Class clHash
    2. Public Shared Function Array2HashString(ByRef sHashArray As Byte(), ByVal Hash As Hash) As String
    3. Dim HashValue(0) As Byte
    4. Dim Result As String = ""
    5. Dim Tmp As String = ""
    6. Select Case Hash
    7. Case clHash.Hash.MD5
    8. Dim MD5 As New MD5CryptoServiceProvider
    9. MD5.ComputeHash(sHashArray)
    10. HashValue = MD5.Hash
    11. ' weitere Hash-Dingers
    12. End Select
    13. For i As Integer = 0 To HashValue.Length - 1
    14. Tmp = Hex(HashValue(i))
    15. If Len(Tmp) = 1 Then Tmp = "0" & Tmp
    16. Result += Tmp
    17. Next
    18. Return Result
    19. End Function
    20. End Class


    Diese Kombination vom Ermitteln eines MD5 hat bei mir gigantisch viel Speicher gefressen. Ich hatte dann experimentiert und später den MD5-Code aus der NotInheritable Class in die Klasse mit den For/Next-Schleifen reinkopiert. Da wurde es immer noch nicht besser. Erst als ich das Dim MD5 As New MD5CryptoServiceProvider aus der Sub Array2HashString rausgenommen hatte und direkt in die Klasse mit den For/Next-Schleifen plaziert hatte, war der Spuk zuende.
    Aktuelles Projekt: Z80 Disassembler für Schneider/Amstrad CPC :love:

    oobdoo schrieb:

    war der Spuk zuende.
    na, der "Spuk" ist nicht weiter spooky.
    Tatsächlich muss man genau mit sowas rechnen, wenn man massenhaft Disposable Objekte verwendet - und die nicht disposed.

    Allerdings noch besser, als nun massenhaft Objekte zu erstellen und sie massenhaft wieder zu disposen ist genau was du gemacht hast: Du erstellst nur eines davon, und verwendest es mehrfach.
    Also du kommst mir sehr entgegen, weil meine Rede: Besser deinen Code, dann wirds besser.
    Ich würde sogar vermuten, dass nun auch die Performance sich bessert.

    Insgesamt könntest du aus diesem Thread lernen, die IDisposable-Schnittstelle zu beachten. Wenn du gründlich sein willst, google auch bisserl danach, oder noch besser: lies in einem guten Buch dazu nach.
    Ungründlich kann ich diir auch eine Kurzform liefern:
    Dispose heisst "wegwerfen". Disposable also: "wegwerfbar".
    Klassen, die IDisposable sind dazu vorgesehen, weggeworfen zu werden, wenn nicht mehr gebraucht.
    Jo, das sollte man denn auch machen, sonst läuft man Gefahr, dass immer mehr SystemResourcen belegt und nicht mehr freigegeben werden.
    Bei wenigen solchen Objekten wirkt sich das meist noch nicht so aus, aber wenn in MassenOperationen nicht aufgeräumt wird ist mit Unerfreulichkeiten zu rechnen.

    Ich weiss nu nicht deinen Wissensstand, vielleicht musst du vor dem IDisposable-Pattern auch erst noch lernen, was überhaupt ein Interface (=Schnittstelle) ist.
    Auch das geht gründlich (Buch) oder ungründlich (Forum, etwa dieses:Grundlagen: Fachbegriffe)