2015-01-21 6 views
15

VB.NET 2010 ile hesaplamalı yoğun bir program yazıyorum ve hızı optimize etmek istiyorum. İşlemin sınıf düzeyi değişkenine atanması durumunda AndAlso ve OrElse operatörlerinin anormal derecede yavaş olduğunu görüyorum. Örneğin, ifadeleriAndAlso OrElse anormal derecede yavaş olabilir

a = _b AndAlso _c 
_a = a 

, derlenmiş exe

_a = _b AndAlso _c 

yaklaşık 80 makine devir alır tek deyimi aralarında yaklaşık 6 makine döngülerini alırken. Burada _a, _b ve _c Özel Boolean değişkenleri Form1'dır ve söz konusu ifadeler, örneğinin bir örneğidir, bunların a yerel bir Boole değişkenidir.

Tek deyimin neden bu kadar uzun sürdüğünü bulamıyorum.

Instruction    Explanation        Stack 
00: ldarg.0    Push Me (ref to current inst of Form1) Me 
01: ldarg.0    Push Me         Me, Me 
02: ldfld bool Form1::_b Pop Me, read _b and push it    _b, Me 
07: brfalse.s 11   Pop _b; if false, branch to 11   Me 
09: ldarg.0    (_b true) Push Me      Me, Me 
0a: ldfld bool Form1::_c (_b true) Pop Me, read _c and push it _c, Me 
0f: brtrue.s 14   (_b true) Pop _c; if true, branch to 14 Me 
11: ldc.i4.0    (_b, _c not both true) Push result 0  result, Me 
12: br.s 15    Jump unconditionally to 15    result, Me 
----- 
14: ldc.i4.1    (_b, _c both true) Push result 1   result, Me 
15: stfld bool Form1::_a Pop result and Me; write result to _a (empty) 
1a: 

kimse deyimi _a = _b AndAlso _c 80 makine döngülerini yerine öngörülen 5 ya da öylesine alır neden aydınlatabilir: Ben iyi görünüyor CIL kod düzeyine NetReflector aşağı kullanarak gezindiğini?

Windows XP'yi .NET 4.0 ve Visual Studio Express 2010 ile kullanıyorum. Zamanları, temelde bir StopView nesnesini kullanan ve 1000'den fazla yineleme içeren bir For-Next döngüsünü zamanlamak için kullandığım, açıkça kirli bir pasaj ile ölçtüm. Söz konusu kodu ve boş bir For-Next döngüsü ile karşılaştırmak; Birkaç döngüyü boşa harcayan ve işlemcinin durmasını engelleyen her iki döngüye de yararsız bir talimat içerir. Ham ama amaçlarım için yeterince iyi.

+0

Bu yorum bazılarına kapalı görünüyorsa üzgünüm, ancak hesaplama hızını ve çevrim sürelerini aralıyor olsaydım - genel olarak VB.NET veya .NET kullanmıyordum. – TyCobb

+0

belki de daha fazla kod gönderirseniz, verimliliği artırmak için başka öneriler sunabiliriz. – Jeremy

+0

Sadece AND kullanmayı denediniz mi? Özellikle Boole değişkeniyle çalıştığınız için. –

cevap

12

Burada, bu kodu yavaşlatan iki etken vardır. Bunu IL'den göremiyorsunuz, sadece makine kodu size bir fikir verebilir.


Önce AndAlso operatörü ile ilişkilendirilen geneldir. Kısa devirli bir operatördür, sol taraftaki işleç False olarak değerlendirilirse sağ taraftaki işleneni değerlendirmez. Bu, makine kodunda bir şube gerektirir. Dallanma, bir işlemcinin yapabileceği en yavaş şeylerden biridir, boru hattının yıkanması riskini önlemek için şubenin ön tarafında no'lu olduğunu varsayın. Yanlış tahmin ederse, büyük bir vuruşu olacaktır. Çok iyi this post ile kaplıdır. a değişkeni oldukça rastgele ise tipik perf kaybı ve bu nedenle zayıf tahmin edilen dal yaklaşık% 500'dür.

Bunun yerine Ve işlecini kullanarak bu riski ortadan kaldırırsanız, makine kodunda bir dal gerektirmez. Bu sadece tek bir talimattır, AND işlemci tarafından uygulanır. Böyle bir ifadede AndAlso'yu desteklemenin hiçbir anlamı yoktur, sağ taraftaki işlenenin değerlendirilmesi durumunda hiçbir şey ters gitmez. Burada uygulanamaz, ancak IL bir dal gösterse bile, jitter hala bir CMOV komutuyla (koşullu hareket) makine kodunu dalsız yapabilir.


Ama senin için en anlamlı Formu sınıfı MarshalByRefObject sınıfından miras olmasıdır. Miras zinciri, MarshalByRefObject> Bileşen> Denetim> ScrollableControl> ContainerControl> Form'dur.

MBRO, Just-in-Time derleyicisi tarafından özel olarak ele alınır, kod, başka bir AppDomain'de veya başka bir makinede yaşayan gerçek nesneyle sınıf nesnesi için bir proxy ile çalışıyor olabilir.Bir vekil, sınıfın hemen her türlü üyesi için titremeye şeffaftır, basit yöntem çağrıları olarak uygulanırlar. Alanlar haricinde, bir alana erişim bir yöntem çağrısı değil, bir bellek okuma/yazma ile yapıldığı için proxy edilemezler. Jitter nesnenin yerel olduğunu kanıtlayamazsa, JIT_GetFieldXxx() ve JIT_SetFieldXxx() adındaki yardımcı yöntemleri kullanarak CLR'ye çağırmak zorunda kalır. CLR, nesne referansının bir proxy veya gerçek anlaşma olup olmadığını ve farkla ilgili olup olmadığını bilir. Ek yük oldukça önemli, 80 döngü sağa doğru geliyor.

Değişkenler Form sınıfınızın üyeleri olduğu sürece bu konuda yapabileceğiniz fazla bir şey yoktur. Onları bir yardımcı sınıfına taşımak, geçici çözümdür.

+0

Güzel! Akıllılığınız için teşekkürler :) – Jeremy

+3

Harika! Çok teşekkür ederim. Değişkenleri bir yardımcı sınıfa taşıdığımda, talimat 80 yerine 3½ döngü alır. –

+2

Güzel sonuç. Q + A bundan daha iyi olamaz. Lütfen sorunuzu güncelleyin ve nasıl ölçtüğünüze dair birkaç kelime harcayın, yeterli programcı bunu yapmayın. –