2015-07-10 13 views
6

Kısa bir süre önce C# öğrenmeye başladım ve bu For loop yazdığımda kod çıktımın çift sayısını iki şekilde aldığını öğrendim. Benim için sezgisel olan sürüm 2'nin sözdizimini öğrendim. Ancak, sürüm 1, çevrimiçi olarak başka bir yerde bulunan örneklenmişti. İki versiyon arasında bir fark olup olmadığını anlamak istiyorum.Konsol projemde çift sayı üretmek için daha verimli sözdizimi nedir?

//Version 1 
for (int num = 0; num < 100; num+=2) 
{ 
    Console.WriteLine (num); 
} 

//Version 2 
for (int num = 0; num < 100; num++) 
{ 
    if (num % 2 == 0) 
    { 
     Console.WriteLine (num); 
    } 
} 

İki olası yol arasında, iki sözdizimi arasında bir fark var mı? Eğer evet ise, hangisi daha verimli ve neden?

+5

techincally sürüm 1 hızlıdır versiyon # daha 2,005 kat daha yavaş olduğunu ama gerçekten işlemcilerin hızı ile önemli değildir. – dbarnes

+0

Hmmm Anlıyorum .. Ama her iki versiyon da sözdiziminden bağımsız olarak aynı şeyi deniyor? Ve sürüm 1 daha hızlıdır, çünkü daha küçük bir koddur? @dbarnes – bmeredit

+4

Sözdizimi, gerçek kodun muhtemelen derleyici optimizasyonlarından dolayı yazılı olarak yazılmayacağından etkilenmez. Daha hızlı kılan şey, döngüsünüzün 100 yerine 50 adım olmasıdır. Algoritma hızının ne zaman alakalı olduğu hakkında daha fazla bilgi edinmek istiyorsanız, Big-O notasyonuna bakın. – dbarnes

cevap

5

(daki örnek kodu, N = 100)

  • Versiyon # 1 yapar N/2 ilave
  • Versiyon # 2 N eklemeler, artı N tamsayı bölümleri (nispeten pahalı bir işlem) yapar.

Ve Sürüm 3, bit çevirerek oynamanın unuttum. Bit düzey işlemleri bölümü daha bir sürü daha ucuz, ve biz C# dünyasında tamsayılar two's-tamamlayıcı ikili değerler olduğunu bildiğimiz için, düşük dereceden bitinin durum dolayısıyla, bir tamsayı bile olup olmadığını söyler:

bool isEven(int n) { return 0 == (n & 1) ; } 

Biz böyle bir testi koşum takımı, yazabilirsiniz:

class Program 
{ 
    public static int Version1(int n) 
    { 
    int cnt = 0; 
    for (int i = 0 ; i < n ; i+=2) 
    { 
     ++cnt; 
    } 
    return cnt; 
    } 

    public static int Version2(int n) 
    { 
    int cnt = 0; 
    for (int i = 0 ; i < n ; ++i) 
    { 
     if (i % 2 == 0) 
     { 
     ++cnt; 
     } 
    } 
    return cnt; 
    } 

    public static int Version3(int n) 
    { 
    int cnt = 0; 
    for (int i = 0 ; i < n ; ++i) 
    { 
     if (0 == (i & 1)) 
     { 
     ++cnt; 
     } 
    } 
    return cnt; 
    } 

    private static void Main(string[] args) 
    { 
    int n = 500000000; 
    Stopwatch timer = new Stopwatch(); 

    timer.Start(); 
    Version1(n); 
    timer.Stop(); 
    Console.WriteLine("{0:c} -- Version #1 (incrementing by 2)" , timer.Elapsed) ; 

    timer.Restart(); 
    Version2(n); 
    timer.Stop(); 
    Console.WriteLine("{0:c} -- Version #2 (incrementing by 1 with modulo test)" , timer.Elapsed) ; 

    timer.Restart(); 
    Version3(n); 
    timer.Stop(); 
    Console.WriteLine("{0:c} -- Version #3 (incrementing by 1 with bit twiddling)" , timer.Elapsed) ; 

    return; 

    } 

} 

