C ++ 'daki İstisnalar gerçekten yavaş mı


99

C ++ 'da Sistematik Hata İşlemeyi izliyordum - Andrei Alexandrescu C ++' daki İstisnaların çok çok yavaş olduğunu iddia ediyor.

Bu hala C ++ 98 için geçerli mi?


44
"C ++ 98 istisnalarının" "C ++ 03 istisnalarından" veya "C ++ 11 istisnalarından" daha hızlı / yavaş olup olmadığını sormanın bir anlamı yoktur. Performansları, derleyicinin bunları programlarınızda nasıl uyguladığına bağlıdır ve C ++ standardı bunların nasıl uygulanması gerektiği hakkında hiçbir şey söylemez; tek şart, davranışlarının standarda ("olduğu gibi" kuralı) uyması gerektiğidir.
In silico

İlgili (ama gerçekte yinelenmeyen) soru: stackoverflow.com/questions/691168/…
Philipp

2
evet, çok yavaş, ancak normal operasyonlar için atılmamalı veya dal olarak kullanılmamalıdır
BЈовић


BЈовић'nın ne dediğini açıklığa kavuşturmak için, istisnaları kullanmak korkulacak bir şey değildir. Zaman alıcı işlemlerle (potansiyel olarak) karşılaştığınız bir istisna atıldığı zamandır. Ayrıca C ++ 89'u neden özellikle bilmek istediğinizi de merak ediyorum ... bu en son sürüm C ++ 11 ve istisnaların çalışması için gereken süre uygulama tanımlıdır, bu nedenle benim 'potansiyel olarak' zaman alıcı .
thecoshman

Yanıtlar:


163

Bugün istisnalar için kullanılan ana model (Itanium ABI, VC ++ 64 bit) Sıfır Maliyetli model istisnalarıdır.

Buradaki fikir, bir koruma kurarak ve her yerde istisnaların varlığını açıkça kontrol ederek zaman kaybetmek yerine, derleyicinin bir istisna (Program Sayacı) atabilecek herhangi bir noktayı bir işleyici listesine eşleyen bir yan tablo oluşturmasıdır. Bir istisna atıldığında, doğru işleyiciyi (varsa) seçmek için bu listeye başvurulur ve yığın çözülür.

Tipik if (error)stratejiyle karşılaştırıldığında :

  • Sıfır Maliyet modeli, adından da anlaşılacağı gibi, hiçbir istisna olmadığında ücretsizdir
  • ifbir istisna meydana geldiğinde yaklaşık 10x / 20x maliyeti

Bununla birlikte, maliyeti ölçmek önemsiz değildir:

  • Yan sehpa genellikle soğuktur ve bu nedenle hafızadan getirmek uzun zaman alır
  • Doğru işleyicinin belirlenmesi RTTI'yi içerir: getirilmesi gereken birçok RTTI tanımlayıcısı, belleğin etrafına dağılmış ve çalıştırılacak karmaşık işlemler (temelde dynamic_casther işleyici için bir test)

Dolayısıyla, çoğunlukla önbellek ıskalıyor ve bu nedenle saf CPU koduyla karşılaştırıldığında önemsiz değil.

Not: Daha fazla ayrıntı için TR18015 raporu, bölüm 5.4 İstisnaların Ele Alınması (pdf) bölümünü okuyun

Bu nedenle, evet, istisnalar istisnai yolda yavaştır , ancak bunun dışında ifgenel olarak açık kontrollerden ( strateji) daha hızlıdırlar .

Not: Andrei Alexandrescu bunu "daha hızlı" sorguluyor gibi görünüyor. Ben şahsen olayların her iki yönde değiştiğini gördüm, bazı programların istisnalarla daha hızlı ve diğerlerinin dallarda daha hızlı olduğunu, bu yüzden gerçekten de belirli koşullarda optimizasyon kaybı var gibi görünüyor.


Önemli mi ?

Ben öyle olmadığını iddia ediyorum. Bir program , performansı değil (en azından ilk kriter olarak değil) okunabilirliği dikkate alınarak yazılmalıdır . İstisnalar, arayanın arızayı yerinde halledemeyeceği veya istemeyeceği beklendiğinde ve yığından geçirildiğinde kullanılacaktır. Bonus: C ++ 11'de istisnalar, Standart Kitaplık kullanılarak iş parçacıkları arasında sıralanabilir.

