Bu yüzden, dene / yakala'nın biraz ek yük getirdiğini ve bu nedenle süreç akışını kontrol etmenin iyi bir yolu olmadığını biliyorum, ancak bu ek yük nereden geliyor ve gerçek etkisi nedir?
Yanıtlar:
Dil uygulamaları konusunda uzman değilim (bu yüzden bunu biraz tuzla alın), ancak bence en büyük maliyetlerden biri yığını açmak ve yığın izleme için depolamak. Bunun yalnızca istisna atıldığında gerçekleşeceğinden şüpheleniyorum (ama bilmiyorum) ve öyleyse, her istisna atıldığında bu makul boyutta gizli maliyet olacaktır ... bu yüzden sadece bir yerden atlıyormuşsunuz gibi değil kodda diğerine, çok şey oluyor.
OLAĞANÜSTÜ davranış için istisnalar kullandığınız sürece bunun bir sorun olduğunu sanmıyorum (yani program boyunca tipik, beklenen yolunuz değil).
Burada yapılması gereken üç nokta:
İlk olarak, kodunuzda gerçekten dene-yakala bloklarının bulunmasında çok az veya YOK performans cezası vardır. Bunları başvurunuzda bulundurmaktan kaçınmaya çalışırken bu dikkate alınmamalıdır. Performans vuruşu, yalnızca bir istisna atıldığında devreye girer.
Başkalarının bahsettiği gibi gerçekleşen yığın çözme işlemlerine ek olarak bir istisna atıldığında, yığın izleme gibi istisna sınıfının üyelerini doldurmak için bir dizi çalışma zamanı / yansıma ile ilgili şeyin gerçekleştiğinin farkında olmalısınız. nesne ve çeşitli tip üyeleri vb.
İnanıyorum ki throw;
, istisnayı yeniden atacaksanız, genel tavsiyenin , istisnayı tekrar atmaktan veya bu durumlarda olduğu gibi yeni bir tane oluşturmaktan ziyade, tüm bu yığın bilgilerinin yeniden toplanması olmasının nedenlerinden biri olduğuna inanıyorum. Atın hepsi korunur.
throw new Exception("Wrapping layer’s error", ex);
İstisnalar atılmadığında dene / yakala / nihayet kullanmanın ek yükünü mü yoksa süreç akışını kontrol etmek için istisnaları kullanmanın ek yükünü mü soruyorsunuz? İkincisi, bir yürümeye başlayan çocuğun doğum günü mumunu yakmak için bir dinamit çubuğu kullanmaya benzer ve ilgili ek yük aşağıdaki alanlara düşer:
Yerleşik olmayan koda ve normal olarak uygulamanızın çalışma kümesinde olmayan verilere erişme istisnası nedeniyle ek sayfa hataları bekleyebilirsiniz.
Yukarıdaki öğelerin her ikisi de genellikle "soğuk" koda ve verilere erişir, bu nedenle, bellek baskınız varsa, sabit sayfa hataları olasıdır:
Maliyetin gerçek etkisine gelince, bu, o sırada kodunuzda başka neler olup bittiğine bağlı olarak çok değişebilir. Jon Skeet'in burada bazı yararlı bağlantılarla birlikte iyi bir özeti var . İstisnaların performansınıza önemli ölçüde zarar verdiği noktaya gelirseniz, sadece performansın ötesinde istisnaları kullanımınız açısından sorunlarınız olduğu şeklindeki ifadesine katılma eğilimindeyim.
Tecrübelerime göre en büyük ek yük aslında bir istisna atmak ve onu ele almaktır. Bir keresinde, aşağıdakine benzer bir kodun, birinin bir nesneyi düzenleme hakkına sahip olup olmadığını kontrol etmek için kullanıldığı bir proje üzerinde çalıştım. Bu HasRight () yöntemi, sunum katmanının her yerinde kullanıldı ve genellikle 100'lerce nesne için çağrıldı.
bool HasRight(string rightName, DomainObject obj) {
try {
CheckRight(rightName, obj);
return true;
}
catch (Exception ex) {
return false;
}
}
void CheckRight(string rightName, DomainObject obj) {
if (!_user.Rights.Contains(rightName))
throw new Exception();
}
Test veritabanı test verileriyle dolduğunda, bu durum yeni formlar açılırken çok gözle görülür bir yavaşlamaya neden olur.
Bu yüzden onu aşağıdaki şekilde yeniden düzenledim, ki bu - sonraki hızlı 'n kirli ölçümlere göre - yaklaşık 2 kat daha hızlı:
bool HasRight(string rightName, DomainObject obj) {
return _user.Rights.Contains(rightName);
}
void CheckRight(string rightName, DomainObject obj) {
if (!HasRight(rightName, obj))
throw new Exception();
}
Kısacası, normal süreç akışında istisnalar kullanmak, istisnasız benzer süreç akışını kullanmaktan yaklaşık iki kat daha yavaştır.
Teorileri aksine yaygın kabul try
/ catch
önemli performans etkileri olabilir ve en bir istisnası atılır mı o ya da değil!
Eski yıllar içinde Microsoft MVP'leri tarafından blog gönderisi birkaç kaplıydı ve ben bunları kolayca bulabiliriz güven henüz StackOverflow umurunda olmuştur bu kadar ilgili içerik Ben bazıları bağlantılar sağlamaktayız böylece dolgu kanıt:
try
/ catch
/finally
( ve bölüm iki ) Peter Ritchie hangi optimizasyon araştırıyor tarafındantry
/catch
/finally
devre dışı bırakır (ve standardından tırnak bu işe daha da gidersiniz)Parse
vs TryParse
vsConvertTo
Ian Huff tarafından "istisna işleme çok yavaş" ve delik tarafından bu noktayı gösterdiğini pervasızca devletlerInt.Parse
veInt.TryParse
ısrar herkes için birbirlerine ... karşıTryParse
kullandığıtry
/catch
perde arkasında, bu biraz ışık tutmak gerektiğini!Ayrıca / kullanmadan ve kullanmadan demonte kod arasındaki farkı gösteren bu cevap da var .try
catch
Orada o kadar aşikardır olan kod nesil pervasızca gözlemlenebilir bir ek yük ve bu yükü bile kim Microsoft değer insanlar tarafından kabul edilecek gibi görünüyor! Yine de interneti tekrar ediyorum ...
Evet, önemsiz bir kod satırı için düzinelerce fazladan MSIL talimatı vardır ve bu, devre dışı bırakılan optimizasyonları kapsamaz, bu yüzden teknik olarak bir mikro optimizasyondur.
Yıllar önce, programcıların üretkenliğine (makro optimizasyon) odaklandığı için silinen bir cevap gönderdim.
Bu talihsiz bir durumdur, çünkü burada birkaç nanosaniye tasarrufunun olmaması ve orada CPU zamanının, insanlar tarafından biriktirilen birçok saatlik manuel optimizasyonu telafi etmesi muhtemeldir. Patronunuz hangisine daha fazla ödüyor: zamanınızın bir saati mi yoksa bilgisayar çalışırken bir saat mi? Hangi noktada fişi çekip daha hızlı bir bilgisayar satın almanın zamanının geldiğini kabul ediyoruz ?
Açıktır ki, sadece kodumuzu değil, önceliklerimizi de optimize etmeliyiz ! Son cevabımda iki kod parçacığı arasındaki farklara değindim.
try
/ Kullanarak catch
:
int x;
try {
x = int.Parse("1234");
}
catch {
return;
}
// some more code here...
try
/ Kullanmıyor catch
:
int x;
if (int.TryParse("1234", out x) == false) {
return;
}
// some more code here
Profil oluşturma / optimizasyon (yukarıda ele alınmıştır) olmasa bile, zamanınızı boşa harcama olasılığı daha yüksek olan bir bakım geliştiricisinin perspektifinden düşünün; bu, try
/ catch
sorun için olmasaydı muhtemelen gerekli olmayacaktı , ardından kaydırarak kaynak kodu ... Bunlardan birinde fazladan dört satırlık standart çöp var!
Bir sınıfa daha fazla alan girdikçe, tüm bu standart çöpler (hem kaynakta hem de parçalanmış kodda) makul seviyelerin çok ötesinde birikir. Alan başına dört ekstra satır ve bunlar her zaman aynı çizgiler ... Kendimizi tekrar etmekten kaçınmamız öğretilmedi mi? Sanırım ev yapımı soyutlamanın arkasına try
/ catch
arkasına saklayabiliriz , ama ... o zaman istisnalardan da kaçabiliriz (yani kullanım Int.TryParse
).
Bu karmaşık bir örnek bile değil; try
/ İçinde yeni sınıfları örnekleme girişimlerini gördüm catch
. Yapıcının içindeki tüm kodun, aksi takdirde derleyici tarafından otomatik olarak uygulanacak olan belirli optimizasyonlardan diskalifiye edilebileceğini düşünün. Derleyicinin yapması söyleneni tam olarak yapması yerine , derleyicinin yavaş olduğu teorisini ortaya çıkarmanın daha iyi bir yolu var mı?
Söz konusu kurucu tarafından bir istisna atıldığını ve sonuç olarak bazı hataların tetiklendiğini varsayarsak, zayıf bakım geliştiricisinin daha sonra onu izlemesi gerekir. Bu, bir spagetti kod farklı olarak, bu tür kolay bir iş olmayabilir git kabus, try
/ catch
de pisliklerimizi neden olabilir üç boyutta da diğer sınıfları ve yöntemleri, aynı yöntemin sadece değil diğer parçaya yığın hareket ama verebileceğinden bunların hepsi bakım geliştiricisi tarafından zor yoldan izlenecek ! Yine de bize "gitmenin tehlikeli" olduğu söylendi, heh!
Son ben bahsedilince, try
/ catch
, olan da yararı var o devre dışı optimizasyonlar için tasarlandı ! Bu bir hata ayıklama aracıdır ! Bunun için tasarlandı ve bunun için kullanılması gerekiyor ...
Sanırım bu da olumlu bir nokta. Aksi takdirde, çok iş parçacıklı uygulamalar için güvenli, mantıklı mesaj geçirme algoritmalarını engelleyebilecek optimizasyonları devre dışı bırakmak ve olası yarış koşullarını yakalamak için kullanılabilir;) Bu, dene / yakala kullanmayı düşünebildiğim tek senaryo ile ilgili. Bunun bile alternatifleri var.
Ne optimizasyon yapmak try
, catch
ve finally
devre dışı?
DİĞER ADIYLA
Nasılsın try
, catch
ve finally
yardımcıları ayıklama gibi yararlı?
onlar yazma engelleri. Bu standarttan gelir:
12.3.3.13 Try-catch ifadeleri
Bir açıklama için stmt formunun:
try try-block catch ( ... ) catch-block-1 ... catch ( ... ) catch-block-n
- Kesin atama durumu v başında deneyin blok kesin atama durum aynıdır v başlangıcında Stmt .
- Catch-block-i'nin başlangıcındaki v'nin kesin atama durumu (herhangi bir i için ), stmt'nin başlangıcındaki v'nin kesin atama durumu ile aynıdır .
- Kesin atama durumu v uç noktasında Stmt (ve sadece) kesinlikle atanır v kesinlikle uç noktasında atanan deneyin blok ve her yakalama blok-ı (her için i 1 ila n ).
Diğer bir deyişle, her try
ifadenin başında :
try
tamamlanmalıdır, bu da bir başlangıç için bir iş parçacığı kilidi gerektirir, bu da yarış koşullarında hata ayıklama için yararlıdır!try
ifadeden önce kesinlikle atanmış olan kullanılmayan değişken atamalarını ortadan kaldırınHer catch
ifade için benzer bir hikaye geçerlidir ; İfadenizde try
(veya bir kurucu veya işlev, vb.) aksi takdirde anlamsız bir değişkene atadığınızı varsayalım (diyelim ki garbage=42;
), derleyici, programın gözlemlenebilir davranışıyla ne kadar alakasız olursa olsun bu ifadeyi ortadan kaldıramaz. . Blok girilmeden önce atamanın tamamlanmış olması gerekir catch
.
Değeri ne olursa olsun, finally
benzer şekilde aşağılayıcı bir hikaye anlatır :
12.3.3.14 Son olarak dene ifadeleri
Bir İçin deneyin deyim stmt formunun:
try try-block finally finally-block
• kesin atama durumu v başında deneyin blok kesin atama durum aynıdır v başlangıcında Stmt .
• kesin atama durumu v başında son-blok kesin atama durum aynıdır v başlangıcında Stmt .
• Stmt'nin son noktasındaki v'nin kesin atama durumu, aşağıdakilerden biri olması durumunda (ve yalnızca şu durumlarda) kesinlikle atanır : o v kesinlikle try bloğunun son noktasına atanır o vKesinlikle sonu noktasında atanır nihayet blok (gibi bir kontrol akış transferi Eğer git deyimi) o içinde başlar yapılır deneme-block ve dışarıda uçları deneyin blok , daha sonra v de kesinlikle bu konuda atanmış kabul edilir v kesinlikle nihayet bloğunun son noktasında atanmışsa akış transferini kontrol edin . (Bu yalnızca , bu kontrol akışı transferinde v kesinlikle başka bir nedenle atanmışsa, yine de kesinlikle atanmış kabul edilirse değildir.)
12.3.3.15 Sonunda dene ifadeleri
Bir dene - yakala - nihayet formun ifadesi için kesin atama analizi :
try try-block catch ( ... ) catch-block-1 ... catch ( ... ) catch-block-n finally finally-block
ifadesi bir sanki yapılır deneyin - Sonunda bir çevreleyen deyimi deneyin - catch deyimi:
try { try try-block catch ( ... ) catch-block-1 ... catch ( ... ) catch-block-n } finally finally-block
Sıklıkla adlandırılan bir yöntemin içinde olup olmadığından bahsetmiyorum bile, uygulamanın genel davranışını etkileyebilir.
Örneğin, başka türlü kolayca yakalanabilecek bir şey için istisnalar attığından, çoğu durumda Int32.Parse kullanımını kötü bir uygulama olarak görüyorum.
Dolayısıyla, burada yazılan her şeyi bitirmek için:
1) Beklenmedik hataları yakalamak için try..catch bloklarını kullanın - neredeyse hiç performans kesintisi yok.
2) Önleyebiliyorsanız, istisnai hatalar için istisnalar kullanmayın.
Bir süre önce bununla ilgili bir makale yazmıştım çünkü o zamanlar bunu soran birçok insan vardı. Bunu ve test kodunu http://www.blackwasp.co.uk/SpeedTest TryCatch.aspx adresinde bulabilirsiniz .
Sonuç olarak, bir dene / yakala bloğu için çok küçük bir ek yük vardır, ancak göz ardı edilmesi gereken çok küçüktür. Bununla birlikte, milyonlarca kez çalıştırılan döngülerde dene / yakala blokları çalıştırıyorsanız, mümkünse bloğu döngünün dışına taşımayı düşünebilirsiniz.
Try / catch bloklarıyla ilgili temel performans sorunu, gerçekten bir istisna yakaladığınız zamandır. Bu, uygulamanıza gözle görülür bir gecikme ekleyebilir. Elbette, işler ters gittiğinde, çoğu geliştirici (ve birçok kullanıcı) duraklamayı, gerçekleşmek üzere olan bir istisna olarak kabul eder! Buradaki anahtar, normal işlemler için istisna işlemeyi kullanmamaktır. Adından da anlaşılacağı gibi, istisnaidirler ve atılmalarını önlemek için elinizden geleni yapmalısınız. Bunları düzgün çalışan bir programın beklenen akışının bir parçası olarak kullanmamalısınız.
Geçen yıl bu konuyla ilgili bir blog yazısı yaptım . Buna bir bak. Alt satırda, herhangi bir istisna oluşmazsa bir deneme bloğu için neredeyse hiçbir maliyet yoktur - ve dizüstü bilgisayarımda bir istisna yaklaşık 36μs idi. Bu beklediğinizden daha az olabilir, ancak bu sonuçların sığ bir yığında olduğunu unutmayın. Ayrıca, ilk istisnalar gerçekten yavaştır.
try
/ catch
çok mu? Heh heh), ancak dil özellikleri ve konuyla ilgili bloglar yazan bazı MS MVP'lerle tartışıyor gibisiniz, ölçümler sağlıyor Tavsiyenizin aksine ... Yaptığım araştırmanın yanlış olduğu fikrine açığım, ancak ne yazdığını görmek için blog girişinizi okumam gerekecek.
try-catch
blok ve tryparse()
yöntemleri hedefliyor , ancak kavram aynı.
Derleyici hata mesajları, kod analizi uyarı mesajları ve rutin olarak kabul edilen istisnalar (özellikle bir yere atılan ve başka bir yerde kabul edilen istisnalar) içermeyen kod yazmak, hata ayıklamak ve sürdürmek çok daha kolaydır. Daha kolay olduğu için, kod ortalama olarak daha iyi yazılır ve daha az hatalıdır.
Bana göre, bu programcı ve kalite ek yükü, süreç akışı için try-catch kullanımına karşı birincil argümandır.
İstisnaların bilgisayar yükü, kıyaslandığında önemsizdir ve uygulamanın gerçek dünya performans gereksinimlerini karşılama yeteneği açısından genellikle küçüktür.
Hafthor'un blog gönderisini gerçekten çok seviyorum ve bu tartışmaya iki sentimi de eklemek için şunu söylemek isterim ki, DATA LAYER'ın yalnızca bir tür istisna (DataAccessException) atmasını sağlamak benim için her zaman kolay olmuştur. Bu şekilde İŞ KATMANIM, hangi istisnayı bekleyeceğini bilir ve yakalar. Daha sonra diğer iş kurallarına bağlı olarak (yani iş nesnem iş akışına katılırsa, vb.), Yeni bir istisna (BusinessObjectException) atabilir veya yeniden / atmadan devam edebilirim.
Gerektiğinde try..catch'i kullanmaktan çekinmeyin ve akıllıca kullanın!
Örneğin, bu yöntem bir iş akışına katılır ...
Yorumlar?
public bool DeleteGallery(int id)
{
try
{
using (var transaction = new DbTransactionManager())
{
try
{
transaction.BeginTransaction();
_galleryRepository.DeleteGallery(id, transaction);
_galleryRepository.DeletePictures(id, transaction);
FileManager.DeleteAll(id);
transaction.Commit();
}
catch (DataAccessException ex)
{
Logger.Log(ex);
transaction.Rollback();
throw new BusinessObjectException("Cannot delete gallery. Ensure business rules and try again.", ex);
}
}
}
catch (DbTransactionException ex)
{
Logger.Log(ex);
throw new BusinessObjectException("Cannot delete gallery.", ex);
}
return true;
}
Michael L. Scott'ın Programming Languages Pragmatics adlı kitabında, günümüzdeki derleyicilerin genel durumda herhangi bir ek yük eklemediğini okuyabiliriz, bu, istisnaların olmadığı anlamına gelir. Böylece her çalışma derleme zamanında yapılır. Ancak çalışma zamanında bir istisna atıldığında, derleyicinin doğru istisnayı bulmak için ikili arama yapması gerekir ve bu yaptığınız her yeni atışta gerçekleşir.
Ancak istisnalar istisnadır ve bu maliyet tamamen kabul edilebilir. İstisnasız İstisna İşlemeyi yapmaya çalışırsanız ve bunun yerine dönüş hata kodlarını kullanırsanız, muhtemelen her alt rutin için bir if ifadesine ihtiyacınız olacak ve bu gerçekten gerçek zamanlı bir ek yüke neden olacaktır. Bir if ifadesinin, alt rutinlerinize her girdiğinizde gerçekleştirilecek birkaç montaj talimatına dönüştürüldüğünü bilirsiniz.
İngilizcem için üzgünüm, umarım sana yardımcı olur. Bu bilgiler alıntılanan kitaba dayanmaktadır, daha fazla bilgi için Bölüm 8.5 İstisna İşlemlerine bakın.
Kullanılması gerekmeyen yerlerde kullanıldığında bir dene / yakala bloğunun olası en büyük maliyetlerinden birini analiz edelim:
int x;
try {
x = int.Parse("1234");
}
catch {
return;
}
// some more code here...
Ve işte denemeden / yakalamadan:
int x;
if (int.TryParse("1234", out x) == false) {
return;
}
// some more code here
Önemsiz beyaz boşluğu saymazsak, bu iki eşdeğer kod parçasının bayt cinsinden neredeyse tam olarak aynı uzunlukta olduğu fark edilebilir. İkincisi 4 bayt daha az girinti içerir. Bu kötü bir şey mi?
Yaralanmaya hakaret eklemek için öğrenci, girdi int olarak çözümlenebilirken döngü yapmaya karar verir. Try / catch olmadan çözüm şunun gibi bir şey olabilir:
while (int.TryParse(...))
{
...
}
Ama dene / yakala kullanılırken bu nasıl görünüyor?
try {
for (;;)
{
x = int.Parse(...);
...
}
}
catch
{
...
}
Dene / yakala blokları, girintiyi boşa harcamanın sihirli yollarıdır ve hala başarısız olmasının nedenini bile bilmiyoruz! Hata ayıklamayı yapan kişinin, güzel bir istisna hatasıyla durmak yerine, kod ciddi bir mantıksal kusurun ötesine geçmeye devam ettiğinde nasıl hissettiğini hayal edin. Try / catch blokları tembel bir adamın veri doğrulama / temizlemesidir.
Daha küçük maliyetlerden biri, dene / yakala bloklarının belirli optimizasyonları gerçekten devre dışı bırakmasıdır: http://msmvps.com/blogs/peterritchie/archive/2007/06/22/performance-implications-of-try-catch-finally.aspx . Sanırım bu da olumlu bir nokta. Aksi takdirde, çok iş parçacıklı uygulamalar için güvenli, aklı başında mesaj geçirme algoritmalarını engelleyebilecek optimizasyonları devre dışı bırakmak ve olası yarış koşullarını yakalamak için kullanılabilir;) Bu, dene / yakala kullanmayı düşündüğüm tek senaryo ile ilgili. Bunun bile alternatifleri var.
Int.Parse
lehine kaçındığınızda büyük performans artışının bir analizi Int.TryParse
.