Ve aslında daha hızlı olduğunu saptayın. Yukarıdaki program, döngüyü 500.000.000 kez çalıştırır, böylece ölçecek kadar büyük sayıları alırız.

İşte VS 2013 ile almak zamanlamaları şunlardır:

  • ayıklama (optimize edilmemiş) inşa:

    00:00:00.5500486 -- Version #1 (incrementing by 2) 
    00:00:02.0843776 -- Version #2 (incrementing by 1 with modulo test) 
    00:00:01.2507272 -- Version #3 (incrementing by 1 with bit twiddling) 
    
    • Versiyon 2. 3'tür.versiyon # daha yavaş 789 kere 1
    • Versiyon 3. versiyon # den 2,274 kat daha yavaş olan 1
  • Yayın inşa (optimize)

    00:00:00.1680107 -- Version #1 (incrementing by 2) 
    00:00:00.5109271 -- Version #2 (incrementing by 1 with modulo test) 
    00:00:00.3367961 -- Version #3 (incrementing by 1 with bit twiddling) 
    
    • Versiyon 2. 3.041 kat daha az sürüm # 1'den itibaren
    • Versiyon # 3 1
+0

Güzel cevap ... Yan not: "cnt" ile tam olarak hangi kelimeyi kastettiğinizden emin değilim (en azından bir sesli harf eksiktir) :) Ek olarak uygun zaman ölçümleri Start-up dışında/JIT çalıştırmalıdır/Dur ... –

+0

@AlexeiLevenkov OP değil ama "cnt", "sayım" da olsa oldukça standart bir standart olduğunu düşündüm. – Sinkingpoint

+0

@Quirliom Orada smiley eklemeyi denedim ... –

1

Sürüm 1, aynı çıktıda yarım kez yinelemesi anlamında daha verimlidir. Sürüm 1, 50 kez yinelenir, burada sürüm 2, 100 kez yinelenir, ancak yalnızca sonuçların yarısını basar. Ancak, sanırım ikinci niyetinizi daha iyi gösteriyor.

+0

Katılmıyorum. İlk versiyon, adım artışı olarak amacını açıkça göstermektedir. – StarPilot

2

Aslında ilave mekanizması (Sürüm 1) 2.000.000 boyunca 0,0070000 milisaniye ile ekleme işleminin sonucu modülü (%) göre iyi

biraz daha iyi veya 200.000 yineleme

Ama eğer bir misyon kritik mikro optimizasyon ihtiyacını yoksa ciddi ..., kod yerine okunması kolay için modül operatörü ile sopa kabaca 00.0070000 milisaniye çalıştırma süresinden tasarruf etmeye çalışmaktan daha iyidir.

Here Is The Source

3

Sürüm 1 (derleme optimizasyonlar bağımsız) saf bir analiz açısından "daha hızlı" dir. Ama sorgunuzdaki yorumlarda dbarnes olarak bahsedildiği gibi, C# 'de kodladığınız ve derleyicinin diğer tarafının ne çıktığı tamamen farklı olabilir. Sürüm 2 "daha yavaştır" çünkü döngüde daha fazla döngü yinelemesi vardır ve bu, if ifadesiyle biraz daha büyük kod karmaşıklığına sahiptir. (Bunu veo çözme değer bir sorun kanıtlayabilirim varlığından haberdar önce performansla ilgili olmak o "erken optimizasyonu" olarak adlandırılır)

ilk başta optimizasyonu ile kendinizi çok fazla endişelenmeyin. Sadece öğrenmek için kodlayın, sonra yeniden düzenleyin, sonra varsa performans sorunlarına bakın. Aslında, derleyicinin, bu döngülerin her ikisini de "açmak" üzere yapılandırılabileceğini tahmin ediyorum: bu döngüden kurtulur ve döngü gövdesini sırasıyla 1 veya 2 için 50 veya 100 kez çoğaltır.

3

.Net çerçevesinde "Tick" olarak da bilinen, 100.000 milisaniyelik bir hassasiyetle hangi yöntemin daha hızlı olduğunu tam olarak ölçebilirsiniz.

Bu System.Diagnostic ad alanında Kronometre sınıfını kullanarak yapılır:

var sw1 = new System.Diagnostics.Stopwatch(); 
var sw2 = new System.Diagnostics.Stopwatch(); 

