Sonunda pahalı mı


14

Bir işlevden çıkmadan önce bir kaynak temizleme yapmanız gereken kod olması durumunda, bunu gerçekleştirmenin bu 2 yolu arasında büyük bir performans farkı vardır.

  1. Her iade ifadesinden önce kaynağı temizleme

    void func()
    {
        login();
    
        bool ret = dosomething();
        if(ret == false)
        {
            logout();
            return;
        }
    
        ret = dosomethingelse();
        if(ret == false)
        {
            logout();
            return;
        }
    
        dootherstuff();
    
        logout();
    }
    
  2. Sonunda bir blokta kaynağı temizleme

    void func()
    {
        login();
    
        try
        {
            bool ret = dosomething();
            if(ret == false)
                return;
    
            ret = dosomethingelse();
            if(ret == false)
                return;
    
            dootherstuff();
    
        }
        finally
        {
            logout();
        }
    }
    

Örnek programlarda bazı temel testler yaptım ve çok fazla bir fark yok gibi görünüyor. Bunu yapmayı çok tercih ediyorum finally- ama büyük bir projede herhangi bir performansa neden olup olmayacağını merak ediyordum.


3
Cevap olarak göndermeye değmez, ama hayır. İstisnaların varsayılan hata işleme stratejisi olmasını bekleyen Java kullanıyorsanız, try / catch / nihayet kullanmalısınız - dil, kullandığınız model için optimize edilmiştir.
Jonathan Rich

1
@JonathanRich: nasıl yani?
haylem

2
Kodu, gerçekleştirmeye çalıştığınız şeyi ifade edecek şekilde temiz bir şekilde yazın. Bir sorununuz olduğunu bildiğinizde daha sonra en iyileştirin - dil özelliklerinden kaçının çünkü ilk etapta yavaş olmayan bir şeyden birkaç milisaniye tıraş olabileceğinizi düşünüyorsunuz.
Sean McSomething

@mikeTheLiar - Eğer yazmam gerektiğini kastediyorsan, bunu if(!cond)yapmamı sağlayan java. C ++, ben hem booleans hem de diğer türleri için kod yazma böyle - yani int x; if(!x). Java bunu sadece bana izin verdiğinden booleans, tamamen if(cond)& if(!cond)in java kullanmayı bıraktım .
user93353 7:13

1
@ user93353 bu beni üzüyor. (someIntValue != 0)Booleanları değerlendirmektense kıyaslamaktan çok görmek isterim . Bu bana kokuyor ve ben vahşi doğada gördüğümde hemen yeniden gözden geçiriyorum.
MikeTheLiar

Yanıtlar:


23

Belirtildiği gibi nasıl yavaş Java istisnalar vardır? kişi yavaşlığının try {} catch {}istisnanın kendisinin instatiasyonu içinde olduğunu görebilir .

Bir istisna oluşturmak, çağrı yığınının tamamını çalışma zamanından alır ve bu da masrafın olduğu yerdir. Bir istisna oluşturmuyorsanız, bu çok az bir zaman artışıdır.

Bu soruda verilen örnekte herhangi bir istisna yoktur, kişi bunları oluşturmaktan herhangi bir yavaşlama beklemez - yaratılmazlar. Bunun yerine, burada try {} finally {}nihayet bloğu içinde kaynak dağıtımı ele almak için bir nedir .

Yani soruyu cevaplamak için, hayır, try {} finally {}istisnalar kullanmayan bir yapıda gerçek çalışma zamanı gideri yoktur (görüldüğü gibi duyulmamıştır). Ne olduğunu kimse kodunu okur ve bu tipik olmayan kod tarzı görür ve kodlayıcı başka bir şey sonra bu yöntemde olur etrafında zihin almak zorunda olduğunda muhtemelen pahalı bakım zamanı returnönceki çağrı dönmeden önce.


Daha önce de belirtildiği gibi, bakım bunu yapmanın her iki yolu için de bir argüman . Kayıt için, dikkate alındıktan sonra, nihayet benim tercihim yaklaşım olacaktır.

Birisine yeni bir dil yapısı öğretmenin bakım süresini düşünün. Görmek try {} finally {}, kişinin Java kodunda sıkça gördüğü bir şey değildir ve bu nedenle insanlar için kafa karıştırıcı olabilir. Java'da insanların görmeye aşina olduklarından biraz daha gelişmiş yapıları öğrenmek için bir dereceye kadar bakım süresi vardır.