Bu ince olsa da, bunun map::findatılmaması gerektiğini iddia ediyorum, ancak map::findgeri gönderme checked_ptrgirişimi başarısız olduğu için başarısız olursa bir atışı geri göndermekte sorun yok : ikinci durumda, Alexandrescu'nun tanıttığı sınıf örneğinde olduğu gibi , arayan kişi seçer açık kontrol ve istisnalara güvenme arasında. Arayan kişiyi daha fazla sorumluluk vermeden güçlendirmek genellikle iyi bir tasarımın işaretidir.


3
+1 Yalnızca dört şey eklerim: (0) C ++ 11'e eklenen yeniden fırlatma desteği hakkında; (1) komitenin c ++ verimliliği hakkındaki raporuna bir referans; (2) doğruluk hakkında bazı açıklamalar (okunabilirliği bile gölgede bırakıyor); ve (3) performans hakkında, istisnaları kullanmama durumuna karşı ölçmeye ilişkin açıklamalar (hepsi görelidir)
Şerefe ve hth. - Alf

2
@ Cheersandhth.-Alf: (0), (1) ve (3) bitti: teşekkürler. Doğruluk (2) ile ilgili olarak, okunabilirliği gölgede bıraksa da, diğer hata işleme stratejilerinden daha doğru koda yol açan istisnalardan emin değilim (yürütme istisnalarının yarattığı birçok görünmez yolu unutmak çok kolaydır).
Matthieu M.

2
Açıklama yerel olarak doğru olabilir, ancak istisnaların varlığının derleyicinin yapabileceği varsayımlar ve optimizasyonlar üzerinde genel etkileri olduğunu belirtmek faydalı olabilir. Derleyici her zaman küçük bir programı görebildiğinden, bu çıkarımlar "önemsiz karşı örneklere sahip olmama" sorununu yaşar. İstisnasız ve istisnasız gerçekçi, büyük bir kod tabanında profil oluşturmak iyi bir fikir olabilir.
Kerrek SB

4
> Adından da anlaşılacağı gibi Sıfır Maliyet modeli, hiçbir istisna olmadığında ücretsizdir, bu aslında en ince ayrıntı düzeylerine kadar doğru değildir. Daha fazla kod üretmenin her zaman bir performans etkisi vardır, küçük ve ince bile olsa ... işletim sisteminin yürütülebilir dosyayı yüklemesi biraz daha uzun sürebilir veya daha fazla i-önbellek hatası alırsınız. ayrıca, yığın çözme kodu ne olacak? ayrıca, rasyonel düşünceyle anlamaya çalışmak yerine etkilerini ölçmek için yapabileceğiniz deneylere ne dersiniz?
jheriko

2
@jheriko: Aslında sorularınızın çoğunu zaten yanıtladığıma inanıyorum. Yükleme süresi etkilenmemelidir (soğuk kod yüklenmemelidir), i-önbellek etkilenmemelidir (soğuk kod i-önbelleğe girmemelidir), ... bu nedenle eksik bir soruyu ele almak için: "nasıl ölçülür" => atılan herhangi bir istisnayı bir çağrıyla değiştirmek abort, ikili boyuttaki ayak izini ölçmenize ve yükleme süresi / i-önbelleğin benzer şekilde davrandığını kontrol etmenize olanak sağlar. Elbette, hiçbirini abort
vurmasanız

61

Soru yayınlandığında, doktora giderken bir taksi beklemekteydim, bu yüzden kısa bir yorum için sadece vaktim vardı. Ama şimdi yorum yaptıktan, olumlu oy verdikten ve olumsuz oy kullandığım için kendi cevabımı eklemem daha iyi olur. Matthieu'nun cevabı zaten oldukça iyi olsa bile .


İstisnalar, diğer dillere kıyasla özellikle C ++ 'da mı yavaş?

İddia yeniden

" C ++ 'da Sistematik Hata İşlemeyi izliyordum - Andrei Alexandrescu , C ++' daki İstisnaların çok çok yavaş olduğunu iddia ediyor."

