Mari pertimbangkan metode asinkron yang sangat sederhana ini:
static async Task myMethodAsync()
{
await Task.Delay(500);
}
Ketika saya mengkompilasi ini dengan VS2013 (sebelum kompiler Roslyn) mesin negara yang dihasilkan adalah sebuah struct.
private struct <myMethodAsync>d__0 : IAsyncStateMachine
{
...
void IAsyncStateMachine.MoveNext()
{
...
}
}
Ketika saya mengkompilasinya dengan VS2015 (Roslyn) kode yang dihasilkan adalah ini:
private sealed class <myMethodAsync>d__1 : IAsyncStateMachine
{
...
void IAsyncStateMachine.MoveNext()
{
...
}
}
Seperti yang Anda lihat, Roslyn menghasilkan kelas (dan bukan struct). Jika saya ingat dengan benar implementasi pertama async / await dukungan di kompiler lama (CTP2012 kurasa) juga menghasilkan kelas dan kemudian diubah menjadi struct dari alasan kinerja. (dalam beberapa kasus, Anda dapat sepenuhnya menghindari tinju dan alokasi heap…) (Lihat ini )
Adakah yang tahu mengapa ini diubah lagi di Roslyn? (Saya tidak punya masalah tentang ini, saya tahu bahwa perubahan ini transparan dan tidak mengubah perilaku kode apa pun, saya hanya ingin tahu)
Edit:
Jawaban dari @Damien_The_Unbeliever (dan source code :)) imho menjelaskan semuanya. Perilaku yang dijelaskan dari Roslyn hanya berlaku untuk debug build (dan itu diperlukan karena batasan CLR yang disebutkan dalam komentar). Dalam Rilis itu juga menghasilkan struct (dengan semua manfaat itu ..). Jadi ini tampaknya menjadi solusi yang sangat cerdas untuk mendukung Edit dan Continue serta kinerja yang lebih baik dalam produksi. Hal-hal menarik, terima kasih untuk semua yang telah berpartisipasi!
sumber
async
metode hampir selalu memiliki titik asynchronous yang sebenarnya -await
yang menghasilkan kontrol, yang akan membutuhkan struct untuk dimasukkan ke dalam kotak. Saya percaya struct hanya akan mengurangi tekanan memori untukasync
metode yang berjalan secara sinkron.Jawaban:
Saya tidak memiliki pengetahuan sebelumnya tentang hal ini, tetapi karena Roslyn adalah open-source akhir-akhir ini, kita dapat menelusuri kode untuk mendapatkan penjelasan.
Dan di sini, di baris 60 dari AsyncRewriter , kami menemukan:
// The CLR doesn't support adding fields to structs, so in order to enable EnC in an async method we need to generate a class. var typeKind = compilationState.Compilation.Options.EnableEditAndContinue ? TypeKind.Class : TypeKind.Struct;
Jadi, meskipun ada beberapa daya tarik untuk menggunakan
struct
s, kemenangan besar memungkinkan Edit dan Lanjutkan untuk bekerja dalamasync
metode jelas dipilih sebagai opsi yang lebih baik.sumber
Sulit untuk memberikan jawaban pasti untuk sesuatu seperti ini (kecuali seseorang dari tim kompilator mampir :)), tetapi ada beberapa hal yang dapat Anda pertimbangkan:
Kinerja "bonus" dari struct selalu merupakan pertukaran. Pada dasarnya, Anda mendapatkan yang berikut:
Apa artinya ini dalam kasus menunggu? Sebenarnya ... tidak ada. Hanya ada periode waktu yang sangat singkat di mana mesin status ada di tumpukan - ingat,
await
secara efektif melakukan areturn
, sehingga metode tumpukan mati; mesin negara harus dipertahankan di suatu tempat, dan "suatu tempat" pasti ada di heap. Masa pakai tumpukan tidak cocok dengan kode asinkron :)Selain itu, mesin negara melanggar beberapa pedoman bagus untuk mendefinisikan struct:
struct
s harus berukuran paling banyak 16-byte - mesin negara berisi dua pointer, yang dengan sendirinya memenuhi batas 16-byte dengan rapi pada 64-bit. Selain itu, ada negara itu sendiri, sehingga melampaui "batas". Ini bukan masalah besar , karena kemungkinan besar hanya pernah diteruskan oleh referensi, tetapi perhatikan bagaimana hal itu tidak sesuai dengan kasus penggunaan untuk struct - struct yang pada dasarnya adalah tipe referensi.struct
s harus tidak dapat diubah - ini mungkin tidak membutuhkan banyak komentar. Itu mesin negara . Sekali lagi, ini bukan masalah besar, karena struct adalah kode yang dibuat secara otomatis dan pribadi, tetapi ...struct
s harus secara logis mewakili satu nilai. Jelas tidak terjadi di sini, tapi itu sudah mengikuti dari memiliki keadaan yang bisa berubah di tempat pertama.Dan tentu saja, semua ini terjadi jika tidak ada penutupan. Jika Anda memiliki penduduk lokal (atau bidang) yang melintasi
await
s, statusnya meningkat lebih jauh, membatasi kegunaan penggunaan struct.Mengingat semua ini, pendekatan kelas pasti lebih bersih, dan saya tidak akan mengharapkan peningkatan kinerja yang nyata dari penggunaan a
struct
. Semua objek yang terlibat memiliki seumur hidup yang sama, sehingga satu-satunya cara untuk meningkatkan kinerja memori akan membuat semua dari merekastruct
s (toko di beberapa penyangga, misalnya) - yang tidak mungkin dalam kasus umum, tentu saja. Dan sebagian besar kasus di mana Anda akan menggunakanawait
di tempat pertama (yaitu, beberapa pekerjaan I / O asinkron) sudah melibatkan kelas lain - misalnya, buffer data, string ... Agak tidak mungkin Anda menginginkanawait
sesuatu yang hanya kembali42
tanpa melakukan apa pun alokasi heap.Pada akhirnya, saya akan mengatakan satu-satunya tempat di mana Anda benar-benar melihat perbedaan kinerja yang nyata adalah tolok ukur. Dan mengoptimalkan tolok ukur adalah ide yang konyol, untuk sedikitnya ...
sumber