2017-08-29 68 views
10

Kodu: özdeş usings içindeAynı kod bloğu yürütme süresinin farklı nedeni nedir?

internal class Program 
{ 
    private static void Main(string[] args) 
    { 
     const int iterCount = 999999999; 

     var sum1 = 0; 
     var sum2 = 0; 

     using (new Dis()) 
     { 
      var sw = DateTime.Now; 
      for (var i = 0; i < iterCount; i++) 
       sum1 += i; 
      Console.WriteLine(sum1); 
      Console.WriteLine(DateTime.Now - sw); 
     } 

     using (new Dis()) 
     { 
      var sw = DateTime.Now; 
      for (var i = 0; i < iterCount; i++) 
       sum2 += i; 
      Console.WriteLine(sum2); 
      Console.WriteLine(DateTime.Now - sw); 
     } 

     Console.ReadLine(); 
    } 

    private class Dis : IDisposable 
    { 
     public void Dispose(){} 
    } 
} 

İki özdeş blokları.

Çıktı:

2051657985 
00:00:00.3690996 
2051657985 
00:00:02.2640266 

İkinci blok 2.2 saniye sürer! Ama eğer kullanımdan kurtulursanız, süreler aynı oldu (ilk olarak ~ 0.3s). .net framework 4.5 ve .net core 1.1 ile denedim, sürümde, sonuçlar aynı.

Bu davranışı kimse açıklayabilir mi?

+2

Belki de 'Console.WriteLine' yerine değişkenlerle deneyin?Bu sorun olabilir. – ispiro

+0

Kodunuzu linqpad'de çalıştırdığımda, her iki kod bloğu için de ~ 2.5 saniye verir. Ideone üzerinde çalışırken her iki blok için de ~ 0,3 saniye verir: https://ideone.com/ReOpaH. – Chris

+2

Ayrıca uygun bir kronometre kullanın. Tarih saatini, kronometreyi kullanmak istediğinizi belirtiyorsunuz ama o zaman bir tane kullanmıyorsunuz. – Chris

cevap

13

Alttaki sebebi görmek için jitter'in oluşturduğu makine koduna bakmanız gerekir. Araçlar> Seçenekler> Hata ayıklama> Genel> JIT optimizasyonunu kaldır seçeneğinin işaretini kaldırın. Sürüm oluşturmaya geçin. Birinci ve ikinci döngülerdeki bir kesme noktası ayarlayın. Ne zaman hata ayıklama> Windows> Disassembly.

Sen döngü organları için makine kodunu görürsünüz:

    sum1 += i; 
00000035 add   esi,eax 

Ve:

    sum2 += i; 
000000d9 add   dword ptr [ebp-24h],eax 

Veya başka bir deyişle

, sum1 değişken CPU kayıt esi saklanır. Ancak sum2 değişken, bellekte, yöntemin yığın çerçevesinde saklanır. Büyük, büyük fark. Kayıtlar çok hızlı, hafıza yavaş. Yığın çerçevesi için bellek, L1 önbelleğinde, bu önbelleğe erişen 3 makinenin gecikme süresine sahip olacak. Mağaza tamponu, çok sayıda yazma ile hızlıca boğulur ve bu da işlemcinin durmasına neden olur.

Değişkenleri CPU kayıt defterinde tutmanın bir yolu bulmak one of the primary jitter optimization duties. Ama bunun sınırlamaları var, özellikle x86'nın az sayıda kaydı var. Hepsi tamamlandığında, jitter seçeneğinin bir seçeneği yoktur, ancak bunun yerine belleği kullanır. using ifadesinin başlık altında ek bir gizli yerel değişkene sahip olduğunu unutmayın, bu nedenle bir etkisi vardır.

İdeal olarak, jitter optimize edici, yazmaçların nasıl ayrılacağı konusunda daha iyi bir seçim yapmış olur. Döngü değişkenleri (bunu yaptı) ve toplam değişkenler için bunları kullanma. Önde gelen bir derleyici, kod analizini gerçekleştirmek için yeterli zamana sahip olur. Ancak tam zamanında bir derleyici sıkı zaman kısıtlamaları altında çalışır.

Temel karşı önlemler şunlardır:

    ayrı yöntemlerle içine kodunu böylece ESI gibi bir kayıt yeniden kullanılabilir ayrıl
  • .
  • Titreşmeyi zorla kaldırma (Proje> Özellikler> Yapılandırma sekmesi> "32 bit tercih et" seç). x64 8 ek kayıt sağlar.

son kurşun eski x64 titreme (kullanmak .NET 3.5 hedef) için geçerlidir, ama ilk 4,6 kullanılabilir hale x64 seğirme yeniden yazma (aka RYuJIT) için. Eski bir jitter, kodu optimize etmek için çok zaman harcadığı için gerekli olan bir yeniden yazma. Hayal kırıklığı yaratan RyuJIT, hayal kırıklığı yaratan bir şöhrete sahip. Sanırım optimize edicisi burada daha iyi bir iş çıkartabilir.

+0

Teşekkürler, cevap gibi görünüyor! ilginç olarak, toplam 2 (dekal 2 = 0;) bildirimini bloklar arasında yerleştirmek için hareket ettirirsek, kayıtlar her iki döngüde de kullanılır. –