Roslyn'de neden zaman uyumsuz durum makineleri sınıfları (yapılar değil) var?


87

Bu çok basit eşzamansız yöntemi ele alalım:

static async Task myMethodAsync() 
{
    await Task.Delay(500);
}

Bunu VS2013 (Roslyn öncesi derleyici) ile derlediğimde, üretilen durum makinesi bir yapıdır.

private struct <myMethodAsync>d__0 : IAsyncStateMachine
{  
    ...
    void IAsyncStateMachine.MoveNext()
    {
        ...
    }
}

VS2015 (Roslyn) ile derlediğimde üretilen kod şudur:

private sealed class <myMethodAsync>d__1 : IAsyncStateMachine
{
    ...
    void IAsyncStateMachine.MoveNext()
    {
        ...
    }
}

Gördüğünüz gibi Roslyn bir sınıf (yapı değil) üretir. Eğer doğru hatırlıyorsam, eski derleyicideki async / await desteğinin ilk uygulamaları (CTP2012 sanırım) da sınıflar oluşturdu ve daha sonra performans nedenlerinden yapı olarak değiştirildi. (Bkz (bazı durumlarda tamamen boks ve yığın ayırma ... önleyebilirsiniz) bu )

Roslyn'de bunun neden tekrar değiştirildiğini bilen var mı? (Bununla ilgili herhangi bir problemim yok, bu değişikliğin şeffaf olduğunu ve herhangi bir kodun davranışını değiştirmediğini biliyorum, sadece merak ediyorum)

Düzenle:

@Damien_The_Unbeliever'ın (ve kaynak kodu :)) cevabı imho her şeyi açıklıyor. Roslyn'in açıklanan davranışı yalnızca hata ayıklama derlemesi için geçerlidir (ve bu, açıklamada bahsedilen CLR sınırlaması nedeniyle gereklidir). Sürümde aynı zamanda bir yapı oluşturur (tüm faydalarıyla birlikte ..). Dolayısıyla bu, hem Düzenleme hem de Devam Etme ve üretimde daha iyi performansı desteklemek için çok akıllıca bir çözüm gibi görünüyor. İlginç şeyler, katılan herkese teşekkürler!


2
Karmaşıklığın (yeniden değiştirilebilir yapılar) buna değmeyeceğine karar verdiklerinden şüpheleniyorum. asyncyöntemler hemen hemen her zaman gerçek bir eşzamansız noktaya sahiptir - awaitbu, yapının yine de kutulanmasını gerektirecek bir denetim sağlar. Ben inanıyorum yapılar yalnızca bellek basıncı rahatlatmak istiyorum asynceşzamanlı çalıştırmak oldu yöntemlerle.
Stephen Cleary

Yanıtlar:


112

Bunun hakkında herhangi bir ön bilgim yoktu, ancak Roslyn bu günlerde açık kaynak olduğundan, bir açıklama için kodu araştırmaya gidebiliriz.

Ve burada, AsyncRewriter'ın 60. satırında şunları buluyoruz:

// 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;

Bu nedenle, s kullanımına bazı çekicilikler varken struct, Düzenle ve Devam Et'in asyncyöntemler dahilinde çalışmasına izin vermenin büyük kazanımı açıkça daha iyi seçenek olarak seçildi.


18
Çok iyi yakaladın! Ve buna dayanarak şunu da keşfettim: Bu yalnızca onu hata ayıklamada oluşturduğunuzda olur (mantıklıdır, bu, EnC'yi yaptığınız zaman olur), ancak Sürüm'de bir yapı oluştururlar (bu durumda EnableEditAndContinue yanlıştır. .). Btw. Ayrıca koda bakmayı da denedim ama bulamadım. Çok teşekkürler!
gregkalapos

3

Böyle bir şeye kesin bir cevap vermek zordur (derleyici ekibinden biri gelmedikçe :)), ancak dikkate alabileceğiniz birkaç nokta var:

Yapıların performans "bonusu" her zaman bir değiş tokuştur. Temel olarak, aşağıdakileri elde edersiniz:

  • Değer semantiği
  • Olası yığın (belki kayıt olabilir mi?) Tahsisi
  • Dolaylı yoldan kaçınmak

Bekleme durumunda bu ne anlama geliyor? Aslında ... hiçbir şey. Durum makinesinin yığında olduğu çok kısa bir süre vardır - unutmayın, awaitetkin bir şekilde a yapar return, bu nedenle yöntem yığını ölür; devlet makinesi bir yerde korunmalıdır ve bu "bir yer" kesinlikle yığında. Yığın ömrü eşzamansız koda uymuyor :)

Bunun dışında, durum makinesi, yapıları tanımlamak için bazı iyi yönergeleri ihlal eder:

  • structs en fazla 16 bayt büyük olmalıdır - durum makinesi, kendi başına 64 bit üzerinde 16 baytlık sınırı düzgün bir şekilde dolduran iki işaretçi içerir. Bunun dışında devletin kendisi var, bu yüzden "sınır" ı aşıyor. Bu büyük bir sorun değil , çünkü büyük olasılıkla sadece referans olarak geçmiştir, ancak bunun yapılar için kullanım durumuna tam olarak uymadığına dikkat edin - temelde bir referans türü olan bir yapı.
  • structs değişmez olmalıdır - bu muhtemelen çok fazla yoruma ihtiyaç duymaz. Bu bir devlet makinesi . Yine, yapı otomatik olarak oluşturulan kod ve özel olduğu için bu önemli bir şey değil, ama ...
  • structs mantıksal olarak tek bir değeri temsil etmelidir. Kesinlikle buradaki durum böyle değil, ama bu zaten bir çeşit değişebilen duruma sahip olmanın sonucu.
  • Her yerde jenerik kullandığımız için sık sık kutulanmamalı - burada sorun değil . Durum nihayetinde yığında bir yerdedir, ancak en azından kutulanmaz (otomatik olarak). Yine, sadece dahili olarak kullanıldığı gerçeği, bunu hemen hemen geçersiz kılar.

Ve tabii ki, tüm bunlar kapatmanın olmadığı bir durumda. URL'leri geçen yereller (veya alanlar) olduğunda await, durum daha da şişirilir ve yapı kullanmanın kullanışlılığı sınırlanır.

Tüm bunlar göz önüne alındığında, sınıf yaklaşımı kesinlikle daha temiz ve structbunun yerine a kullanmanın gözle görülür bir performans artışı beklemem . Bellek performansını geliştirmek için tek yol yapmak olacaktır, böylece ilgili nesnelerin tüm benzer kullanım ömrüne sahiptir tüm onlardan structtabii ki, genel durumda imkansız - s (bazı tampon deposu, örneğin,). Ve Kullanmak istiyorum Çoğu durumda awaitilk etapta (olduğunu, bazı uyumsuz g / Ç iş) zaten diğer sınıfları dahil - örneğin, veri tampon, ip ... Size would rather düşüktür awaitşey olduğunu basitçe döner 42herhangi yapmadan yığın ayırmaları.

Sonunda, gerçekten bir performans farkı göreceğiniz tek yerin kıyaslama olacağını söyleyebilirim. Ve karşılaştırmalar için optimizasyon yapmak aptalca bir fikir, en hafif tabirle


Her zaman değil ihtiyaç gidip kaynağını okuması yokken derleyici ekibinin bir üyesi ve onlar :-) yararlı bir yorumunu bıraktım
Damien_The_Unbeliever

3
@Damien_The_Unbeliever Evet, bu kesinlikle harika bir keşifti, cevabınızı çoktan yükseltmiştim: P
Luaan

1
Yapı, kodun eşzamansız olarak çalışmaması durumunda çok yardımcı olur, örneğin veriler zaten bir arabellekte.
Ian Ringrose
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.