Apakah DateTime. Sekarang cara terbaik untuk mengukur kinerja suatu fungsi?

474

Saya perlu menemukan hambatan dan perlu seakurat mungkin mengukur waktu.

Apakah potongan kode berikut ini cara terbaik untuk mengukur kinerja?

DateTime startTime = DateTime.Now;

// Some execution process

DateTime endTime = DateTime.Now;
TimeSpan totalTimeTaken = endTime.Subtract(startTime);
David Basarab
sumber
Omong-omong, jika Anda tidak mencari sesuatu penghitung kinerja cepat dan kotor dapat digunakan.
Jonathan C Dickinson
1
Jika Anda membutuhkan ketelitian yang lebih besar gunakan Stopwatch.GetTimestamp, jika tidak jawabannya adalah baik.
dbasnett
@dbasnett Bisakah Anda menjelaskan lebih detail?
David Basarab
Dalam contoh di atas, ubah start dan endtime menjadi long dan tetapkan Stopwatch.GetTimestamp sebagai ganti DateTime. Sekarang. Waktu yang diperlukan adalah (mulai-akhir) / Stopwatch.Frequency.
dbasnett
Lihat juga: The Case Against DateTime.Now
Matt Johnson-Pint

Jawaban:

649

Tidak, tidak. Gunakan Stopwatch (dalam System.Diagnostics)

Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();

Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds);

Stopwatch secara otomatis memeriksa keberadaan timer presisi tinggi.

Perlu disebutkan bahwa DateTime.Nowseringkali sedikit lebih lambat daripada DateTime.UtcNowkarena pekerjaan yang harus dilakukan dengan zona waktu, DST dan semacamnya.

DateTime.UtcNow biasanya memiliki resolusi 15 ms. Lihat posting blog John Chapman tentang DateTime.Nowpresisi untuk ringkasan yang bagus.

Hal-hal sepele yang menarik: Stopwatch kembali hidup DateTime.UtcNowjika perangkat keras Anda tidak mendukung penghitung frekuensi tinggi. Anda dapat memeriksa untuk melihat apakah Stopwatch menggunakan perangkat keras untuk mencapai presisi tinggi dengan melihat Stopwatch bidang statis .

Markus Olsson
sumber
3
Saya akan menempatkan satu PerformWork (); sebelum Stopwatch untuk "memanas".
DiVan
2
Harus juga menambahkan rekomendasi bahwa jika Anda PerformWork()sangat kekurangan, Anda mungkin dapat memanggilnya berulang kali dan menghitung rata-rata kumpulan panggilan. Selain itu, atur seluruh batch panggilan daripada memulai / menghentikan Anda Stopwatchuntuk menghindari efek strobo yang akan memperkeruh pengukuran waktu Anda.
devgeezer
1
Stopwatch bukan threadsafe di multicore. Lihat stackoverflow.com/questions/6664538/… dan stackoverflow.com/questions/1149485/…
Pavel Savara
1
sw.ElapsedMilliseconds; bisa juga
Flappy
2
@Pavel, agar jelas, Stopwatch direkomendasikan oleh Microsoft sebagai solusi terbaik (overhead rendah dan presisi tinggi) pada prosesor multicore modern yang menjalankan Windows 7 dan Windows 8. msdn.microsoft.com/en-us/library/windows/ desktop / ...
Ron
91

Jika Anda menginginkan sesuatu yang cepat dan kotor saya sarankan menggunakan Stopwatch sebagai gantinya untuk tingkat presisi yang lebih tinggi.

Stopwatch sw = new Stopwatch();
sw.Start();
// Do Work
sw.Stop();

Console.WriteLine("Elapsed time: {0}", sw.Elapsed.TotalMilliseconds);

Atau, jika Anda membutuhkan sesuatu yang sedikit lebih canggih Anda mungkin harus mempertimbangkan untuk menggunakan profiler pihak ke-3 seperti SEMUT .

mmcdole
sumber
56

Artikel ini mengatakan bahwa pertama-tama Anda perlu membandingkan tiga alternatif Stopwatch,, DateTime.NowDAN DateTime.UtcNow.

Ini juga menunjukkan bahwa dalam beberapa kasus (ketika penghitung kinerja tidak ada) Stopwatch menggunakan DateTime.UtcNow + beberapa pemrosesan tambahan. Karena itu jelas bahwa dalam hal ini DateTime.UtcNow adalah pilihan terbaik (karena yang lain menggunakannya + beberapa pemrosesan)