//Version 1 
sw1.Start(); 
for (int num = 0; num < 100; num += 2) 
{ 
    Console.WriteLine(num); 
} 
sw1.Stop(); 

//Version 2 
sw2.Start(); 
for (int num = 0; num < 100; num++) 
{ 
    if (num % 2 == 0) 
    { 
     Console.WriteLine(num); 
    } 
} 
sw2.Stop(); 

Console.Clear(); 
Console.WriteLine("Ticks for first method: " + sw1.ElapsedTicks); 
Console.WriteLine("Ticks for second method: " + sw2.ElapsedTicks); 

çıktı ilk yöntem daha hızlı olduğunu gösterecektir.

Bu neden böyle? İlk versiyonda, konsol çıktısını dikkate almazsak, sadece bir işlem yapılır (+= 2) ve sonuçta program döngü 50 döngüsünden geçer.

İkinci sürümde, iki işlem (++ ve % 2) ve döngüde 100 döngüye ek olarak bir karşılaştırma (num % 2 == 0) yapılması gerekir. Tam olarak böyle bir program ölçerseniz

, o zaman birden milisaniye gibi zaman içinde büyük bir fark, sahip olacaktır. Bunun nedeni Console.WriteLine aslında çok zaman alıyor. İkinci versiyonda 50 kat daha fazla yapıldığı için, daha fazla zaman alıyor. Algoritmayı tek başına ölçmek istiyorsanız, konsol çıktısını atın.

sen benim makinede keneler farkı ortalama 43 keneler 24 kene olduğunu, bunu yaparsan.

Yani sonuç olarak, ilk yöntem hakkında 19/10000 milisaniye tarafından, daha verimlidir.

+3

Tabii ki, eğer çıktıyı çıkarırsanız, _ ve optimizasyonları on_ yaparsanız, hiçbir döngü hiçbir şey elde edemez ve böylece _could_ sıfır zaman alır. –

+7

"çünkü [' WriteLine'] ikinci sürümde 50 kez daha tamamlandı "- bunun nasıl doğru olabileceğini görmüyorum ... –

1

Sürüm 1 daha etkilidir. "Sürüm 1", "sürüm 1" yerine bir karşılaştırma daha aldığından "sürüm 1" iki karşılaştırmayı alır.

//Version 1 
for (int num = 0; num < 100; num+=2)  // Two comparisons 
{ 
    Console.WriteLine (num); 
} 

//Version 2 
for (int num = 0; num < 100; num++)   // Two comparisons 
{ 
    if (num % 2 == 0)      // One comparison 
    { 
     Console.WriteLine (num); 
    } 
} 
0

Bu yöntem, yöntem 1'den ve yöntem 2'den daha hızlıdır çünkü yineleme döngüden daha hızlıdır.

 var sw3 = new System.Diagnostics.Stopwatch(); 
     Action<int, int> printEvens = null; 
     printEvens = (index, count) => 
     { 
      if (index % 2 == 0) 
       Console.WriteLine(index.ToString()); 
      if (index < count) 
       printEvens(++index, count); 
     }; 

     sw3.Start(); 
     printEvens(0, 100); 
     sw3.Stop(); 

benim makinede çıkış ..

Yöntem 1: 96.282 Yöntem 2: 184.336 Yöntem 3: N belirli bir değeri için yineleme 59.188

+0

Bu daha verimli, ama ne soruluyordu. –

+1

Genel olarak yineleme, C# 'da döngüden * daha * daha hızlıdır. –

+0

Eğer konsol.writeline'ın yöntem 1 ve 2 zaman damgası aynı çıkar ve 8 ticks gibi HIZLI. Bu senaryo yönteminde 3, saçmadır, 459. Ancak, GUI mantığını eklediğinizde, yöntem 3 daha hızlıdır. Emin değilim neden. Ya da yinelemeleri arttırırsanız, 3. yöntemin daha yavaş ve daha yavaş olduğunu görürsünüz. Bu yüzden yığın izinin daha büyük özyinelemesinin daha yavaş ilerlediğini düşünürdüm. Böylece daha derin özyineleme daha yavaş gidiyor. –

İlgili konular