none
Powershell läuft nur auf 25% CPU RRS feed

  • Frage

  • Moin,

    ich habe jetzt schon echt viel im Netz gesucht aber nichts dazu gefunden. Ich habe eine Powershellscript was sehr rechenintensiv ist. Mein Problem ist jetzt das Powershell nur 25% der CPU's verwendet. Wenn es nach mir geht kann er auch gerne 50% oder 75% haben. Nur leider bekomme ich es nicht hin das der Prozess mehr als 25% nimmt. Habe es schon mit /affinity 15 Probiert, nur leider bringt das auch nichts. 

    Das System auf dem das Script läuft ist ein Windows 2008 R2 Service Pack 1 mit 32 GB RAM und einem Quad 2,40 GHz Intel Xeon(R)E5-2609 Prozessor.

    Das Script ist eigentlich recht klein:

    $SourceFileName = 'XXX.TXT'

    $Buffer = Get-Content $SourceFileName -encoding byte
    $Encoding = [System.Text.Encoding]::GetEncoding(20106)
    $String = $Encoding.GetString($Buffer)

    $String | Out-File "XXXX.TXT" -Encoding default

    vielleicht gibt es ja auch eine Lösung wie ich das Script besser bauen kann. Das Problem ist halt das ich für eine 2.596 KB Datei 4 bis 6 min brauche und das ist noch eine der kleineren Dateien.

    Danke schon mal für die Hilfe.


    Freitag, 27. Oktober 2017 08:47

Antworten

  • Dein Script läuft eben als einzelner Thread und kann damit zu einem bestimmten Zeitpunkt immer nur auf einem einzigen Core ausgeführt werden. Prozessoraffinität bringt dir in diesem Fall nichts, denn das sorgt nur dafür, dass das Betriebssystem einen bestimmten Core bevorzugt, deshalb läuft das Script aber keinen Deut flotter.

    Das Stichwort für eine gleichzeitige Ausführung einer Anwendung oder eines Scripts lautet Multithreading. Dazu musst du allerdings deine Aufgabe in mehrere sinnvolle Teilaufgaben aufteilen. Ich hab's noch nie verwendet, aber in PowerShell ist Multithreading wohl grundsätzlich über die RunSpace-API möglich:

    https://blogs.technet.microsoft.com/heyscriptingguy/2015/11/26/beginning-use-of-powershell-runspaces-part-1/

    Die Frage ist eben, ob du dir diesen Aufwand antun möchtest oder nicht besser in dieser Zeit einen Kaffee holen gehst.

    Freitag, 27. Oktober 2017 10:12
  • Moin,

    bitte nutze den Codeblock, wenn Du Code postest.

    du müsstest mal schauen, welche der drei Zeilen die Zeit verbraucht: 2, 4 oder 5. Das kannst Du z.B. mit der Stoppuhr:

    $SourceFileName = 'XXX.TXT'
    $timer = [System.Diagnostics.Stopwatch]::StartNew()
    $Buffer = Get-Content $SourceFileName -encoding byte
    $t1 = $timer.Elapsed.TotalSeconds
    Write-Host "Datei einlesen: $t1"
    $Encoding = [System.Text.Encoding]::GetEncoding(20106)
    $String = $Encoding.GetString($Buffer)
    $t2 = $timer.Elapsed.TotalSeconds
    Write-Host "Encoding konvertieren: $($t2 - $t1)"
    $String | Out-File "XXXX.TXT" -Encoding default
    $t3 = $timer.Elapsed.TotalSeconds
    Write-Host "Ausgabe: $($t3 - $t2)"


    Evgenij Smirnov

    I work @ msg services ag, Berlin -> http://www.msg-services.de
    I blog (in German) @ http://it-pro-berlin.de
    my stuff in PSGallery --> https://www.powershellgallery.com/profiles/it-pro-berlin.de/
    Exchange User Group, Berlin -> http://exusg.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com

    Freitag, 27. Oktober 2017 10:32
  • du müsstest mal schauen, welche der drei Zeilen die Zeit verbraucht: 2, 4 oder 5. Das kannst Du z.B. mit der Stoppuhr:

    Genau. Und dann nimmst Du statt Get-Content und Out-File einen Stream Reader/Writer (Google und Stackoverflow helfen Dir dabei :-).

    $String = $Encoding.GetString($Buffer)

    Wenn das einen wesentlichen Teil der Gesamtzeit beansprucht: Das sollte sich ja hervorragend parallelisieren lassen. Dann mußt Du allerdings statt $buffer/$string auf .NET ArrayList umstellen (Powershell Arrays sind schnarchlangsam).

    Freitag, 27. Oktober 2017 11:16
  • Schau mal, ob das hilft:

    $SourceFileName = "C:\TEMP\in.txt"
    $timer = [System.Diagnostics.Stopwatch]::StartNew()
    $Encoding = [System.Text.Encoding]::GetEncoding(20106)
    foreach ($line in [System.IO.File]::ReadLines($SourceFileName)) {
        $String = $Encoding.GetString([byte[]]($line.ToCharArray()))
       [System.IO.File]::AppendAllText("C:\TEMP\out.txt",$String,[System.Text.Encoding]::Default)
    }
    $t1 = $timer.Elapsed.TotalSeconds
    Write-Host "Datei einlesen und konvertieren: $t1"


    Evgenij Smirnov

    I work @ msg services ag, Berlin -> http://www.msg-services.de
    I blog (in German) @ http://it-pro-berlin.de
    my stuff in PSGallery --> https://www.powershellgallery.com/profiles/it-pro-berlin.de/
    Exchange User Group, Berlin -> http://exusg.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com


    Freitag, 27. Oktober 2017 13:09