Namun, ternyata, penghitung hampir selalu ada - lihat Penjelasan tentang penghitung kinerja resolusi tinggi dan keberadaannya terkait dengan .NET Stopwatch? .

Ini adalah grafik kinerja. Perhatikan bagaimana UtcNow berbiaya rendah dibandingkan dengan alternatif:

Masukkan deskripsi gambar di sini

Sumbu X adalah ukuran data sampel, dan sumbu Y adalah waktu relatif dari contoh.

Satu hal Stopwatchyang lebih baik adalah memberikan pengukuran waktu resolusi yang lebih tinggi. Lain adalah sifatnya yang lebih OO. Namun, membuat pembungkus OO UtcNowtidak sulit.

Valentin Kuzub
sumber
Tautan pertama tampaknya rusak.
Peter Mortensen
1
menjadi rusak ya .. mesin waktu bisa menunjukkannya saya kira. Tapi mengapa Anda mengedit "tiga", saya rasa tidak diperlukan di sini.
Valentin Kuzub
18

Sangat berguna untuk mendorong kode pembandingan Anda ke kelas / metode utilitas. The StopWatchkelas tidak perlu Disposedatau Stoppedpada kesalahan. Jadi, kode paling sederhana untuk menentukan waktu beberapa tindakan adalah

public partial class With
{
    public static long Benchmark(Action action)
    {
        var stopwatch = Stopwatch.StartNew();
        action();
        stopwatch.Stop();
        return stopwatch.ElapsedMilliseconds;
    }
}

Contoh kode panggilan

public void Execute(Action action)
{
    var time = With.Benchmark(action);
    log.DebugFormat(“Did action in {0} ms.”, time);
}

Ini adalah versi metode ekstensi

public static class Extensions
{
    public static long Benchmark(this Action action)
    {
        return With.Benchmark(action);
    }
}

Dan contoh kode panggilan

public void Execute(Action action)
{
    var time = action.Benchmark()
    log.DebugFormat(“Did action in {0} ms.”, time);
}
Anthony Mastrean
sumber
1
Bagaimana dengan granularitas yang lebih baik? Banyak hal terjadi dalam waktu kurang dari satu ms.
Henrik
Kembalikan properti yang telah berlalu lalu, ini adalah TimeSpan. Saya hanya menunjukkan polanya. Bersenang-senang mengimplementasikannya.
Anthony Mastrean
Kembali Elapsed.TotalMillisecondsuntuk presisi yang lebih tinggi. Lihat pertanyaan ini juga stackoverflow.com/questions/8894425/…
nawfal
16

The stopwatch fungsi akan lebih baik (presisi tinggi). Saya juga merekomendasikan hanya mengunduh salah satu profiler populer, ( DotTrace dan ANTS adalah yang paling sering saya gunakan ... percobaan gratis untuk DotTrace berfungsi penuh dan tidak mengomel seperti yang lain).

jsight
sumber
13

Gunakan kelas System.Diagnostics.Stopwatch.

Stopwatch sw = new Stopwatch();
sw.Start();

// Do some code.

sw.Stop();

// sw.ElapsedMilliseconds = the time your "do some code" took.
rp.
sumber
11

Ditto Stopwatch, ini jauh lebih baik.

Mengenai pengukuran kinerja, Anda juga harus memeriksa apakah "// Beberapa Proses Eksekusi" Anda adalah proses yang sangat singkat.

Ingat juga bahwa proses pertama "// Some Execution Process" Anda mungkin lebih lambat daripada proses selanjutnya.

Saya biasanya menguji metode dengan menjalankannya 1000 kali atau 1000000 kali dalam satu lingkaran dan saya mendapatkan data yang jauh lebih akurat daripada menjalankannya sekali.

Andrei Rînea
sumber
9

Ini semua adalah cara yang bagus untuk mengukur waktu, tetapi itu hanya cara yang sangat tidak langsung untuk menemukan hambatan.

Cara paling langsung untuk menemukan bottneck di utas adalah menjalankannya, dan saat melakukan apa pun yang membuat Anda menunggu, hentikan dengan jeda atau kunci istirahat. Lakukan ini beberapa kali. Jika kemacetan Anda memakan waktu X%, X% adalah probabilitas bahwa Anda akan menangkapnya dalam aksi di setiap snapshot.

