Parallel.For Abbruch

  • VB.NET

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von exc-jdbi.

    Parallel.For Abbruch

    Hallo Community

    Ich muss eine ellenlange Berechnung machen die ungefähr 5 - 10 sec dauert. Damit das schneller durchgerechnet wird verwende ich jetzt eine Parallel.For.

    Ich hab hier mal ein synonymes Beispiel zusammengebastelt für die Berechnung einer Null-Stelle. Ich weiss ich könnte auch mathematische Werkzeuge wie Newton etc dafür verwenden, möchte aber mein eigentliches Berechnungsproblem mit der Parallel.For gelöst haben.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Option Explicit On
    3. Imports System.Threading.Tasks
    4. Public Module Module1
    5. Public Sub Main()
    6. Dim _min = -2, _max = 2
    7. Dim root = FindRoot(_min, _max, 100000)
    8. End Sub
    9. Function FindRoot(min As Int32, max As Int32, scale As Int32) As Double
    10. Dim _abbort = False
    11. Dim result As Double = 0
    12. Dim cnt = scale * (max - min)
    13. Parallel.For(1, cnt,
    14. Sub(i, state)
    15. Dim y = CalcFunc(min + i / scale, _abbort)
    16. If y <= 0 Then
    17. state.Stop()
    18. result = min + i / scale
    19. End If
    20. End Sub)
    21. Return result
    22. End Function
    23. Function CalcFunc(x As Double, ByRef _abbort As Boolean) As Double
    24. If _abbort Then Return 0
    25. Return 1 + 1 / (2 * x) + 1 / (3 * x) + 1 / (4 * x) + 1 / (5 * x)
    26. End Function
    27. End Module

    Sobald die Berechnung y <= 0 liefert, soll komplett abgebrochen werden. Das macht das Programm auch, nur irgendwie werden da weitere Berechnungen gemacht die noch in der Pipeline sitzen, denn schlussendlich ist das Resultat bei mir immer result = -1.


    Wie kann ich das beheben?

    Danke für die Hilfe

    exc-jdbi
    @exc-jdbi Dein Code nach WinForm und los:

    Mit Exit Sub statt Stop kommt 0 raus.
    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!
    @RodFromGermany

    Danke für die Antwort.

    Komisch, bei dir scheint das zu funktionieren. Bei mir leider nicht. Er liefert mir ständig das "result = -1".

    Mit state.Break() funkst es bei diesem Beispiel, jedoch bei meinem Projekt muss ich die Berechnungen abbrechen mit "_abbort = true". Er liefert mir aber da auch ein falsches Ergebnis.

    Freundliche Grüsse

    exc-jdbi
    @exc-jdbi Nun ist das aber eine Iteration, und als solche baut der neue Wert auf dem alten auf und der Abbruch erfolgt, wenn ein Kriterium erfüllt ist.
    In diesem Sinne ist Parallel.For() die falsche Herangehensweise.
    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!
    Ich denke du hast wahrscheinlich recht.

    Hab jetzt gedacht ich könne das mit ner Parallel.For lösen.

    Multi-Threading wäre doch wiederum übertrieben? Etwas Paralleles wäre schon genial, denn mit etwa 100'000 verschiedenen Berechnungen gleichzeitig würde die Berechnung bestimmt weniger als 1 Sekunde brauchen.
    @exc-jdbi Wobei sich die Frege erhebt:
    Ist der Algorithmus parallelisierbar?
    Ich hab n Problem bei Bildverarbeitung gehabt, das lisß sich genial mit Parralel.For lösen.
    Bei einer 2D-FFT wirds schon tricky, da hab ich die Sinus- und Cosinus-Arrays geclont.
    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!
    Ja. Die Berechnungen sind eigenständig, wie zum Beispiel bei einer Spektrum-Berechnung. Jede einzelne Berechnung baut auf irgendwelche Parameter (kann ich wählen) und sofern das Resultat ein bestimmtes Kriterium erfüllt, besteht eine Lösung. Und sofern 1 Lösung vorhanden ist, werden die anderen Berechnungen nicht mehr gebraucht, auch wenn sie das Kriterium gleichzeitig erfüllen würden.

    Edit: Habs jetzt so gelöst
    docs.microsoft.com/de-de/dotne…ion-to-plinq#cancellation

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „exc-jdbi“ ()

    exc-jdbi schrieb:

    Habs jetzt so gelöst
    Mit welchem Code?
    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!
    Hab das ungefähr so gemacht. Gibt's noch ne bessere Möglichkeit? Eventuell eleganter?
    Kannst auch in C# schreiben versteheh ich bestens :rolleyes:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. ​Sub Sample2()
    2. Dim _min = -2, _max = 2
    3. Dim root = FindRoot2(_min, _max, 100_000)
    4. Console.WriteLine(root)
    5. Console.ReadLine()
    6. End Sub
    7. Function FindRoot2(min As Int32, max As Int32, scale As Int32) As Double
    8. Dim _abbort = False
    9. Dim result As Double = 0
    10. Dim cnt = scale * (max - min)
    11. Dim nums = Enumerable.Range(1, cnt)
    12. Dim cts As New CancellationTokenSource()
    13. Dim token As CancellationToken = cts.Token
    14. Dim query = nums.AsParallel.WithCancellation(token)
    15. Dim t = Task.Factory.StartNew(
    16. Sub()
    17. Try
    18. query.ForAll(
    19. Sub(i)
    20. If token.IsCancellationRequested() Then
    21. Return
    22. End If
    23. Dim y = CalcFunc(min + i / scale, _abbort)
    24. 'Ist nur für das Beispiel, um einigermassen
    25. 'einen guten Wert zu bekommen.
    26. Dim validrange = -1 / 3 / scale
    27. If y <= 0 AndAlso y > validrange Then
    28. cts.Cancel()
    29. _abbort = True
    30. result = min + i / scale
    31. End If
    32. End Sub)
    33. Catch tce As TaskCanceledException
    34. Catch oca As OperationCanceledException
    35. Catch ex As Exception
    36. End Try
    37. End Sub, token)
    38. t.Wait()
    39. If Not token.IsCancellationRequested Then
    40. cts.Cancel()
    41. End If
    42. cts.Dispose()
    43. Return result
    44. End Function
    45. Public Function CalcFunc(x As Double, ByRef _abbort As Boolean) As Double
    46. If _abbort Then Return 0
    47. 'Ist nur ein Beispiel:
    48. 'Die Berechnungen sind um einiges komplexer, und vor allem
    49. 'rechenintensiver. Abbruch muss vorhanden sein.
    50. 'Sobald _abbort = true werden alle Berechnungen abgebrochen
    51. Return 1 + 1 / (2 * x) + 1 / (3 * x) + 1 / (4 * x) + 1 / (5 * x)
    52. End Function
    @exc-jdbi C': Habs grade mal versucht zu übertragen, wollte nicht so richtig.
    Wenn Du es vorrätig hast, poste es mal bitte.
    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!
    Wahrscheinlich liegts an der Double-Berechnung in Zeile 23

    C#-Quellcode

    1. var x = min + ((double)i / scale);
    2. var y = CalcFunc(x, ref _abbort);


    Und die Funktion in Zeile 41

    C#-Quellcode

    1. private static double CalcFunc(double x,ref bool _abbort)
    2. {
    3. if (_abbort) return 0.0;
    4. return 1.0 + 1.0 / (2.0 * x) + 1.0 / (3.0 * x) + 1.0 / (4.0 * x) + 1.0 / (5.0 * x);
    5. }


    Spoiler anzeigen

    C-Quellcode

    1. public static void Main(string[] args)
    2. {
    3. Sample();
    4. }
    5. private static void Sample()
    6. {
    7. int _max = 2;
    8. int _min = -2;
    9. var root = FindRoot(_min, _max, 100_000);
    10. Console.WriteLine(root);
    11. Console.ReadLine();
    12. }
    13. private static double FindRoot(int min,int max,int scale)
    14. {
    15. var _abbort = false;
    16. var result = 0.0;
    17. var cnt = scale * (max - min);
    18. var cts = new CancellationTokenSource();
    19. var nums = Enumerable.Range(1, cnt);//.ToArray();
    20. var token = cts.Token;
    21. //var query = Partitioner.Create(nums, true).AsParallel().WithCancellation(token);
    22. var query = nums.AsParallel().WithCancellation(token);
    23. var lst = new List<int>(500000);
    24. var t = Task.Factory.StartNew(() =>
    25. {
    26. try
    27. {
    28. query.ForAll((i) =>
    29. {
    30. if (token.IsCancellationRequested)
    31. return;
    32. var x = min + ((double)i / scale);
    33. var y = CalcFunc(x, ref _abbort);
    34. double validrange = -1.0 / 3.0 / (double)scale;
    35. if ((y <= 0) && (y > validrange))
    36. {
    37. cts.Cancel();
    38. _abbort = true;
    39. result = x;
    40. }
    41. lst.Add(i);
    42. });
    43. }
    44. catch (OperationCanceledException) { }
    45. catch (Exception) { }
    46. },token);
    47. t.Wait();
    48. if (!token.IsCancellationRequested)
    49. cts.Cancel();
    50. cts.Dispose();
    51. return result;
    52. }
    53. private static double CalcFunc(double x,ref bool _abbort)
    54. {
    55. if (_abbort) return 0.0;
    56. return 1.0 + 1.0 / (2.0 * x) + 1.0 / (3.0 * x) + 1.0 / (4.0 * x) + 1.0 / (5.0 * x);
    57. }

    Freundliche Grüsse

    exc-jdbi
    @exc-jdbi Jetzt wird es lustig:
    Dein Code in WinForm, Button und Label
    ====
    Studio 2013
    VB => -1.28333
    CS => -1.99997
    CS neu erstellt => -1.28333
    CS neu erstellt => -1.99997
    ====
    Studio 2017
    VB => -1.99997
    VB neu erstellt => -1.28333
    CS => -1.28333
    CS neu erstellt => -1.28333
    ???
    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!
    Ja die Zahlen sind möglich.

    Der Bereich für If y <= 0 AndAlso y > validrange Then ist für eine Auflösung von cnt = 400000 ziemlich gross. Da müssen nur wie in deinem Falle die Zahlen i anders kommen, und schon gibt es vollkommen andere Werte.

    Im Prinzip habe ich das nicht beachtet. Mir geht es viel mehr um die Parallelisierung.
    Mit If y < 0 AndAlso y > validrange Then sollte es dann immer funktionieren.

    Gruss exc-jdbi

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „exc-jdbi“ ()

    exc-jdbi schrieb:

    Mir geht es viel mehr um die Parallelisierung.
    Nur ist das Grütze, wenn das mathematisch nicht stabil ist.
    Mach das vielleicht am Beispiel von Bildverarbeitung, auch wenn es da keinen Grund gibt, Parallel.For abzubrechen.
    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!
    @RodFromGermany

    Ich werde mir die Bildverarbeitung nächste Woche mal anguggen, wenn die Zeit reicht. Kann mir schon vorstellen, dass das ziemlich tricky ist. Zwischenzeitlich lass ich die Parallelisierung so wie ich es oben gemacht habe. Ich denke so schlecht kann die nicht sein.

    Danke dir für die Hilfestellung. :thumbsup:

    Freundliche Grüsse

    exc-jdbi

    exc-jdbi schrieb:

    Kann mir schon vorstellen, dass das ziemlich tricky ist.
    Im Prinzip ist das das Anwenden von Operatoren auf einen Datensatz.
    Wichtig: Der Operator macht aus einem Input-Bild ein Output-Bild, das Input-Bild wird dabei nicht verändert!
    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!
    @exc-jdbi Habich keinen vorrätig. Ein Schlagwort wäre Operatoren der Bildverarbeitung.
    Im Prinzip gehst Du mit einbem 3x3, 5x5, 7x7 Pixel-Quadrat über ein Bild und rechnest aus diesen den neuen Zielpunkt aus.
    Beispiel: Medianfilter, sortiere die 9, 25, 49 Pixel der Größe nach und nimm das mittelste Pixel als Output-Wert.
    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!
    Ich denke das kann ich schon mal gebrauchen.

    Vielleicht ein paar Worte, was ich jetzt gemeint habe. Leider kenne ich mich noch ein bisschen zu wenig aus.

    Interessant so auf dem ersten Blick hört sich für mich das "Aufsplitten von Verfahrenslösungen". Also Werkzeuge erschaffen, die Berechnungsverfahren detaillierter betrachten. Ich denke genau in dem hat das Parallelisieren potential.

    Ich weiss auch nicht, ob ich die Motivation dazu habe, mich soweit einzuarbeiten. Interessant wäre es sicher.
    Ein Ziel könnte sein, mehrere parallele Berechnungsschichten (mit Beachtung der Prozessorauslastung bzw. -kerne) einleiten zu können, sofern das überhaupt möglich ist, um gesamthaft eine schnellere oder im Minimum eine ähnliche Gesamtberechnungsgeschwindigkeit zu erhalten. Eventuell bietet DotNet schon entsprechende Lösungen, und man muss sie nur anwenden können.

    Im Prinzip mache ich das ja schon, in dem ich das Ganze "Funktional" zerlege, was sicher die Grundbasis entspricht. Für die Parallelisierung wäre es aber noch interessant zu Wissen, was für weitere Möglichkeiten es gibt. Das Aufsplitten hätte ja auch zur Folge, dass es verständlicher ist.

    Aber wie schon geschrieben. Ich weiss gar nicht, ob ich mich so weit in das Thema einarbeiten will und kann. Schlussendlich braucht es dazu schon ein ziemlich gutes Verständnis für solche Sachen, nebst Motivation.

    Freundliche Grüsse

    exc-jdbi
    @exc-jdbi Das Problem ist, einmal zu erkennen, ob ein Algorithmus parallelisierbar ist und wenn ja, wie mache ich das.
    Bei der Bildverarbeitung ist das sehr einfach.
    Du liest Pixel aus einem großen Array un ein kleines (oder eben nicht), dabei ist es egal, ob der Parallelprozess ebenfalls auf dassebe Pixel zugreift, da das nur ein Lesen ist.
    Das Schreiben der Resultatpixel iat auch unabhängig von den anderen, da jede Quelle genau ein Ziel-Pixel erzeugt.
    Lustig wird es, wenn da iwie Zufallszahlen benötigt werden, denn die Random-Klasse ist nicht Thread-Save.
    Da müsste man z.B. wissen, wie viele Zufallszahlen benötigt werden und könnte die vorab berechnen und in einem Array ablegen.
    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!