Mengatur waktu eksekusi perintah di PowerShell

217

Apakah ada cara sederhana untuk menentukan waktu pelaksanaan perintah di PowerShell, seperti perintah 'waktu' di Linux?
Saya datang dengan ini:

$s=Get-Date; .\do_something.ps1 ; $e=Get-Date; ($e - $s).TotalSeconds

Tapi saya ingin sesuatu yang lebih sederhana

time .\do_something.ps1
Paolo Tedesco
sumber

Jawaban:

337

Ya.

Measure-Command { .\do_something.ps1 }

Perhatikan bahwa satu kelemahan minor Measure-Commandadalah Anda tidak melihat stdoutoutput.

[Perbarui, terima kasih kepada @JasonMArcher] Anda dapat memperbaikinya dengan menyalurkan output perintah ke beberapa commandlet yang menulis ke host, mis. Out-DefaultSehingga menjadi:

Measure-Command { .\do_something.ps1 | Out-Default }

Cara lain untuk melihat hasilnya adalah menggunakan Stopwatchkelas .NET seperti ini:

$sw = [Diagnostics.Stopwatch]::StartNew()
.\do_something.ps1
$sw.Stop()
$sw.Elapsed
Keith Hill
sumber
114
Anda juga dapat melihat output seperti ini, Measure-Command {ps | Keluar Default}. Atau apa pun yang menulis langsung ke host, yang mungkin bermanfaat atau tidak.
JasonMArcher
18
Saya mengambil solusi ini dan menulis fungsi yang mungkin berguna bagi orang lain. gist.github.com/2206444 - Contoh: time { ping -n 1 google.com } -Samples 10akan menjalankan perintah 10kali dan mengembalikan waktu rata-rata, minimum dan maksimum yang diambil. Anda dapat menambahkan -Silentuntuk menelan STDOUT.
joshuapoehls
13
Preferensi saya adalah untuk menetapkan hasil Measure-Command ke variabel, seperti $t = Measure-Command {<<your command or code block>>}. Mencobanya dan kemudian ketik $tpada prompt untuk melihat hasil Anda dan semua properti Anda memiliki akses ke, seperti $t.Milliseconds, $t.TotalSeconds, dll Kemudian kita dapat menulis ke output apapun yang kita inginkan, misalnya,Write-Host That command took $t.TotalSeconds to complete.
Baodad
apa yang lebih cepat digunakan? net.stopwatch, atau perintah-ukur, atau hanya membandingkan dua vars get-date ... (maksud saya apa yang lebih efisien untuk menyimpan secara permanen dalam skrip?)
Hicsy
Mungkin termasuk intisari dari komentar JasonMArcher (jadi jelas itu bisa digunakan dengan cara yang lebih halus daripada keseluruhan skrip PowerShell)?
Peter Mortensen
183

Anda juga bisa mendapatkan perintah terakhir dari histori dan mengurangi EndExecutionTimedari itu StartExecutionTime.

.\do_something.ps1  
$command = Get-History -Count 1  
$command.EndExecutionTime - $command.StartExecutionTime
Shay Levy
sumber
22
Coba ini kapan-kapan: Get-History | Group {$_.StartExecutionTime.Hour} | sort Count -descuntuk melihat pola penggunaan PowerShell Anda menurut jam sehari. :-)
Keith Hill
18
+1 karena dapat menggunakan ini untuk mencari tahu berapa lama waktu yang diperlukan bahkan ketika Anda tidak mengharapkannya membutuhkan waktu yang lama ketika Anda memulai sehingga Anda tidak berpikir untuk membungkusnya dengan Measure-Command.
Chris Magnuson
3
PowerShell terkadang mengagumkan.
ConstantineK
Saya berharap saya bisa memberi Anda lebih dari sekadar +1 :)
David Ferenczy Rogožan
Ya, ini luar biasa! Saya membuat satu liner menggunakan:$command = Get-History -Count 1 ; "{0}" -f ($command.EndExecutionTime - $command.StartExecutionTime)
Phil
106

Menggunakan Measure-Command

Contoh

Measure-Command { <your command here> | Out-Host }

Pipa untuk Out-Hostmemungkinkan Anda melihat output dari perintah, yang jika tidak dikonsumsi oleh Measure-Command.