Berikut adalah penjelasan yang lebih lengkap tentang bagaimana dan mengapa itu bekerja

Mike Dunlavey
sumber
7

@ Sean Chambers

FYI, kelas .NET Timer bukan untuk diagnostik, ini menghasilkan peristiwa pada interval yang telah ditentukan, seperti ini (dari MSDN ):

System.Timers.Timer aTimer;
public static void Main()
{
    // Create a timer with a ten second interval.
    aTimer = new System.Timers.Timer(10000);

    // Hook up the Elapsed event for the timer.
    aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);

    // Set the Interval to 2 seconds (2000 milliseconds).
    aTimer.Interval = 2000;
    aTimer.Enabled = true;

    Console.WriteLine("Press the Enter key to exit the program.");
    Console.ReadLine();
}

// Specify what you want to happen when the Elapsed event is 
// raised.
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
    Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}

Jadi ini benar-benar tidak membantu Anda tahu berapa lama sesuatu, hanya saja sejumlah waktu telah berlalu.

Timer juga ditampilkan sebagai kontrol di System.Windows.Forms ... Anda dapat menemukannya di kotak alat desainer Anda di VS05 / VS08

Adam Haile
sumber
6

Ini cara yang benar:

using System;
using System.Diagnostics;

class Program
{
    public static void Main()
    {
        Stopwatch stopWatch = Stopwatch.StartNew();

            // some other code

        stopWatch.Stop();

        // this not correct to get full timer resolution
        Console.WriteLine("{0} ms", stopWatch.ElapsedMilliseconds);

        // Correct way to get accurate high precision timing
        Console.WriteLine("{0} ms", stopWatch.Elapsed.TotalMilliseconds);
    }
}

Untuk informasi lebih lanjut, gunakan Use Stopwatch alih-alih DataTime untuk mendapatkan penghitung kinerja yang akurat .

jiya jain
sumber
6

Sistem Tim Visual Studio memiliki beberapa fitur yang dapat membantu mengatasi masalah ini. Pada dasarnya Anda dapat menulis unit test dan mencampurnya dalam skenario yang berbeda untuk dijalankan terhadap perangkat lunak Anda sebagai bagian dari stress atau load test. Ini dapat membantu mengidentifikasi area kode yang paling memengaruhi kinerja aplikasi Anda.

Grup Pola dan Praktik Microsoft memiliki beberapa panduan dalam Pedoman Pengujian Kinerja Sistem Tim Visual Studio .

Iain
sumber
5

Saya baru saja menemukan posting di blog Vance Morrison tentang kelas CodeTimer yang ditulisnya yang membuat penggunaan StopWatchlebih mudah dan melakukan beberapa hal yang rapi di samping.

OwenP
sumber
5

Ini tidak cukup profesional:

Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();

Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds);

Versi yang lebih andal adalah:

PerformWork();

int repeat = 1000;

Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < repeat; i++)
{
   PerformWork();
}

sw.Stop();

Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds / repeat);

Dalam kode saya yang sebenarnya, saya akan menambahkan panggilan GC.Collect untuk mengubah tumpukan terkelola ke status yang diketahui, dan menambahkan panggilan Tidur sehingga interval kode yang berbeda dapat dengan mudah dipisahkan dalam profil ETW.

Sampai jumpa StackOverflow
sumber
4

Saya telah melakukan sangat sedikit pemeriksaan kinerja semacam ini (saya cenderung hanya berpikir "ini lambat, buatlah lebih cepat") jadi saya sudah hampir selalu melakukannya.

Google tidak mengungkapkan banyak sumber daya / artikel untuk memeriksa kinerja.

Banyak yang menyebutkan menggunakan pinvoke untuk mendapatkan informasi kinerja. Banyak materi yang saya pelajari hanya benar-benar menyebutkan menggunakan perfmon ..

Edit:

Terlihat pembicaraan StopWatch .. Bagus! Saya telah belajar sesuatu :)

Ini terlihat seperti artikel yang bagus

Rob Cooper
sumber
4

Cara saya menggunakan dalam program saya menggunakan kelas StopWatch seperti yang ditunjukkan di sini.

Stopwatch sw = new Stopwatch();
sw.Start();


// Critical lines of code

long elapsedMs = sw.Elapsed.TotalMilliseconds;
Blackvault
sumber