Andrei'nin iddia ettiği gibi kelimenin tam anlamıyla buysa, o zaman düpedüz yanlış değilse de bir kez olsun çok yanıltıcıdır. Çünkü yükseltilen / atılan bir istisna, programlama dilinden bağımsız olarak, dildeki diğer temel işlemlerle karşılaştırıldığında her zaman yavaştır . İddia edilen iddianın da belirttiği gibi, sadece C ++ 'da değil, C ++' da diğer dillerden daha fazla.

Genel olarak, çoğunlukla dilden bağımsız olarak, diğerlerinden daha yavaş olan iki temel dil özelliği, çünkü karmaşık veri yapılarını işleyen rutin çağrılarına tercüme ederler.

  • istisna atma ve

  • dinamik bellek tahsisi.

C ++ 'da mutlu bir şekilde, zaman açısından kritik kodlarda her ikisinden de kaçınabilir.

Ne yazık ki , C ++ 'ın varsayılan verimliliği oldukça yakın gelse bile, Bedava Öğle Yemeği Gibi Bir Şey Yok . :-) İstisna atmadan ve dinamik bellek tahsisinden kaçınarak kazanılan verimlilik genellikle daha düşük bir soyutlama seviyesinde kodlama ile elde edilir, C ++ 'ı sadece "daha iyi bir C" olarak kullanır. Ve daha düşük soyutlama daha fazla "karmaşıklık" anlamına gelir.

Daha fazla karmaşıklık, bakım için daha fazla zaman harcanması ve kodun yeniden kullanımından çok az fayda sağlanması veya hiç fayda görmemesi anlamına gelir; bu, tahmin edilmesi veya ölçülmesi zor olsa bile gerçek parasal maliyetlerdir. Yani, C ++ ile, istenirse, yürütme verimliliği için bazı programcı verimliliğinin ticareti yapılabilir. Bunu yapıp yapmamak büyük ölçüde mühendislik ve içgüdüsel bir karardır, çünkü pratikte sadece kazanç, maliyet değil, kolayca tahmin edilebilir ve ölçülebilir.


C ++ istisna atma performansının herhangi bir nesnel ölçüsü var mı?

Evet, uluslararası C ++ standardizasyon komitesi C ++ performansı hakkında TR18015 Teknik Rapor yayınladı .


İstisnaların "yavaş" olması ne anlama geliyor ?

Esas olarak , işleyici araması nedeniyle, throwörneğin bir intatama ile karşılaştırıldığında Çok Uzun Süre ™ alabileceği anlamına gelir .

TR18015'in 5.4 "İstisnalar" bölümünde tartıştığı gibi, iki temel istisna işleme uygulama stratejisi vardır:

  • Her bir trybloğun istisna yakalamayı dinamik olarak kurduğu yaklaşım , böylece dinamik işleyiciler zincirinin aranması, bir istisna atıldığında gerçekleştirilir ve

  • Derleyicinin, atılan bir özel durum için işleyiciyi belirlemek için kullanılan statik arama tabloları oluşturduğu yaklaşım.

İlk çok esnek ve genel yaklaşım neredeyse 32-bit Windows'ta zorlanırken, 64-bit arazi ve * nix-land'de ikinci çok daha verimli yaklaşım yaygın olarak kullanılır.

Ayrıca, bu raporun da tartıştığı gibi, her yaklaşım için, istisna işlemenin verimliliği etkilediği üç ana alan vardır:

  • try-bloklar,

  • düzenli işlevler (optimizasyon fırsatları) ve

  • throw-ifade.

