Perbedaan Antara Invoke dan DynamicInvoke

128

Apa perbedaan antara Invoke dan DynamicInvoke dalam delegasi? Tolong beri saya contoh kode yang menjelaskan perbedaan antara dua metode itu.

testCoder
sumber

Jawaban:

206

Ketika Anda memiliki instance delegasi, Anda mungkin tahu tipe yang tepat, atau Anda mungkin tahu itu adalah Delegate. Jika Anda tahu jenis pastinya, Anda bisa menggunakannya Invoke, yang sangat cepat - semuanya sudah divalidasi sebelumnya. Sebagai contoh:

Func<int,int> twice = x => x * 2;
int i = 3;
int j = twice.Invoke(i);
// or just:
int j = twice(i);

Namun! Jika Anda hanya tahu Delegate, itu harus menyelesaikan parameter dll secara manual - ini mungkin melibatkan unboxing, dll - banyak refleksi terjadi. Sebagai contoh:

Delegate slowTwice = twice; // this is still the same delegate instance
object[] args = { i };
object result = slowTwice.DynamicInvoke(args);

Catatan Saya sudah menulis argstangan panjang untuk memperjelas bahwa ada object[]yang terlibat. Ada banyak biaya tambahan di sini:

  • array
  • memvalidasi argumen yang disahkan adalah "cocok" untuk yang sebenarnya MethodInfo
  • unboxing dll jika diperlukan
  • renungan-memohon
  • maka penelepon perlu melakukan sesuatu untuk memproses nilai kembali

Pada dasarnya, hindari DynamicInvokekapan saja Anda bisa. Invokeselalu lebih disukai, kecuali yang Anda miliki adalah a Delegatedan a object[].

Untuk perbandingan kinerja, berikut ini dalam mode rilis di luar debugger (konsol) mencetak:

Invoke: 19ms
DynamicInvoke: 3813ms

Kode:

Func<int,int> twice = x => x * 2;
const int LOOP = 5000000; // 5M
var watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
    twice.Invoke(3);
}
watch.Stop();
Console.WriteLine("Invoke: {0}ms", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
    twice.DynamicInvoke(3);
}
watch.Stop();
Console.WriteLine("DynamicInvoke: {0}ms", watch.ElapsedMilliseconds);
Marc Gravell
sumber
3
Apakah itu berarti bahwa dalam hal penggunaan, kompiler DynamicInvoke menghasilkan lebih banyak kode IL untuk menangani permintaan delegasi?
testCoder
2
@testCoder tidak, itu akan menggunakan refleksi
Marc Gravell
@ Marccravell ketika saya mencoba ini dalam metode yang meningkatkan acara, saya menerima panggilan metode pertama adalah mengambil sekitar 0,7766 ms tetapi kedua mengambil sekitar 0,0568 ms. Ketika yang pertama adalah Invoke, itu membutuhkan waktu lebih lama daripada DynamicInvoke atau sebaliknya. Ketika saya mencoba contoh Anda dengan 1 loop dan lihat msInvoke: 0,0478ms, DynamicInvoke: 0,053ms . Mengapa Anda membandingkannya lebih dari 1 panggilan? Dan mengapa yang pertama membutuhkan waktu lebih lama dari panggilan fungsi kedua?
uzay95
4
@ uzay95 Panggilan pertama ke metode ini menyebabkan kompilasi JIT dilakukan oleh CLR - ini berlaku untuk metode apa pun pada saat pertama kali dipanggil setelah proses dimulai. Dalam skenario semacam ini, Anda dapat melakukan salah satu dari tiga hal: (1) jalankan metode beberapa kali sehingga waktu yang dibutuhkan untuk panggilan pertama menjadi tidak signifikan dalam hasil akhir, (2) jangan mulai mengukur sampai setelah Anda Sudah memanggil metode sekali, atau (3) menggunakan ngen.exe (berlebihan). Posting ini menjelaskannya dengan cukup baik ... stackoverflow.com/questions/4446203/
Quanta
@ marc-gravell Anda tidak perlu membuat array untuk meneruskan ke DynamicInvoke karena tanda tangan metode menyatakan kata kunci params untuk parameter args.
zodo