finally {}Blok hep çalışır. Ve bu yüzden kullanmalısın. Ayrıca, birisi uygun zamanda oturum kapatmayı unuttuğunda veya yanlış zamanda çağırdığında ya da çağırdıktan sonra iki kez çağrılacak şekilde geri dönmeyi / çıkmayı unutduğunda, son olmayan yaklaşımda hata ayıklamanın bakım süresini de göz önünde bulundurun. Bununla ilgili o kadar çok hata var ki, kullanımı try {} finally {}imkansız.

Bu iki maliyetleri tartma, bu bakım zamanında pahalı değil kullanmak try {} finally {}yaklaşım. İnsanlar, try {} finally {}bloğun diğer sürümle karşılaştırıldığı kaç kesirli milisaniye veya ek jvm talimatı konusunda sıkıntı çekebilirken , hata ayıklama için harcanan saatleri kaynak aktarımını ele almanın ideal yolundan daha az olarak düşünmek gerekir.

Öncelikle ve tercihen hataların daha sonra yazılmasını önleyecek şekilde korunabilir kod yazın.


1
Anon tarafından önerilen bir düzenleme şöyle dedi: "try / nihayet çalışma zamanı istisnaları ile ilgili başka türlü sahip olmayacağınız garantiler sunar. İster beğenir ister beğenmesin, herhangi bir satır neredeyse bir istisna fırlatabilir" - Me: bu tam olarak doğru değil . Soruda verilen yapının , kodun performansını yavaşlatan ek istisnaları yoktur. Deneme / sonlandırma, denememe / sonlandırma bloğuna kıyasla blokun etrafına sarılarak ek performans etkisi yoktur .

2
Bakımda bu denemenin bu kullanımını anlamaktan daha pahalı olabilecek şey, birkaç temizleme bloğunun bakımını yapmaktır. Şimdi en azından, ek temizlik gerekirse, koyacağınız tek bir yer olduğunu biliyorsunuz.
Bart van Ingen Schenau

@BartvanIngenSchenau Gerçekten. Bir nihayet denemek için muhtemelen en iyi yol, kodda gördüğüm bir yapı kadar yaygın değil - insanlar sonunda nihayet yakalamak ve genel olarak istisna işleme anlamaya çalışmak için yeterince sorun var .. ama deneyin - yakalama olmadan son? Ne? İnsanlar gerçekten anlamıyorlar . Önerdiğiniz gibi, dil yapısını anlamak ve bundan kaçınmaya çalışmaktan ve işleri kötü yapmaktan daha iyidir.

Çoğunlukla denemenin alternatifinin de ücretsiz olmadığını bilmek istedim. Keşke masraflara eklediğiniz için başka bir oy verebilsem.
Bart van Ingen Schenau

5

/programming/299068/how-slow-are-java-exceptions

Bu sorudaki kabul edilen cevap, bir işlev yakalama işlevinin bir deneme yakalama bloğuna sarılarak, işlev çağrısı yerine% 5'ten daha az maliyet olduğunu gösterir. Aslında istisnayı atmak ve yakalamak, çalışma zamanının çıplak işlev çağrısının 66 katından daha fazla balonlanmasına neden oldu. Eğer istisnai tasarım düzenli atmak için bekliyorsanız o zaman (düzgün profilli performans kritik kod) kaçınmaya çalışacağız, ancak istisnai durum nadir ise o zaman büyük bir anlaşma değil.


3
"istisnai durum nadirse" - iyi orada bir totoloji var. İstisnai olanlar nadirdir ve istisnalar tam olarak bu şekilde kullanılmalıdır. Asla tasarım veya kontrol akışının bir parçası olmamalıdır.
Jesse C. Slicer

1
İstisnalar pratikte nadiren görülmez, sadece tesadüfi yan etkidir. Örneğin, kötü kullanıcı arayüzünüz varsa, istisna kullanım durumu akışına normal akıştan daha sık neden olabilir
Esailija

2
Sorunun kodunda atılan herhangi bir istisna yoktur.