Temel olarak, dinamik işleyici yaklaşımıyla (32 bit Windows) istisna işleme, trybloklar üzerinde , çoğunlukla dilden bağımsız olarak bir etkiye sahiptir (çünkü bu, Windows'un Yapılandırılmış İstisna İşleme şeması tarafından zorlanır ), statik tablo yaklaşımı ise try- bloklar. Bunu tartışmak, SO cevabı için pratik olandan çok daha fazla alan ve araştırma gerektirir. Bu nedenle, ayrıntılar için rapora bakın.

Maalesef 2006 tarihli rapor, 2012'nin sonlarından itibaren biraz tarihli ve bildiğim kadarıyla daha yeni olan karşılaştırılabilir bir şey yok.

Diğer bir önemli bakış açısı, istisnaların kullanımının performans üzerindeki etkisinin , destekleyici dil özelliklerinin izole edilmiş verimliliğinden çok farklı olmasıdır, çünkü raporun da belirttiği gibi,

"İstisna işlemeyi değerlendirirken, hatalarla başa çıkmanın alternatif yollarıyla karşılaştırılmalıdır."

Örneğin:

  • Farklı programlama stilleri nedeniyle bakım maliyetleri (doğruluk)

  • Yedekli arama sitesi ifarıza kontrolü ile merkezileştirilmiştry

  • Önbelleğe alma sorunları (örneğin, daha kısa kod önbelleğe sığabilir)

Raporda dikkate alınması gereken farklı yönler listesi vardır, ancak yine de yürütme verimliliği hakkında somut gerçekleri elde etmenin tek pratik yolu, muhtemelen aynı programı istisnaları kullanarak ve istisnaları kullanmadan, geliştirme süresinde belirli bir sınır içinde ve geliştiricilerle uygulamaktır her yola aşina ve ardından ÖLÇÜM .


İstisnalardan kaçınmanın iyi bir yolu nedir?

Doğruluk neredeyse her zaman verimliliğin önüne geçer.

İstisnalar olmaksızın aşağıdakiler kolaylıkla gerçekleşebilir:

  1. Bazı P kodları, bir kaynak elde etmek veya bazı bilgileri hesaplamak içindir.

  2. Çağıran kod C, başarı / başarısızlık için kontrol edilmiş olmalı, ancak yapmıyor.

  3. Var olmayan bir kaynak veya geçersiz bilgi C'yi izleyen kodda kullanılır ve genel kargaşaya neden olur.

Asıl sorun nokta (2) 'dir, burada olağan dönüş kodu şeması ile çağıran kod C kontrol etmeye zorlanmaz.

Bu tür bir denetimi zorlayan iki ana yaklaşım vardır:

  • P başarısız olduğunda doğrudan bir istisna atar.

  • P, C'nin ana değerini kullanmadan önce incelemesi gereken bir nesne döndürdüğünde (aksi takdirde bir istisna veya sonlandırma).

İkinci yaklaşım, ilk olarak Barton ve Nackman tarafından * Bilimsel ve Mühendislik C ++: Gelişmiş Teknikler ve Örneklerle Giriş adlı kitaplarında tanımlanan AFAIK idi ve burada Fallow"olası" bir fonksiyon sonucu için çağrılan bir sınıfı tanıttılar . Benzer bir sınıf adıoptionalBoost kitaplığında şimdi sunulmaktadır. Ve POD dışı sonuç durumunda Optionalbir std::vectordeğer taşıyıcısı kullanarak bir sınıfı kendiniz kolayca uygulayabilirsiniz .

İlk yaklaşımla çağıran kod C'nin istisna işleme tekniklerini kullanmaktan başka seçeneği yoktur. İkinci yaklaşımla, bununla birlikte, çağıran kod C'nin kendisi,if , esaslı kontrolün yoksa genel istisna işlemenin . Bu nedenle, ikinci yaklaşım programcı ile yürütme süresi verimliliği arasında denge kurmayı destekler.


Çeşitli C ++ standartlarının istisna performansı üzerindeki etkisi nedir?

"Bunun C ++ 98 için hala geçerli olup olmadığını bilmek istiyorum"

C ++ 98, ilk C ++ standardıydı. İstisnalar için, standart bir istisna sınıfları hiyerarşisi getirmiştir (maalesef oldukça kusurlu). Performans üzerindeki ana etki, istisna belirtimlerinin olasılığıydı (C ++ 11'de kaldırıldı), ancak bunlar ana Windows C ++ derleyicisi Visual C ++ tarafından hiçbir zaman tam olarak uygulanmadı: Visual C ++, C ++ 98 özel durum belirtim sözdizimini kabul eder, ancak yalnızca yok sayar istisna özellikleri.

C ++ 03, C ++ 98'in teknik bir düzeltmesiydi. C ++ 03'te gerçekten yeni olan tek şey değer başlatma idi . Bunun istisnalarla ilgisi yok.

C ++ 11 standart genel istisna özellikleri kaldırıldı ve noexcept anahtar kelimeyle .

C ++ 11 standardı ayrıca, istisnaların depolanması ve yeniden atılması için destek ekledi; bu, C ++ istisnalarını C dili geri aramalarında yaymak için harika. Bu destek, geçerli istisnanın nasıl saklanabileceğini etkili bir şekilde kısıtlar. Bununla birlikte, bildiğim kadarıyla, yeni kodda istisna işlemenin bir C dili geri aramasının her iki tarafında daha kolay kullanılabilmesi dışında, performansı etkilemiyor.


7
"istisnalar, programlama dilinden bağımsız olarak, dildeki diğer temel işlemlere kıyasla her zaman yavaştır" ... istisnaların kullanımını sıradan akış kontrolünde derlemek için tasarlanmış diller dışında.
Ben Voigt

5
"bir istisna fırlatmak hem ayırmayı hem de yığın çözmeyi içerir". Bu aynı zamanda genel olarak doğru değildir ve yine OCaml bir karşı örnektir. Çöp toplama dillerinde, yığını çözmenize gerek yoktur çünkü yok edici yoktur, bu nedenle yalnızca longjmpişleyiciye kalırsınız .
JD

2
@JonHarrop: Muhtemelen Pyhon'un istisna işleme için bir nihayet cümlesi olduğunu bilmiyorsunuzdur. bu, bir Python uygulamasının ya yığın çözülmesine sahip olduğu ya da Python olmadığı anlamına gelir. hakkında iddialarda bulunduğunuz (fantezi) konular hakkında tamamen bilgisiz görünüyorsunuz. afedersiniz.
Şerefe ve hth. - Alf

2
@ Cheersandhth.-Alf: "Pyhon'un istisna işleme için bir nihayet cümlesi vardır. Bu, bir Python uygulamasının ya yığın çözülmesine sahip olduğu ya da Python olmadığı anlamına gelir. try..finallyYapı, yığın çözülmesi olmadan uygulanabilir. F #, C # ve Java'nın tümü try..finallyyığın çözme kullanmadan uygulanır . Sen sadece longjmpişleyiciye (daha önce açıkladığım gibi).
JD

4
@JonHarrop: Eğer ses bir ikilem poz gibi. ama şimdiye kadar tartışılan herhangi bir şeyle alakası yok ve şimdiye kadar uzun bir olumsuzluklar içeren saçmalıklar dizisi yayınladınız . i antagonisti olarak size o "araçları" ortaya çıkaracaktır neyi tercih ediyor, çünkü bazı belirsiz ifadeler katılıp katılmadığını için sana güvenmek zorunda kalacak ve ben kesinlikle yok hepsi anlamsız saçma olduğunu, downvoting vb sonra size güven
Şerefe ve hth. - Alf

14

Kodu montaja dönüştürmedikçe veya karşılaştırmadıkça performans hakkında asla iddiada bulunamazsınız.

İşte gördükleriniz: (hızlı tezgah)

Hata kodu, oluşma yüzdesine duyarlı değildir. İstisnaların, asla atılmadıkları sürece biraz yükü vardır. Onları fırlattığınızda sefalet başlar. Bu örnekte, vakaların% 0,% 1,% 10,% 50 ve% 90'ı için atılmıştır. İstisnalar% 90 oranında atıldığında kod, istisnaların% 10 oranında atıldığı durumdan 8 kat daha yavaştır. Gördüğünüz gibi, istisnalar gerçekten yavaştır. Sık sık atılırlarsa kullanmayın. Uygulamanızın gerçek zamanlı gereksinimi yoksa, çok nadiren ortaya çıkarsa bunları atmaktan çekinmeyin.

Onlar hakkında birçok çelişkili görüş görüyorsunuz. Ama son olarak, istisnalar yavaş mı? Ben yargılamıyorum. Sadece kıyaslamayı izle.

C ++ istisnaları performans karşılaştırması


13

Derleyiciye bağlıdır.

Örneğin GCC, istisnaları ele alırken çok zayıf performans göstermesiyle biliniyordu, ancak bu son birkaç yılda önemli ölçüde iyileşti.

Ancak, istisnaların ele alınmasının - adından da anlaşılacağı gibi - yazılım tasarımınızdaki kuraldan ziyade istisna olması gerektiğini unutmayın. Saniyede performansı etkileyecek kadar çok istisna atan bir uygulamanız varsa ve bu hala normal çalışma olarak kabul ediliyorsa, işleri farklı şekilde yapmayı düşünmelisiniz.

İstisnalar, tüm bu hantal hata işleme kodunu ortadan kaldırarak kodu daha okunaklı hale getirmenin harika bir yoludur, ancak bunlar normal program akışının bir parçası olur olmaz takip edilmesi gerçekten zor hale gelir. Unutmayın, a throwhemen hemen goto catchkılık değiştirmiş bir durumdur .


Şu anda olduğu gibi, "bu C ++ 98 için hala geçerlidir", bu kesinlikle derleyiciye bağlı değildir. ayrıca, bu cevap throw new Exceptionbir Java-ism'dir. bir kural olarak asla işaret atmamalı .
Şerefe ve hth. - Alf

1
98 standardı istisnaların tam olarak nasıl uygulanacağını dikte ediyor mu?
thecoshman

6
C ++ 98 bir derleyici değil, bir ISO standardıdır. Bunu uygulayan birçok derleyici var.
Philipp

3
@thecoshman: Hayır. C ++ standardı, herhangi bir şeyin nasıl uygulanması gerektiği hakkında hiçbir şey söylemiyor (standardın "Uygulama Sınırları" bölümünün olası istisnası dışında).
In silico

2
@Insilico daha sonra, istisnaların nasıl gerçekleştirildiğinin (şok edici bir şekilde) uygulamanın tanımlandığı (okuma, derleyiciye özel) mantıksal sonucunu çıkarabilirim.
thecoshman

4

Evet, ama bu önemli değil. Neden?
Bunu okuyun:
https://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx

Temelde Alexandrescu'nun gibi kullanarak istisnalar (kullandıkları çünkü 50x yavaşlama tarif söylüyor, catcholarak else) çok yanlış. Bunu yapmaktan hoşlanan ppl için C ++ 22 :) 'nin şöyle bir şey ekleyeceği söyleniyor:
(temelde mevcut olandan kod üreten derleyici olduğu için bunun çekirdek dil olması gerektiğini unutmayın)

result = attempt<lexical_cast<int>>("12345");  //lexical_cast is boost function, 'attempt'
//... is the language construct that pretty much generates function from lexical_cast, generated function is the same as the original one except that fact that throws are replaced by return(and exception type that was in place of the return is placed in a result, but NO exception is thrown)...     
//... By default std::exception is replaced, ofc precise configuration is possible
if (result)
{
     int x = result.get(); // or result.result;
}
else 
{
     // even possible to see what is the exception that would have happened in original function
     switch (result.exception_type())
     //...

}

Not: İstisnalar bu kadar yavaş olsa bile ... yürütme sırasında kodun bu bölümünde çok fazla zaman harcamamanız bir sorun değildir ... Örneğin, kayan bölme yavaşsa ve bunu 4x yaparsanız Zamanınızın% 0,3'ünü FP bölümü için harcarsanız, bu önemli değil ...


0

In silico'nun dediği gibi, uygulamasına bağımlıdır, ancak genel olarak istisnalar herhangi bir uygulama için yavaş kabul edilir ve performans yoğun kodda kullanılmamalıdır.

DÜZENLEME: Bunları hiç kullanmayın demiyorum ama yoğun performans gerektiren kodlar için onlardan kaçınmak en iyisidir.


9
Bu, en iyi ihtimalle, istisna performansına bakmanın çok basit bir yoludur. Örneğin, GCC, hiçbir istisna atılmadığında bir performans vuruşuna uğramayacağınız "sıfır maliyetli" bir uygulama kullanır. Ve istisnalar, istisnai (yani nadir) durumlar içindir, bu nedenle, bazı ölçülere göre yavaş olsalar bile, bu, onları kullanmamak için hala yeterli neden değildir.
In silico

@insilico eğer bakarsanız neden dedim, istisnaları tam nokta kullanmamayı söylemedim. Performans yoğun kod belirledim, bu doğru bir değerlendirme, esas olarak gpgpus ile çalışıyorum ve istisnalar kullanırsam vurulabilirim.
Chris McCabe
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.