Schnellere Funktion als StreamWriter

  • VB.NET

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

    Der Vorteil hier ist, der erste Prozessorkern (Thread1) nur das berechnen erledigen muss und dass danach an die Queue weitergibt.
    der Zweite Kern (Thread2) holt sich die Daten nach der Reihe und schreibt diese direkt in eine Datei.

    Somit behindern sich die beiden nicht. Wobei beides ungefähr gleich schnell läuft (Durchschnittlich über die gesamte Laufzeit gesehen).

    Aber wie gesagt, da lässt sich sicher noch mehr optimieren :D

    Edit: Ich lass das Programm heute nacht nochmal durchlaufen. Mit aktivierter Optimierung, ohne Überprüfung auf Ganzzahlüberlauf, ohne andere Programme laufen und mit ngen vorkompiliert.
    Mal schauen wie schnell es wird :D


    Edit2: Warum er weniger findet ist mir unklar. :S vl stimmt mit einem der beiden Algorithmen etwas nicht.
    Wie hast du denn gezählt?
    SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

    Weil einfach, einfach zu einfach ist! :D

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

    AFAIK macht ihr das quasie so:

    Quellcode

    1. Do
    2. Berechne Zahl
    3. Schreibe Zahl auf Platte
    4. Loop


    ..genau das Thema gab es schonmal: Das beginnen eines Schreibzugriffes braucht seine Zeit. Fängt man für jede paar Bytes neu an, dann kostet es VIEL Zeit^^ Darum ist es besser wenn man nur größere Datenmengen (einige KB sollte schon reichen) aufeinmal schreibt. Es sollte also so aussehen:

    Quellcode

    1. Do
    2. Berechne Zahl
    3. Schreibe Zahl in Buffer
    4. Wenn Buffer = 1000 Zahlen groß, dann: Schreibe Buffer auf Platte
    5. Loop
    6. Schreibe Buffer auf Platte


    Natürlich habe ich das ganze getestet um sicher zu sein, dass ich keinen Blösinn verzapfe^^ Ich habe die Zahlen von 0 bis 10.000.000 binär in eine Datei geschrieben (jede Zahl benötigt 4Bytes weil Integer). Einzeln (Buffergröße = 4 Byte) dauert der vorgang 16,3 Sekunden. Mit einem größeren Buffer (=> 4000Bytes) ist die sache in 3,6 Sekunden fertig. Nochmal: 3,6 Sekunden für 10.000.000 (beliebig große) Integer-Werte! Wie der EDR schon sagte, würde ein BuffredStream mehr Leistung bringen - ich schätze der macht das Intern so ähnlich.

    Im Endeffekt würde ich also sagen, dass das Schreiben recht schnell ist. Selbst wenn es eine Minute dauern würde - fünf Stunden steht da in keinem Verhältnis! Sicher, dass deine Rechenroutine optimiert genug ist?

    lg

    BiedermannS schrieb:

    der Zweite Kern (Thread2) holt sich die Daten nach der Reihe und schreibt diese direkt in eine Datei.

    Humbug, bzw Zeitverschwendung. Wenn man mehr als einen Kern hat, sollten alle Kerne RECHNEN. Schreiben kann man in einem extra TASK machen, da das IO-bound und nicht CPU-bound ist. Dh das schreiben dauert zwar Zeit, aber die CPU dreht die meiste Zeit Däumchen.
    zuallerallererst aber bitte erstmal einen gscheiten Primzahl-Algo entwickeln!
    werden alle prims gesucht, kommt man glaub ums Sieb des Aristophanes nicht herum (naja - ich jdfs. weiß nix anneres).
    Ich glaube auch (habs nicht probiert), damit liesse sich was durchaus ziemlich performantes coden.
    Ich hab mir den Algorithmus auch nur aus dem Netz geholt, läuft aber ziemlich performant.

    Wenn beide kerne rechnen, gibts zwei Möglichkeiten:

    1) Dir ist die Reihenfolge egal, dann passt alles
    2) Die Reihenfolge muss stimmen. Dann müssen die Threads synchronisiert werden. bzw, deren Ausgabe später zusammengeführt werden.

    @picoflop: Humbug würde ich es nicht gleich nennen. Weil sich die einzelnen Aufgaben schon mal nicht mehr gegenseitig ausbremsen. Was einfach einen gewissen Zeitvorteil bringt.

    Wie bereits gesagt, das ganze ist noch optimierungsfähig.
    SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

    Weil einfach, einfach zu einfach ist! :D

    BiedermannS schrieb:

    Weil sich die einzelnen Aufgaben schon mal nicht mehr gegenseitig ausbremsen

    Noch mal: IO, wenn asynchron, "bremst" nicht (bzw kaum)!

    läuft aber ziemlich performant.

    Du prüfst n/2, man kann das ganze aber einfach verbessern

    Sei k ein Element aus N > 0
    dann gilt: Nur (6*k)+1 und (6*k)+5 können überhaupt Prim sein. Denn 6k, 6k+2 und 6k+4 sind Vielfache von 2 und 6k+3 ist ein vielfaches von 3. Dadurch prüft man nicht mehr n/2 sondern nur noch n/3

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „picoflop“ ()

    Ich habs Stichprobenweise auf primzahlen.zeta24.com/de/online_primzahltest.php getestet. Und sie waren alle korrekt.

    Der Algo ist von Dotnet Snippets.de:
    dotnet-snippets.de/dns/effizie…osser-zahlen-SID1373.aspx

    @picoflop: ich hab nur gesagt das es schneller ist, wenn T1 rechnet und T2 schreibt, als wenn das Ganze nacheinander gemacht wird (ohne Threads). Auf das war der Geschwindigkeitsgewinn bezogen

    Edit: Prüfung auf 2 kann ich sowieso weglassen, da ich keine geraden Zahlen hineinschicke....
    SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

    Weil einfach, einfach zu einfach ist! :D

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „BiedermannS“ ()

    Ich habe spaßhalber die Dauer des Speichervorgangs einer Liste gemessen. Das Ergebnis will ich euch nicht vorenthalten:
    Methode:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Dim sw As New Stopwatch
    2. Dim maxValue = 5000000
    3. Dim minValue = 1
    4. Debug.Print(New String("*"c, 70))
    5. Debug.Print("MinValue: {0}", minValue)
    6. Debug.Print("MaxValue: {0}", maxValue)
    7. ' ******************** Variant 1 *******************
    8. Debug.Print(New String("*"c, 70))
    9. Debug.Print("Variant 1 - List (Of String)")
    10. Dim result1 As New List(Of String)
    11. sw.Restart()
    12. For index = minValue To maxValue
    13. result1.Add(index.ToString)
    14. Next
    15. Debug.Print("Variant 1 - Zahlen generieren: {0} Millisekunden", sw.ElapsedMilliseconds)
    16. sw.Restart()
    17. Dim filename = IO.Path.Combine(IO.Path.GetTempPath, "test1.log")
    18. IO.File.WriteAllText(filename, String.Join(vbNewLine, result1.ToArray))
    19. Debug.Print("Variant 1 - Speichern: {0} Millisekunden", sw.ElapsedMilliseconds)
    20. ' ******************** Variant 2 *******************
    21. Debug.Print(New String("*"c, 70))
    22. Debug.Print("Variant 1 - List (Of Integer)")
    23. Dim result2 As New List(Of Integer)
    24. sw.Restart()
    25. For index = minValue To maxValue
    26. result2.Add(index)
    27. Next
    28. Debug.Print("Variant 2 - Zahlen generieren: {0} Millisekunden", sw.ElapsedMilliseconds)
    29. ' To StringBuilder
    30. sw.Restart()
    31. Dim sb As New System.Text.StringBuilder
    32. For index = 0 To result2.Count - 1
    33. sb.AppendLine(result2(index).ToString)
    34. Next
    35. Debug.Print("Variant 2 - Zum StringBuilder: {0} Millisekunden", sw.ElapsedMilliseconds)
    36. sw.Restart()
    37. filename = IO.Path.Combine(IO.Path.GetTempPath, "test2.log")
    38. IO.File.WriteAllText(filename, sb.ToString)
    39. Debug.Print("Variant 2 - Speichern: {0} Millisekunden", sw.ElapsedMilliseconds)
    40. ' ******************** Variant 3 *******************
    41. Debug.Print(New String("*"c, 70))
    42. Debug.Print("Variant 1 - StringBuilder")
    43. Dim result3 As New System.Text.StringBuilder
    44. sw.Restart()
    45. For index = minValue To maxValue
    46. result3.AppendLine(index.ToString)
    47. Next
    48. Debug.Print("Variant 3 - Zahlen generieren: {0} Millisekunden", sw.ElapsedMilliseconds)
    49. sw.Restart()
    50. filename = IO.Path.Combine(IO.Path.GetTempPath, "test3.log")
    51. IO.File.WriteAllText(filename, result3.ToString)
    52. Debug.Print("Variant 3 - Speichern: {0} Millisekunden", sw.ElapsedMilliseconds)
    53. Debug.Print(New String("*"c, 70))

    Ergebnis:
    Spoiler anzeigen

    Für 5.000.000

    Quellcode

    1. **********************************************************************
    2. MinValue: 1
    3. MaxValue: 5000000
    4. **********************************************************************
    5. Variant 1 - List (Of String)
    6. Variant 1 - Zahlen generieren: 1499 Millisekunden
    7. Variant 1 - Speichern: 391 Millisekunden
    8. **********************************************************************
    9. Variant 2 - List (Of Integer)
    10. Variant 2 - Zahlen generieren: 136 Millisekunden
    11. Variant 2 - Zum StringBuilder: 1054 Millisekunden
    12. Variant 2 - Speichern: 220 Millisekunden
    13. **********************************************************************
    14. Variant 3 - StringBuilder
    15. Variant 3 - Zahlen generieren: 1000 Millisekunden
    16. Variant 3 - Speichern: 220 Millisekunden
    17. **********************************************************************


    Für 10.000.000

    Quellcode

    1. **********************************************************************
    2. MinValue: 1
    3. MaxValue: 10000000
    4. **********************************************************************
    5. Variant 1 - List (Of String)
    6. Variant 1 - Zahlen generieren: 2938 Millisekunden
    7. Variant 1 - Speichern: 734 Millisekunden
    8. **********************************************************************
    9. Variant 2 - List (Of Integer)
    10. Variant 2 - Zahlen generieren: 432 Millisekunden
    11. Variant 2 - Zum StringBuilder: 2007 Millisekunden
    12. Variant 2 - Speichern: 469 Millisekunden
    13. **********************************************************************
    14. Variant 3 - StringBuilder
    15. Variant 3 - Zahlen generieren: 1981 Millisekunden
    16. Variant 3 - Speichern: 442 Millisekunden
    17. **********************************************************************

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „xtts02“ ()

    So:

    VB.NET-Quellcode

    1. Public Sub CalcPrime(ByVal max As Long)
    2. Dim sw As New Stopwatch
    3. sw.Start()
    4. Using bw As New StreamWriter(New FileStream("D:\primes.txt", FileMode.OpenOrCreate, FileAccess.Write))
    5. Dim Noprim(max) As Boolean
    6. For i = 2 To max
    7. Noprim(i) = False
    8. Next
    9. For i = 2 To max
    10. If Not Noprim(i) Then
    11. bw.WriteLine(i)
    12. For j = i * i To max Step i
    13. Noprim(j) = True
    14. Next
    15. End If
    16. Next
    17. End Using
    18. sw.Stop()
    19. Debug.Print("fertig: " & sw.Elapsed.ToString)
    20. End Sub


    Dauert es bei 100.000.000 nicht mal ne halbe Minute bei mir.
    Das ist meine Signatur und sie wird wunderbar sein!

    Mono schrieb:

    Dim Noprim(max) As Boolean

    DAS ist halt der Nachteil der "Sieb"-Algorithmen ...
    Man kann das zwar noch eindampfen indem man direkt Bits anspricht, aber irgendwann reicht der Speicher nicht mehr aus.

    Zeigt aber auf jeden Fall, wie der direkt davor stehende Post, dass das SCHREIBEN der Daten nun wirklich kein (zeitliches) Problem darstellt
    Zu der Anzahl der Primzahlen. Ich hab mich in meinem Post mit den Zahlen verschrieben. Es sind genau 5761455 Zahlen.

    Und ich hab die Primzahlen 5 & 7 vergessen :S
    SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

    Weil einfach, einfach zu einfach ist! :D
    ich hab über dein' Algo gehirnt und fund ihn wasserdicht.
    Testet halt jede Zahl mit allen möglichen Modulos.
    Modulo ist eiglich lahm, aber im Durchschnitt sind weniger als 3 Tests erforderlich, um eine Zahl als Prim auszuschließen.

    Das Speicherproblem des Siebs des Eratosthenes konnte ich lösen, und ist sogar 20% schneller als dein Algo, aber wie picoflop schon sagte: die Sieb-Lösung ist nicht parallelisierbar, und hat daher eindeutig verloren.
    Wieso sollte sie nicht parallelisierbar sein?

    Man kann durchaus Threadübergreifend auf Listen zugreifen. Die Frage wäre eher, wie gut bzw. schlecht das funktioniert.
    SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

    Weil einfach, einfach zu einfach ist! :D

    ErfinderDesRades schrieb:

    Das Speicherproblem des Siebs des Eratosthenes konnte ich lösen


    Und wie ?
    Denn genau das ist (auch laut Pico) das Problem, nicht die Parallelisierung.
    Das ist meine Signatur und sie wird wunderbar sein!

    VB.NET-Quellcode

    1. Imports TNumb = System.UInt64
    2. Public Class PrimFinder
    3. Public PrimBoxes As Heap(Of PrimBox) = heap.FromSelector(Function(pb As PrimBox) pb.NextMultiple, True)
    4. ''' <summary>Das Sieb des Eratosthenes wird hier nicht ausgebreitet, sondern jede gefundene PrimBox merkt sich, welche Zahl sie als
    5. ''' nächstes wegstreichen wird (NextMultiple). Das ganze im Heap, sodaß die Primbox mit dem niedrigsten NextMultiple sofort verfügbar ist</summary>
    6. Public Sub Run(ByVal upTo As TNumb)
    7. With PrimBoxes
    8. .Clear()
    9. .Push(New PrimBox(3))
    10. For n As TNumb = 5 To upTo Step 2
    11. If .Peek.TryIncrement(n) Then
    12. .ReHeap()
    13. While .Peek.TryIncrement(n)
    14. .ReHeap()
    15. End While
    16. Else
    17. .Push(New PrimBox(n))
    18. End If
    19. Next
    20. End With
    21. End Sub
    22. End Class
    23. <Diagnostics.DebuggerDisplay("{Prim} - {NextMultiple}")> _
    24. Public Class PrimBox
    25. Public Prim, NextMultiple As TNumb
    26. Public Function TryIncrement(ByVal test As TNumb) As Boolean
    27. If NextMultiple > test Then Return False
    28. NextMultiple += Prim << 1
    29. Return True
    30. End Function
    31. Public Sub New(ByVal prim As TNumb)
    32. Me.Prim = prim
    33. NextMultiple = prim + (prim << 1)
    34. End Sub
    35. End Class
    Wesentlich ist der Kommentar

    Hier gehts zum Heap - performante sortierte Datenstruktur