Alle Antworten

  • Wenn Du einen Quad-Core hast und ein Prozess lastet einen Core zu 100 % aus, dann ist in der Anzeige nur eine 25 %-ige Auslastung zu sehen, weil ja die anderen 75 % Cores quasi nix zu tun haben ... oder? Dein Flaschenhals ist also vermutlich gar nicht der Prozessor.

    Best regards (79,108,97,102|%{[char]$_})-join''


    • Bearbeitet BOfH-666 Freitag, 27. Oktober 2017 09:45
    Freitag, 27. Oktober 2017 09:44
  • Dein Script läuft eben als einzelner Thread und kann damit zu einem bestimmten Zeitpunkt immer nur auf einem einzigen Core ausgeführt werden. Prozessoraffinität bringt dir in diesem Fall nichts, denn das sorgt nur dafür, dass das Betriebssystem einen bestimmten Core bevorzugt, deshalb läuft das Script aber keinen Deut flotter.

    Das Stichwort für eine gleichzeitige Ausführung einer Anwendung oder eines Scripts lautet Multithreading. Dazu musst du allerdings deine Aufgabe in mehrere sinnvolle Teilaufgaben aufteilen. Ich hab's noch nie verwendet, aber in PowerShell ist Multithreading wohl grundsätzlich über die RunSpace-API möglich:

    https://blogs.technet.microsoft.com/heyscriptingguy/2015/11/26/beginning-use-of-powershell-runspaces-part-1/

    Die Frage ist eben, ob du dir diesen Aufwand antun möchtest oder nicht besser in dieser Zeit einen Kaffee holen gehst.

    Freitag, 27. Oktober 2017 10:12
  • Wenn ich das richtig versehe spalte ich die Datei die 2 MB groß ist in 4 stücke je 500kb und lasse alle 4 Dateien gleichzeitig laufen und füge Sie dann einfach wieder zusammen. Das wäre ein Anfang um das ganze etwas schneller zu gestalten. Das mit dem Kaffee wäre gut, hilft aber nicht wenn ich Teilweise Dateien habe die 150 MB groß sind :/ Das macht dann so 3-4 Stunden bis sie durch sind. Mit dem Multithreading vielleicht nur noch 1 Stunde. Vielleicht hat ja noch jemand anderes eine Idee.


    Freitag, 27. Oktober 2017 10:29
  • Moin,

    bitte nutze den Codeblock, wenn Du Code postest.

    du müsstest mal schauen, welche der drei Zeilen die Zeit verbraucht: 2, 4 oder 5. Das kannst Du z.B. mit der Stoppuhr:

    $SourceFileName = 'XXX.TXT'
    $timer = [System.Diagnostics.Stopwatch]::StartNew()
    $Buffer = Get-Content $SourceFileName -encoding byte
    $t1 = $timer.Elapsed.TotalSeconds
    Write-Host "Datei einlesen: $t1"
    $Encoding = [System.Text.Encoding]::GetEncoding(20106)
    $String = $Encoding.GetString($Buffer)
    $t2 = $timer.Elapsed.TotalSeconds
    Write-Host "Encoding konvertieren: $($t2 - $t1)"
    $String | Out-File "XXXX.TXT" -Encoding default
    $t3 = $timer.Elapsed.TotalSeconds
    Write-Host "Ausgabe: $($t3 - $t2)"


    Evgenij Smirnov

    I work @ msg services ag, Berlin -> http://www.msg-services.de
    I blog (in German) @ http://it-pro-berlin.de
    my stuff in PSGallery --> https://www.powershellgallery.com/profiles/it-pro-berlin.de/
    Exchange User Group, Berlin -> http://exusg.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com

    Freitag, 27. Oktober 2017 10:32
  • du müsstest mal schauen, welche der drei Zeilen die Zeit verbraucht: 2, 4 oder 5. Das kannst Du z.B. mit der Stoppuhr:

    Genau. Und dann nimmst Du statt Get-Content und Out-File einen Stream Reader/Writer (Google und Stackoverflow helfen Dir dabei :-).

    $String = $Encoding.GetString($Buffer)

    Wenn das einen wesentlichen Teil der Gesamtzeit beansprucht: Das sollte sich ja hervorragend parallelisieren lassen. Dann mußt Du allerdings statt $buffer/$string auf .NET ArrayList umstellen (Powershell Arrays sind schnarchlangsam).

    Freitag, 27. Oktober 2017 11:16
  • He,

    Sorry das mit dem Codeblock wusste ich nicht.

    Ja also das Ergebnis ist das das Einlesen so lange dauert. Damit hab ich jetzt nicht gerechnet.

    Datei einlesen: 181.6540093
    Encoding konvertieren: 11.5972091
    Ausgabe: 0.0422925000000021

    Das ist ja schon mal ein Ansatz an dem man arbeiten kann. Hoffe ich. Es handelt sich immer noch um die gleiche 2.596 KB Datei.

    • Bearbeitet hRoICE Freitag, 27. Oktober 2017 12:26
    Freitag, 27. Oktober 2017 12:19
  • Hallo,

    das Problem mit den PowerShell-Cmdlets ist, dass sie zwar total einfach zu bedienen sind, in vielen Fällen aber nicht die performanteste Option sind. Meist macht das nichts, weil die Datenmengen nicht so groß sind, dass es auffällt. Wenn man es mit größeren Dateien zu tun hat, muss man dann doch wieder auf .Net zurückgreifen. Allerdings sind 2,5 MB nicht unbedingt groß...

    In jedem Fall solltest du mal einen StreamReader versuchen.

    Viele Grüße

    Christoph

    Freitag, 27. Oktober 2017 12:44
  • OK, hab's zum Test laufen lassen. Hier die Verteilung:

    Datei einlesen: 208.2878587
    Encoding konvertieren: 55.1497152
    Ausgabe: 0.451859000000013

    D.h. Du musst am Einlesen optimieren, vorzugsweise mit der System.IO.StreamReader Klasse.


    Evgenij Smirnov

    I work @ msg services ag, Berlin -> http://www.msg-services.de
    I blog (in German) @ http://it-pro-berlin.de
    my stuff in PSGallery --> https://www.powershellgallery.com/profiles/it-pro-berlin.de/
    Exchange User Group, Berlin -> http://exusg.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com

    Freitag, 27. Oktober 2017 12:51
  • Ok das heißt ich muss das mit System.IO.StreamReader in die Variable einlesen, hab ich zwar noch nie gemacht. Aber irgendwie muss das ja gehen. Das muss ja dann auch wieder im encoding byte sein, das sollte auch gehen oder? Hat einer ein Tipp für mich? :D
    • Bearbeitet hRoICE Freitag, 27. Oktober 2017 13:03
    Freitag, 27. Oktober 2017 13:02
  • Schau mal, ob das hilft:

    $SourceFileName = "C:\TEMP\in.txt"
    $timer = [System.Diagnostics.Stopwatch]::StartNew()
    $Encoding = [System.Text.Encoding]::GetEncoding(20106)
    foreach ($line in [System.IO.File]::ReadLines($SourceFileName)) {
        $String = $Encoding.GetString([byte[]]($line.ToCharArray()))
       [System.IO.File]::AppendAllText("C:\TEMP\out.txt",$String,[System.Text.Encoding]::Default)
    }
    $t1 = $timer.Elapsed.TotalSeconds
    Write-Host "Datei einlesen und konvertieren: $t1"


    Evgenij Smirnov

    I work @ msg services ag, Berlin -> http://www.msg-services.de
    I blog (in German) @ http://it-pro-berlin.de
    my stuff in PSGallery --> https://www.powershellgallery.com/profiles/it-pro-berlin.de/
    Exchange User Group, Berlin -> http://exusg.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com


    Freitag, 27. Oktober 2017 13:09
  • Moin,

    bin wieder aus dem Urlaub zurück. Das sieht echt gut aus und funktioniert auch Prima, was er jetzt aber macht ist das er mir alle Zeilenumbrüche raushaut. Gibt es dafür ein Grund? Weil vorher hat er das nicht gemacht. Ich habe Sie jetzt nachträglich eingebaut, aber das schon sehr komisch.

    Grüße hRoICE

    Mittwoch, 1. November 2017 11:00
  • Moin,

    bin wieder aus dem Urlaub zurück. Das sieht echt gut aus und funktioniert auch Prima, was er jetzt aber macht ist das er mir alle Zeilenumbrüche raushaut. Gibt es dafür ein Grund? Weil vorher hat er das nicht gemacht. Ich habe Sie jetzt nachträglich eingebaut, aber das schon sehr komisch.

    Grüße hRoICE

    Ja, stimmt.

    Beim Lesen ist es ja ein "Read Line", damit ist der Umbruch nicht Bestandteil der eingelesenen Zeile. Beim Schreiben ist es hingegen "AppendAllText", und da wird 1:1 der Inhalt angehängt.


    Evgenij Smirnov

    I work @ msg services ag, Berlin -> http://www.msg-services.de
    I blog (in German) @ http://it-pro-berlin.de
    my stuff in PSGallery --> https://www.powershellgallery.com/profiles/it-pro-berlin.de/
    Exchange User Group, Berlin -> http://exusg.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com

    Mittwoch, 1. November 2017 12:02
  • Wenn Performance wichtig ist, lohnt sich ggf. der Wechsel auf StreamWriter an Stelle der statischen Methoden.
    Die Append-Funktionen werden nämlich immer langsamer, je größer die Dateien werden, da eine Datei keinen Index hat.
    Bei einem Stream bleibt die Datei offen und es kann Text (mit CRLF) oder Bytes ausgegeben werden.

    https://msdn.microsoft.com/de-de/library/6ka1wd3w(v=vs.110).aspx

    Mittwoch, 1. November 2017 13:41
  • Hab es noch etwas umgebaut, jetzt funktioniert es wie gewünscht.
    Remove-Item "XXXX.TXT" -ErrorAction Ignore
    $SourceFileName = "XXX.TXT"
    $Encoding = [System.Text.Encoding]::GetEncoding(20106)
    foreach ($line in [System.IO.File]::ReadAllText($SourceFileName)) {
        $String = $Encoding.GetString([byte[]]($line.ToCharArray()))
        [System.IO.File]::AppendAllText("XXXX.TXT",$String,[System.Text.Encoding]::Default)
    }
    Ich habe gerade auch eine 100 MB Datei getestet. Die ging innerhalb von 1 min durch. Das echt ein super Ergebnis. Danke dafür.

    • Bearbeitet hRoICE Donnerstag, 2. November 2017 12:21
    Donnerstag, 2. November 2017 12:20
  • Also bei einer Datenrate von 300MB/Sekunde und mehr für eine Festplatte sind 100MB/Minute ja extrem langsam.
    Das macht gerade 1,6MB/Sekunde.
    Die reine Verarbeitungszeit ist da eher zu vernachlässigen.
    Vergleiche mal einen einfachen Filecopy.

    Wie man der Beschreibung entnehmen kann, führt die Funktion AppendAlltext die folgenden Schritte aus:
    - Open
    - Positionieren am Ende
    - Schreiben
    - Close

    Dabei sind die Operationen 1, 2 und 4 die langsamsten.
    Bei Verwendung eines StreamWriters erfolgen die Schritte 1, 2 und 4 jedoch nur 1x und ich denke, dein Script könnte fast 100x schneller sein (bei großen Dateien).
    Auch bei kleinen Dateien kann es schon auffallend schneller werden.

    Donnerstag, 2. November 2017 12:28
  • Na klar ist eine Minute nicht schnell wenn man sich überlegt was eigentlich dahinter steht und wie schnell es gehen müsste. Aber ich sehe das ich mit Cmdlets nie so schnell gewesen wäre und 1 min ist für mich super. Ich habe es mit StreamWriters versucht, aber ich bin da für die gleiche Datei bei 1:16 min, das heißt für mich das es sogar langsamer ist.
    Donnerstag, 2. November 2017 13:40