2
@ JesseC.Slicer Akış denetimi olarak kullanılmalarının nadir olmadığı diller vardır. Örnek olarak, python'da herhangi bir şeyi tekrarlarken, yineleyici yapıldığında bir durdurma istisnası atar. Ayrıca OCaml'de standart kütüphaneleri çok "mutlu" görünüyor, nadir olmayan çok sayıda durumda atıyor (eğer bir haritanız varsa ve haritada bulunmayan bir şeyi aramaya çalışırsanız) ).
stonemetal

@stonemetal KeyError ile Python'un yaptığı gibi
Izkata

4

Optimize etmek yerine kodunuzun ne yaptığını düşünün.

Nihayet bloğu eklemek farklı bir uygulamadır - bu, her istisnadan çıkacağınız anlamına gelir.

Özellikle - giriş bir istisna atarsa, ikinci işlevinizdeki davranış ilk işlevinizden farklı olacaktır.

Giriş ve çıkış aslında işlev davranışının bir parçası değilse, o zaman yöntemin bir şey ve bir şey iyi yapmasını öneririm:

    void func()
    {
            bool ret = dosomething();
            if(ret == false)
                return;

            ret = dosomethingelse();
            if(ret == false)
                return;

            dootherstuff();

    }

ve ana kodunuzun yaptıklarından temelde farklı göründüğü için login / logout'u yöneten bir bağlamda zaten kapsüllenmiş bir fonksiyonda olmalıdır.

Mesajınızı çok aşina olmadığım Java olarak etiketli ama .net içinde bunun için bir kullanma bloğu kullanırdım:

yani:

    using(var loginContext = CreateLoginContext()) 
    {
            // Do all your stuff
    }

Oturum açma bağlamında kaynakları kapatıp temizleyecek bir Dispose yöntemi vardır.

Edit: Aslında soruya cevap vermedi!

Ölçülebilir bir kanıtım yok, ancak bunun erken optimizasyona layık olması için daha pahalı veya kesinlikle önemli ölçüde pahalı olmasını beklemem.

Cevabımın geri kalanını bırakacağım çünkü soruyu cevaplamasa da OP için yararlı olduğunu düşünüyorum.


1
Cevabınızı tamamlamak için Java 7 using, söz konusu kaynağın AutoCloseablearayüzü uyguladığını varsayarak, .net yapınıza benzer bir denemeyle deneme ifadesi sundu .
haylem

Hatta retbir satır sonra kontrol edilecek iki kez atanan bu değişkeni bile kaldırır . Başaralım if (dosomething() == false) return;.
ott--

Kabul etti, ancak OP kodunu sağlandığı şekilde tutmak istedim.
Michael

@ ott-- OP kodunun kullanım durumunun basitleştirilmesi olduğunu varsayıyorum - örneğin, daha çok benzer bir şeyleri olabilir ret2 = doSomethingElse(ret1), ancak bu soru ile ilgili değildir, bu yüzden kaldırıldı.
Izkata

if (dosomething() == false) ...kötü kod. Daha sezgisel kullanınif (!dosomething()) ...
Steffen Heil

1

Varsayım: C # kodunda gelişiyorsunuz.

Hızlı yanıt, try / nihayet blokları kullanmaktan önemli bir performans isabetinin olmamasıdır. İstisnaları attığınızda ve yakaladığınızda bir performans isabeti var.

Daha uzun cevap kendiniz için görmektir. Oluşturulan temel CIL koduna bakarsanız ( örn. Kırmızı kapı reflektörü) , oluşturulan temel CIL talimatlarını görebilir ve etkisini bu şekilde görebilirsiniz.


12
Soru java olarak etiketlendi .
Joachim Sauer

İyi tespit! ;)
Michael Shaw

3
Ayrıca, yöntemler sadece büyük tadı olabilir, ancak büyük harfle değil.
Erik Reppen

soru c # :) hala iyi bir cevap
PhillyNJ

-2

Diğerleri zaten belirtildiği gibi, Ttese kod farklı sonuçlar var, eğer ikisinden biri olacak dosomethingelseya dootherstuffbir özel durum.

Ve evet, herhangi bir işlev çağrısı bir istisna atabilir, en azından bir StackOVerflowError! Bunları doğru bir şekilde işlemek nadiren mümkün olsa da (temizleme işlevi olarak adlandırılan herhangi bir olasılık muhtemelen aynı soruna sahip olacağından, bazı durumlarda (örneğin kilitleri uygulamak gibi) bunu doğru bir şekilde ele almak çok önemlidir ...


2
bu önceki 6
cevapta
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.