Droj
sumber
Saya pikir maksud Anda Measure-Command {<your command } | Out-Host - Tuan Rumah berada di luar blok skrip
Peter McEvoy
1
@ Peter - harus di dalam blok, jika Measure-Command mengkonsumsi output sebelum pergi ke konsol.
Droj
1
Gotcha ... dalam hal ini, Anda mungkin bahkan tidak membutuhkan pipa. Seharusnya hanya mencetak hasilnya, kecuali jika Anda membungkusnya dengan beberapa blok lain ....
Droj
2
Apakah Out-Default mungkin lebih baik daripada Out-Host karena kompatibel dengan skrip? jsnover.com/blog/2013/12/07/write-host-considered-harmful
MarcH
1
Yah saya mencoba Out-Default dan berfungsi dengan baik di terminal juga, jadi mengapa tidak selalu menggunakan Out-Default? (Saya belum mencobanya dalam skrip maaf)
MarcH
18

Sederhana

function time($block) {
    $sw = [Diagnostics.Stopwatch]::StartNew()
    &$block
    $sw.Stop()
    $sw.Elapsed
}

kemudian bisa digunakan sebagai

time { .\some_command }

Anda mungkin ingin mengubah output

Mike West
sumber
1
Measure-Commandmenyembunyikan output perintah, jadi solusi ini terkadang lebih baik.
codekaizen
Ini adalah solusi yang fantastis, yang menghormati output perintah. Anda juga dapat menjalankannya tanpa kurung kurawal untuk perintah sederhana, misalnya: "time ls", persis seperti yang Anda lakukan di Unix.
Raúl Salinas-Monteagudo
5

Inilah fungsi yang saya tulis yang bekerja mirip dengan timeperintah Unix :

function time {
    Param(
        [Parameter(Mandatory=$true)]
        [string]$command,
        [switch]$quiet = $false
    )
    $start = Get-Date
    try {
        if ( -not $quiet ) {
            iex $command | Write-Host
        } else {
            iex $command > $null
        }
    } finally {
        $(Get-Date) - $start
    }
}

Sumber: https://gist.github.com/bender-the-greatest/741f696d965ed9728dc6287bdd336874

Bender Yang Terbesar
sumber
Pertanyaannya adalah "Mengatur waktu eksekusi perintah di PowerShell". Apa hubungannya dengan mengatur waktu proses menggunakan Unix?
Jean-Claude DeMars
5
Ini adalah fungsi Powershell yang saya tulis, yang menunjukkan cara menghitung sendiri waktu eksekusi sebagai lawan dari menggunakan Measure-Commandatau salah satu dari berbagai cara lain yang dapat Anda lakukan saat eksekusi di Powershell. Jika Anda membaca pertanyaan aslinya, ia meminta sesuatu yang berfungsi "seperti timeperintah di Linux".
Bender the Greatest
3

Menggunakan Stopwatch dan memformat waktu yang telah berlalu:

Function FormatElapsedTime($ts) 
{
    $elapsedTime = ""

    if ( $ts.Minutes -gt 0 )
    {
        $elapsedTime = [string]::Format( "{0:00} min. {1:00}.{2:00} sec.", $ts.Minutes, $ts.Seconds, $ts.Milliseconds / 10 );
    }
    else
    {
        $elapsedTime = [string]::Format( "{0:00}.{1:00} sec.", $ts.Seconds, $ts.Milliseconds / 10 );
    }

    if ($ts.Hours -eq 0 -and $ts.Minutes -eq 0 -and $ts.Seconds -eq 0)
    {
        $elapsedTime = [string]::Format("{0:00} ms.", $ts.Milliseconds);
    }

    if ($ts.Milliseconds -eq 0)
    {
        $elapsedTime = [string]::Format("{0} ms", $ts.TotalMilliseconds);
    }

    return $elapsedTime
}

Function StepTimeBlock($step, $block) 
{
    Write-Host "`r`n*****"
    Write-Host $step
    Write-Host "`r`n*****"

    $sw = [Diagnostics.Stopwatch]::StartNew()
    &$block
    $sw.Stop()
    $time = $sw.Elapsed

    $formatTime = FormatElapsedTime $time
    Write-Host "`r`n`t=====> $step took $formatTime"
}

Sampel Penggunaan

StepTimeBlock ("Publish {0} Reports" -f $Script:ArrayReportsList.Count)  { 
    $Script:ArrayReportsList | % { Publish-Report $WebServiceSSRSRDL $_ $CarpetaReports $CarpetaDataSources $Script:datasourceReport };
}

StepTimeBlock ("My Process")  {  .\do_something.ps1 }
Kiquenet
sumber
-2

Hanya satu kata di gambar (salah) kesimpulan dari salah satu perintah pengukuran kinerja yang disebut dalam jawaban. Ada sejumlah jebakan yang harus dipertimbangkan selain dari melihat ke waktu pemanggilan fungsi (perintah) yang kosong.

Perangkat lunak Sjoemels

'Sjoemelsoftware' memilih kata Belanda tahun 2015
Sjoemelen berarti curang, dan kata sjoemelsoftware muncul karena skandal emisi Volkswagen. Definisi resmi adalah "perangkat lunak yang digunakan untuk memengaruhi hasil tes".

Secara pribadi, saya berpikir bahwa " Sjoemelsoftware " tidak selalu sengaja dibuat untuk menipu hasil tes tetapi mungkin berasal dari mengakomodasi situasi praktis yang mirip dengan kasus uji seperti yang ditunjukkan di bawah ini.

Sebagai contoh, menggunakan perintah pengukuran kinerja yang terdaftar, Integrated Query Language (LINQ) (1) , sering memenuhi syarat seperti yang cara berpuasa untuk mendapatkan sesuatu dilakukan dan itu sering, tetapi tentu tidak selalu! Siapa pun yang mengukur peningkatan kecepatan faktor 40 atau lebih dibandingkan dengan perintah PowerShell asli, mungkin salah mengukur atau menarik kesimpulan yang salah.

Intinya adalah bahwa beberapa kelas .Net (seperti LINQ) menggunakan evaluasi malas (juga disebut sebagai eksekusi ditangguhkan (2) ). Berarti ketika menetapkan ekspresi ke variabel, tampaknya segera dilakukan tetapi sebenarnya belum memproses apa-apa!

Mari kita anggap bahwa Anda dot-source. .\Dosomething.ps1 perintah Anda yang memiliki PowerShell atau ekspresi Linq yang lebih canggih (untuk kemudahan penjelasan, saya telah langsung memasukkan ekspresi langsung ke dalam Measure-Command):

$Data = @(1..100000).ForEach{[PSCustomObject]@{Index=$_;Property=(Get-Random)}}

(Measure-Command {
    $PowerShell = $Data.Where{$_.Index -eq 12345}
}).totalmilliseconds
864.5237

(Measure-Command {
    $Linq = [Linq.Enumerable]::Where($Data, [Func[object,bool]] { param($Item); Return $Item.Index -eq 12345})
}).totalmilliseconds
24.5949

Hasilnya tampak jelas, perintah Linq selanjutnya adalah sekitar 40 kali lebih cepat dari perintah PowerShell pertama . Sayangnya, tidak sesederhana itu ...

Mari kita tampilkan hasilnya:

PS C:\> $PowerShell

Index  Property
-----  --------
12345 104123841

PS C:\> $Linq

Index  Property
-----  --------
12345 104123841

Seperti yang diharapkan, hasilnya sama tetapi jika Anda telah memperhatikan dengan seksama, Anda akan memperhatikan bahwa butuh waktu lebih lama untuk menampilkan $Linqhasilnya daripada $PowerShellhasilnya.
Mari kita secara khusus mengukur bahwa dengan hanya mengambil properti dari objek mengakibatkan:

PS C:\> (Measure-Command {$PowerShell.Property}).totalmilliseconds
14.8798
PS C:\> (Measure-Command {$Linq.Property}).totalmilliseconds
1360.9435

Butuh sekitar 90 faktor lebih lama untuk mengambil properti dari $Linqobjek kemudian $PowerShellobjek dan itu hanya satu objek!

Juga perhatikan perangkap lain bahwa jika Anda melakukannya lagi, langkah-langkah tertentu mungkin terlihat jauh lebih cepat daripada sebelumnya, ini karena beberapa ekspresi telah di-cache.

Intinya, jika Anda ingin membandingkan kinerja antara dua fungsi, Anda perlu mengimplementasikannya dalam kasing bekas Anda, mulailah dengan sesi PowerShell yang baru dan simpulkan kesimpulan Anda pada kinerja aktual dari solusi lengkap.

(1) Untuk latar belakang dan contoh lebih lanjut tentang PowerShell dan LINQ, saya sarankan situs ini: PowerShell Kinerja Tinggi dengan LINQ
(2) Saya pikir ada perbedaan kecil antara dua konsep karena dengan evaluasi malas hasilnya dihitung ketika dibutuhkan sebagaimana yang seharusnya. eksekusi yang ditunda adalah hasilnya dihitung ketika sistem idle

besi
sumber
Tujuan dari jawaban ini adalah untuk dapat mengarahkan orang ke pertanyaan umum untuk kesalahpahaman umum sehubungan dengan perintah waktu di PowerShell, seperti yang baru saja saya lakukan untuk pertanyaan berulang seperti: Powershell question - Mencari metode tercepat untuk mengulang melalui objek 500k mencari kecocokan dalam array objek 500k lainnya
iRon