Derleyici optimizasyonlarına dayanan kod yazmak kötü bir uygulama mıdır?


99

Bazı C ++ öğelerini öğreniyorum ve genellikle işlev içinde oluşturulan işlevlerden büyük nesneleri döndürmek zorunda kalıyorum. Referansa göre bir geçiş olduğunu, bir işaretçi ve bir referans tipi çözümleri olduğunu biliyorum, ancak C ++ derleyicilerinin (ve C ++ standardının) bu büyük nesneleri bellekten kopyalamayı engelleyen dönüş değeri optimizasyonuna izin verdiğini de okudum. bunların zamanını ve hafızasını koruyarak.

Şimdi, nesne açıkça değer tarafından döndürüldüğünde sözdiziminin çok daha net olduğunu hissediyorum ve derleyici genellikle RVO'yu kullanır ve süreci daha verimli hale getirir. Bu optimizasyona güvenmek kötü bir uygulama mıdır? Kullanıcı için kodu daha net ve okunaklı kılıyor, bu da son derece önemli, ancak derleyicinin RVO fırsatını yakalayacağını varsaymaya karşı dikkatli olmalı mıyım?

Bu bir mikro optimizasyon mu yoksa kodumu tasarlarken aklımda tutmam gereken bir şey mi?


7
Düzenlemenize cevap vermek için, bu bir mikro optimizasyondur çünkü nanosaniyede kazandıklarınızı kıyaslamaya çalışsanız bile, zar zor göreceksiniz. Gerisi için, neden işe yaramayacağına dair kesin bir cevap vermek için C ++ 'ta çürüdüm. Bunlardan biri, dinamik tahsise ihtiyaç duyduğunuz ve bu nedenle yeni / pointer / referansları kullandığınız zamanlar olması muhtemeldir.
Walfrat

4
@Walfrat nesneleri oldukça büyük olsa bile, megabayt sırasına göre? Dizilerim çözdüğüm sorunların doğası gereği çok büyük olabilir.
Matt

6
@ Matty Ben yapmazdım. Referanslar / işaretçiler tam da bunun için var. Derleyici optimizasyonlarının, bir program oluştururken programcıların dikkate alması gerekenlerin ötesinde olması gerekiyor, evet olsa bile, iki dünyanın üst üste bindiği zamanlar.
Neil

5
@Matt C / çekirdeklerde 10 + ish deneyimi olan geliştiricilere ihtiyaç duyduğunu düşünen son derece spesifik bir şey yapmadığınız sürece, ihtiyacınız olmayan düşük donanım etkileşimi. Çok özel bir şeye ait olduğunuzu düşünüyorsanız, yazınızı düzenleyin ve uygulamanızın yapmanız gerekenleri doğru bir şekilde ekleyin (gerçek zamanlı? Ağır matematik hesaplaması? ...)
Walfrat

37
Gelen özel durumda C ++ 'ın (K) RVO, evet, bu optimizasyon dayanarak mükemmel geçerlidir. Bunun nedeni, C ++ 17 standardının , modern derleyicilerin zaten yaptığı durumlarda, bunun gerçekleşmesini özellikle zorunlu kılmasıdır .
Caleth

Yanıtlar:


130

İstihdam az şaşkınlık prensibini .

Siz ve sadece bu kodu kullanacak olan siz misiniz, ve 3 yıl içinde aynı şeyin yaptığınız şeyden şaşırmayacağınıza emin misiniz?

O zaman devam et.

Diğer tüm durumlarda standart yolu kullanın; Aksi takdirde, siz ve meslektaşlarınız böcek bulmakta zorlanacaksınız.

Örneğin, meslektaşım kodumdan hatalara neden olduğundan şikayet ediyordu. Derleyici ayarlarında kısa devre Boole değerlendirmesini kapattığı ortaya çıktı. Neredeyse tokatlıyordum.


88
@Ne demek istediğim, herkes kısa devre değerlendirmesine güveniyor. Ve bunun hakkında iki kez düşünmek zorunda kalmamalı, açılmalı. Bu bir defacto standardı. Evet, onu değiştirebilirsin, ama yapmamalısın.
Pieter B

49
“Dilin çalışma şeklini değiştirdim ve kirli çürük kodunuz kırıldı! Arghh!” Vay. Tokatlamak uygun olur, meslektaşınızı Zen eğitimine gönderin, orada bir sürü şey var.

109
@ PieterB C ve C ++ dil teknik özelliklerinin kısa devre değerlendirmesini garanti ettiğinden eminim . Bu yüzden sadece fiili bir standart değil , standart. Onsuz, artık C / C ++ kullanmıyorsunuz, ama şüpheyle buna benzer bir şey
kullanmıyorsunuz

47
Sadece referans için, buradaki standart yol değere göre döndürmektir.
DeadMG

28
@ dan04 evet, Delphi'deydi. Beyler, bu örnekte yakalanmadım, yaptığım nokta bu. Başka kimsenin yapmadığı şaşırtıcı şeyler yapmayın.
Pieter B

81

Bu özel durum için kesinlikle sadece değere göre geri dönün.

  • RVO ve NRVO, C ++ 03 modunda bile, herhangi bir düzgün derleyici tarafından gerçekten yapılması gereken iyi bilinen ve sağlam optimizasyonlardır.

  • Hareketli anlambilim, (N) RVO gerçekleşmemişse nesnelerin işlevlerin dışına taşınmasını sağlar. Senin nesne (gibi dahili olarak dinamik veriler kullanıyorsa Bu sadece yararlıdır std::vectoryapar), ancak sorun olduğu takdirde bu gerçekten durum olmalı bu yığını taşan büyük otomatik nesnelerle bir risktir - büyük.

  • C ++ 17 RVO'yu zorlar . Bu yüzden endişelenmeyin, bu sizin için kaybolmayacak ve derleyiciler güncelledikten sonra kendisini tamamen kurmayı bitirecektir.

Ve sonunda, bir gösterici döndürmek için ek bir dinamik tahsisi zorlamak ya da sonuç türünüzü varsayılan olarak yapılandırılabilir olması için zorlamak, böylece bir çıktı parametresi olarak geçebilmeniz için çirkin ve deyimsiz olmayan çözümlerdir. Sahip olmak.

Mantıklı olan kodu yazmanız yeterlidir ve mantıklı olan kodu doğru şekilde optimize ettiği için derleyici yazarlarına teşekkür ederiz.


9
Sadece eğlence için Borland Turbo C ++ 3.0'dan 1990-ish'in RVO'yu nasıl idare ettiğini görün . Spoiler: Temel olarak gayet iyi çalışıyor.
nwp

9
Burada anahtar, rastgele bir derleyiciye özgü optimizasyon veya "belgelenmemiş özellik" değil, fakat C ++ standardının çeşitli sürümlerinde teknik olarak isteğe bağlı olmasına rağmen, endüstri tarafından yoğun şekilde itilen ve hemen hemen her büyük derleyicinin yaptığı bir şey. çok uzun zaman.

7
Bu optimizasyon, birinin istediği kadar sağlam değil. Evet, en bariz durumlarda oldukça güvenilirdir, ancak örneğin gcc'nin bugzilla'sına bakıldığında, kaçırıldığı çok az bariz durum vardır.
Marc Glisse

62

Şimdi, nesne açıkça değer tarafından döndürüldüğünde sözdiziminin çok daha net olduğunu hissediyorum ve derleyici genellikle RVO'yu kullanır ve süreci daha verimli hale getirir. Bu optimizasyona güvenmek kötü bir uygulama mıdır? Kullanıcı için kodu daha net ve okunaklı kılıyor, bu da son derece önemli, ancak derleyicinin RVO fırsatını yakalayacağını varsaymaya karşı dikkatli olmalı mıyım?

Bu, küçük, küçük trafikten kaçan bir blogda okuduğunuz, sonradan kullanmakta daha akıllı ve üstün hissettiğiniz için biraz bilinen, şirin, mikro-optimizasyon değildir.

C ++ 11'den sonra RVO bu kod kodunu yazmanın standart yoludur . Yaygın, beklenen, öğretilen, görüşmelerde belirtilen, bloglarda belirtilen, standartta belirtilen, uygulanmadığı takdirde bir derleyici hatası olarak rapor edilecektir. C ++ 17'de, dil bir adım daha ileri gider ve belirli senaryolarda kopya seçimi yapılmasını zorunlu tutar.

Bu optimizasyona kesinlikle güvenmelisiniz.

Bunun ötesinde, değere göre değer dönüşü, referans olarak döndürülen koddan çok okunması ve yönetilmesi için çok daha kolay bir kod sağlar. Değer semantiği, daha fazla optimizasyon fırsatlarına yol açabilecek güçlü bir şeydir.


3
Teşekkürler, bu çok mantıklı ve yukarıda belirtilen "en az şaşkınlık ilkesi" ile tutarlı. Bu, kodu çok net ve anlaşılır hale getirir ve işaretçi shenanigan'larla karıştırmayı zorlaştırır.
Matt

3
@Matt Bu cevabı yükseltmemin bir nedeni de "anlam semantiği" denmesi. C ++ 'da (ve genel olarak programlamada) daha fazla tecrübe kazandıkça, değer anlambiliminin belirli nesneler için kullanılamadığı durumları göreceksiniz, çünkü değişkenler ve değişikliklerinin aynı nesneyi kullanan diğer kodlara görünür hale getirilmesi gerekiyor (bir "paylaşılabilir değişkenlik" örneği. Bu durumlar gerçekleştiğinde, etkilenen nesnelerin (akıllı) işaretçilerle paylaşılması gerekir.
rwong

16

Yazdığınız kodun doğruluğu asla bir optimizasyona bağlı olmamalıdır . Spesifikasyonda kullandıkları C ++ "sanal makinede" çalıştırıldığında doğru sonucu vermelidir.

Ancak, bahsettiğiniz şey daha çok bir tür etkinlik sorusudur. RVO optimizasyon derleyici ile optimize edildiğinde kodunuz daha iyi çalışır. Sorun değil, diğer nedenlerde belirtilen tüm nedenlerden ötürü.

Ancak, bu optimizasyona ihtiyacınız varsa (örneğin, kopya kurucu aslında kodunuzun başarısız olmasına neden olursa), şimdi derleyicinin kaprislerinde olacaksınız.

Sanırım bunun kendi uygulamamdaki en iyi örneği kuyruk çağrısı optimizasyonu:

   int sillyAdd(int a, int b)
   {
      if (b == 0)
          return a;
      return sillyAdd(a + 1, b - 1);
   }

Aptalca bir örnek, ancak bir fonksiyonun sonunda fonksiyonun tekrarlı olarak çağrıldığı bir kuyruk çağrısı gösterir. C ++ sanal makinesi bu kodun düzgün çalıştığını gösterse de, neden böyle bir ekleme yordamı yazmayı neden rahatsız ettiğim konusunda biraz karışıklığa neden olabilirim . Ancak, C ++ 'nın pratik uygulamalarında bir yığına sahibiz ve sınırlı bir alanı vardır. Bilgi niteliğinde bir şekilde yapılırsa, bu fonksiyon en az b + 1yığın çerçevelerini, eklenmesi gibi yığının üzerine itmek zorunda kalacaktır . Hesaplamak istersem sillyAdd(5, 7)bu önemli bir şey değil. Hesaplamak istersem sillyAdd(0, 1000000000), bir StackOverflow'a neden olmakta zorlanabilirim (ve iyi olanı değil ).

Ancak, bu son dönüş hattına ulaştığımızda, şu anki yığın çerçevesindeki her şeyle işimiz bittiğini görebiliriz. Gerçekten etrafta kalmamıza gerek yok. Kuyruk çağrısı optimizasyonu, bir sonraki fonksiyon için mevcut yığın çerçevesini "yeniden" kullanmanızı sağlar. Bu şekilde, sadece 1 yığın çerçevesine ihtiyacımız var b+1. (Hala bütün bu aptalca toplama ve çıkarma işlemlerini yapmak zorundayız, ancak daha fazla yer kaplamıyorlar.) Aslında, optimizasyon kodu şöyle yapar:

   int sillyAdd(int a, int b)
   {
      begin:
      if (b == 0)
          return a;
      // return sillyAdd(a + 1, b - 1);
      a = a + 1;
      b = b - 1;
      goto begin;  
   }

Bazı dillerde, kuyruk çağrısı optimizasyonu, spesifikasyon tarafından açıkça talep edilmektedir. C ++ onlardan biri değil . Durumda arama yapmazsam, bu kuyruk çağrısı optimizasyon fırsatını tanımak için C ++ derleyicilerine güvenemiyorum. Visual Studio sürümümle birlikte, sürüm sürümü kuyruk çağrısı optimizasyonunu yapar, ancak hata ayıklama sürümü (tasarımdan değil) yapmaz.

Bu yüzden hesaplayabilmeye bağlı kalmak benim için kötü olurdu sillyAdd(0, 1000000000).


2
Bu ilginç bir köşe davası, ancak ilk paragrafınızda kurallara genelleyebileceğinizi düşünmüyorum. Diyelim ki, sadece derleyicinin boyut küçültme optimizasyonlarını kullanırsam ve eğer yüklerse, yüklenecek küçük bir cihaz için bir programım var - yanlış mı? benim tek geçerli seçeneğimin assembler içinde yeniden yazmak olduğunu söylemek, özellikle de bu yeniden yazma işlemi optimizer'ın problemi çözmek için yaptığı şeyleri yapmasıdır.
sdenham

5
@sdenham Tartışmada küçük bir oda olduğunu varsayalım. Artık "C ++" için yazmıyorsanız, "WindRiver C ++ compiler version 3.4.1" için yazıyorsanız, oradaki mantığı görebiliyorum. Ancak, genel bir kural olarak, spesifikasyona göre düzgün çalışmayan bir şey yazıyorsanız, çok farklı bir senaryodasınız demektir. Boost kütüphanesinin böyle bir kodu olduğunu biliyorum, ancak her zaman #ifdefbloklar halinde koydular ve standartlara uygun bir geçici çözüm mevcut.
Cort Ammon

4
Bu yazdığı ikinci kod bloğundaki bir yazım hatası b = b + 1mı?
stib

2
Herhangi bir standart belgede kullanılan bir terim olmadığı için "C ++ sanal makinesi" ile ne demek istediğinizi açıklamak isteyebilirsiniz. Ben düşünüyorum Eğer C ++ yürütülmesi modeli söz ediyoruz, ama tamamen değil belli - ve terim tamamen farklı bir şey ile ilgilidir, bir "bayt kodu sanal makine" için aldatıcı benzer.
Toby Speight

1
@supercat Scala'de ayrıca kuyruk özyineleme sözdizimi de var. C ++ kendi canavarı, ancak kuyruk özyinelemesinin, işlevsel olmayan diller için sıradan olmadığını ve açıkça özyinelemeli sözdizimine sahip olmasının makul olduğu küçük bir dil seti bırakarak işlevsel diller için zorunlu olduğunu düşünüyorum. Kelimenin tam anlamıyla kuyruk özyinelemesinin döngülere ve açık mutasyona çevrilmesi, birçok dil için daha iyi bir seçenektir.
prosfilaes

8

Uygulamada C ++ programları bazı derleyici optimizasyonları bekliyorlar.

Özellikle standart konteyner uygulamalarınızın standart başlıklarına bakın . İle GCC , sen ön işlenen formu (isteyebilir g++ -C -E) ve (Gimple iç gösterimi g++ -fdump-tree-gimpleile veya Gimple SSA -fdump-tree-ssakapları kullanarak en kaynak dosyalarının) (teknik çeviri birimleri). Yapılan (ile g++ -O2) yapılan optimizasyon miktarına şaşıracaksınız . Bu yüzden, konteynerlerin uygulayıcıları optimizasyonlara güveniyor (ve çoğu zaman, bir C ++ standart kütüphanesinin uygulayıcısı ne optimizasyonun olacağını biliyor ve konteyner uygulamasını akılda kalanlarla birlikte yazıyor; bazen de optimizasyon geçişini derleyiciye yazacaktı. daha sonra standart C ++ kütüphanesi tarafından istenen özelliklerle ilgilenir)

Uygulamada, C ++ ve standart kaplarını yeterince verimli yapan derleyici optimizasyonlarıdır. Böylece onlara güvenebilirsiniz.

Ve aynı şekilde sorunuzda belirtilen RVO davası için.

C ++ standardı (özellikle yeni özellikler önerirken yeterince iyi optimizasyonlar denemek suretiyle) olası optimizasyonlarla iyi çalışacak şekilde birlikte tasarlanmıştır.

Örneğin, aşağıdaki programı göz önünde bulundurun:

#include <algorithm>
#include <vector>

extern "C" bool all_positive(const std::vector<int>& v) {
  return std::all_of(v.begin(), v.end(), [](int x){return x >0;});
}

ile derleyin g++ -O3 -fverbose-asm -S. Oluşturulan işlevin herhangi bir CALLmakine talimatını çalıştırmadığını göreceksiniz . Böylece çoğu C ++ basamağı (bir lambda kapama konstrüksiyonu, tekrarlanan uygulaması, tekrarlayanlar beginve endyineleyiciler vb.) Optimize edilmiştir. Makine kodu yalnızca bir döngü içeriyor (kaynak kodunda açıkça görünmüyor). Böyle bir optimizasyon olmadan, C ++ 11 başarılı olamaz.

addenda

(ilave 31 Ara st 2017)

Bakınız CppCon 2017: Matt Godbolt “Derleyicim En Son Ne Yaptı? Derleyicinin Kapağını Açma ” .


4

Ne zaman bir derleyici kullanırsanız, anlayış sizin için makine veya bayt kodu üretmesidir. Oluşturulan kodun neye benzediği hakkında herhangi bir garanti vermez, ancak kaynak kodu dilin şartnamesine göre uygular. Bu garantinin kullanılan optimizasyon seviyesine bakılmaksızın aynı olduğunu ve dolayısıyla genel olarak bir çıktının diğerinden daha 'doğru' olduğunu düşünmenin bir nedeni olmadığını unutmayın.

Ayrıca, dilde belirtildiği RVO gibi durumlarda, kaynak kodunu daha basit hale getirirse, kullanmaktan kaçınmanın yolundan çıkmanın anlamsız olduğu görülecektir.

Derleyicilerin verimli çıktılar üretmesi için çok çaba sarf edilmiştir ve açıkça bu amaçların kullanılması amaçlanmıştır.

Optimize edilmemiş kodun kullanılmasının nedenleri olabilir (örneğin, hata ayıklama için), ancak bu soruda belirtilen durum bir görünmüyor (ve kodunuz yalnızca optimize edildiğinde başarısız olursa ve Çalıştığınız cihaz, o zaman bir yerde bir hata var ve derleyicide olması muhtemel değildir.)


3

Bence diğerleri C ++ ve RVO ile ilgili özel açıları iyi anladılar. İşte daha genel bir cevap:

Doğruluk konusunda, derleyici optimizasyonlarına veya genel olarak derleyiciye özel davranışlara güvenmemelisiniz. Neyse ki, bunu yapıyor görünmüyorsunuz.

Performansa gelince , genel olarak derleyiciye özgü davranışa ve özellikle de derleyici optimizasyonlarına güvenmek zorundasınız . Standart uyumlu bir derleyici, kodunuzu dil belirtimine göre davrandığı sürece kodunuzu istediği şekilde derlemekte serbesttir. Ve her bir işlemin ne kadar hızlı olması gerektiğini belirten bir ana dil ile ilgili herhangi bir spesifikasyondan haberdar değilim.


1

Derleyici optimizasyonları yalnızca performansı etkilemeli, sonuçları etkilememelidir. İşlevsel olmayan gereklilikleri yerine getirmek için derleyici optimizasyonlarına güvenmek sadece mantıklı değil, bir derleyicinin diğerine göre seçilmesinin nedeni de genellikle budur.

Belirli işlemlerin nasıl yapıldığını belirleyen bayraklar (örneğin, dizin veya taşma koşulları), derleyici optimizasyonlarında sık sık toplanır, ancak olmamalıdır. Hesaplamaların sonuçlarını açıkça etkilerler.

Bir derleyici optimizasyonu farklı sonuçlara neden olursa, bu bir hatadır - derleyicideki bir hatadır. Derleyicideki bir hataya dayanmak, uzun vadede bir hatadır - düzeltildiğinde ne olur?

Hesaplamaların nasıl çalıştığını değiştiren derleyici bayraklarının kullanılması iyi belgelenmelidir, ancak gerektiği şekilde kullanılmalıdır.


Ne yazık ki, birçok derleyici dokümantasyonu, çeşitli modlarda neyin garanti edildiğini veya neyin garanti edilmediğini belirleme konusunda kötü bir iş çıkarmaktadır. Ayrıca, "modern" derleyiciler yazarları, programcıların ihtiyaç duyduğu ve ihtiyaç duymadığı garantilerin kombinasyonuna aldırış etmezler. Bir programın , herhangi bir yan etkisi olmaması koşuluylax*y>z , taşma durumunda keyfi bir şekilde 0 veya 1 vermesi halinde bir program iyi çalışırsa , bir programcının tüm masrafları aşan taşmaları önlemesi veya derleyiciyi ifadeyi belirli bir şekilde değerlendirmeye zorlaması gerekir.
Tabii

... derleyici olabilir onun boş olarak olsa davranırlar x*y(böylece Kaldırma ve bazı taşma durumlarda davranışını değiştirecek gücü azaltma türüne izin verme) bazı keyfi daha uzun türüne onun işlenen teşvik etmektedir. Ancak birçok derleyici, programcıların her ne pahasına olursa olsun taşmayı engellemesini veya derleyicileri taşma durumunda tüm ara değerleri kesmeye zorlar.
supercat,

1

Hayır.

Ben her zaman böyle yaparım. Bellekte rastgele bir 16 bit bloğa erişmem gerekirse, bunu yaparım

void *ptr = get_pointer();
uint16_t u16;
memcpy(&u16, ptr, sizeof(u16)); // ntohs omitted for simplicity

... ve bu kod parçasını optimize etmek için elinden geleni yapan derleyiciye güven. Kod ARM, i386, AMD64 ve pratik olarak her mimaride çalışır. Teoride, optimizasyon yapmayan bir derleyici aslında çağırabilir memcpyve tamamen kötü performansa neden olabilir , ancak derleyici optimizasyonları kullandığım için bu benim için sorun değil.

Alternatifleri düşünün:

void *ptr = get_pointer();
uint16_t *u16ptr = ptr;
uint16_t u16;
u16 = *u16ptr;  // ntohs omitted for simplicity

Bu alternatif kod, get_pointer()hizalanmamış bir işaretçi döndürürse düzgün hizalama gerektiren makinelerde çalışmaz . Ayrıca, alternatifte takma sorunlar olabilir.

memcpyHile kullanılırken -O2 ile -O0 arasındaki fark harika: 3.2 Gbps IP sağlama toplamı performansı ile 67 Gbps IP sağlama toplamı performansı. Büyüklük farkı sırasına göre!

Bazen derleyiciye yardım etmeniz gerekebilir. Örneğin, döngüleri açmak için derleyiciye güvenmek yerine, bunu kendiniz yapabilirsiniz. Ya ünlü Duff'un cihazını uygulayarak ya da daha temiz bir şekilde.

Derleyici optimizasyonlarına güvenmenin dezavantajı, kodunuzun hatalarını ayıklamak için gdb'yi çalıştırırsanız, birçoğunun optimize edildiğini öğrenmenizdir. Yani, -O0 ile yeniden derlemeniz gerekebilir, yani performans hata ayıklama sırasında tamamen emilecektir. Derleyicileri optimize etmenin faydalarını göz önünde bulundurarak bunun bir dezavantaj olduğunu düşünüyorum.

Ne yaparsanız yapın, yolunuzun aslında tanımsız davranış olmadığından emin olun. Rasgele bir bellek bloğuna 16-bit tamsayı olarak erişilmesi, takma ve hizalama sorunları nedeniyle tanımlanmamış davranışlardır.


0

Herhangi bir şeyde yazılan ancak etkin kodda yapılan tüm girişimler, çok fazla yığın derleyici optimizasyonuna dayanır, her yere gereksiz yığın dökülmelerini önlemek için en temel benzeri verimli kayıt tahsisi ile başlar ve mükemmel değilse, seçim seçiminde en azından makul derecede iyidir. Aksi takdirde, registerher yere ipucu koymak zorunda kaldığımız 80'li yıllara geri dönüp, arka arkaya C derleyicilerine ya da daha önce gotoyararlı bir dallanma optimizasyonuna yardımcı olmak için asgari değişken sayısını bir fonksiyonda kullanmak zorunda kalacağız .

Optimize edicimizin kodumuzu optimize etme kabiliyetine güvenebileceğimizi hissetmeseydik, hepimiz hala montajda kritik performans yürütme yollarını kodluyorduk.

Bu, gerçekten en iyi şekilde, sahip olduğunuz derleyicilerin yeteneklerine bakarak ve derleyicinin nerede göründüğünü anlayamayacağınız bir sıcak nokta varsa bile sökmeden yapılan optimizasyonun yapılabileceğini düşündüğünüz bir meseledir. bariz bir optimizasyon yapmakta başarısız oldunuz.

RVO, yıllardır etrafta olan bir şeydir ve en azından çok karmaşık vakaları hariç tutan, derleyicilerin yaş için güvenilir bir şekilde uyguladığı bir şeydir. Var olmayan bir problemin üzerinde çalışmaya kesinlikle değmez.

Doktoru Güvenmeye Yönelik Yanında Err, Korkma

Aksine, derleyici optimizasyonuna çok az güvenmek konusunda çok fazla güvenmenin yanında bir hata olduğunu söyleyebilirim ve bu öneri, müşteriler arasında verimlilik, sürdürülebilirlik ve algılanan kalitenin çok önemli olduğu kritik performans alanlarında çalışan bir adamdan geliyor. hepsi bir dev bulanıklık. İyileştiricinize çok fazla güvenmenize ve çok fazla güvenmemenizin ve çok fazla güvendiğiniz bazı kesin kenar vakaları bulmanızı ve hayatınızın geri kalanında her zaman batıl inançlardan kurtulmanızı tercih ederim. Bu, en azından bir profilciye ulaşmanızı ve işler gerektiği kadar çabuk yürütülmezse ve batıl inançları değil, değerli bilgiyi edinmenizi sağlar.

İyileştiriciye yaslanmak için iyi gidiyorsunuz. Aynen böyle devam. Optimize edicinin eksikliklerine dair yanlış yönlendirilmiş bir korkuyu bile ön plana çıkarmadan önce, bir döngüde çağrılan her fonksiyonun satır içi olmasını istemeye başlayan o adam gibi davranmayın.

profil Oluşturma

Profil oluşturma gerçekten dolambaçlı, ancak sorunuzun nihai cevabı. Etkili bir kod yazmaya sık sık karşı karşıya kalmaya istekli olan yeni başlayanlar, neyin optimize edileceği değil , neyin optimize edilmeyeceği konusundaki verimsizliklerle ilgili her türlü yanlış yönlendirilmiş ipucunu geliştirdikleri için neyin optimize edileceği değil. Bir profil oluşturucu ile deneyim geliştirmek, size sadece derleyicilerinizin güvenle güvenebileceğiniz optimizasyon yetenekleri değil, aynı zamanda donanımınızın yetenekleri (sınırlamaları) hakkında da tam bir takdir kazandırmaya başlayacaktır. Öğrenmede profil oluşturmada neyin öğrenilenden daha iyileştirilmeye değmeyeceği daha fazla değer vardır.


-1

Yazılım C ++ 'da çok farklı platformlarda ve birçok farklı amaç için yazılabilir.

Tamamen yazılımın amacına bağlıdır. Bakımı, genişletilmesi, yaması, refactor et.c. veya performans, maliyet veya belirli bir donanıma veya geliştirme zamanına uyumluluk gibi diğer hususlar daha önemlidir.


-2

Bunun sıkıcı cevabı şudur: 'bağlıdır'.

Devre dışı bırakılması muhtemel olan ve güvenlik açığının belgelenmediği ve söz konusu kodun ünite test edilmediği ve bu güvenlik açığının belgelendirilmediği durumlarda , kodun yazılması kötü bir uygulama mıdır ? Muhtemelen.

Bir derleyici optimizasyon bağlı kodu yazmak için kötü bir uygulama mı kapalı olması olası değildir yani belgelenmiştir ve birim test edilir ? Belki de değil.


-6

Bize söylemediğiniz daha fazla olmadığı sürece, bu kötü bir uygulamadır, ancak önerdiğiniz nedenle değil.

Muhtemelen daha önce kullandığınız diğer dillerden farklı olarak, C ++ 'da bir nesnenin değerini döndürmek nesnenin bir kopyasını verir. Daha sonra nesneyi değiştirirseniz, farklı bir nesneyi değiştirirsiniz . Yani, eğer öyleysem Obj a; a.x=1;ve Obj b = a;öyleyse b.x += 2; b.f();, öyleyse a.xyine de 3 değil 1 olur.

Bu yüzden hayır, bir nesneyi bir referans veya işaretçi yerine bir değer olarak kullanmak aynı işlevi sağlamaz ve yazılımınızdaki hatalara neden olabilirsiniz.

Belki bunu biliyorsunuzdur ve sizin özel kullanım durumunuzu olumsuz yönde etkilemez. Ancak, sorunuzdaki ifadeye dayanarak, bu ayrımdan haberdar olamayacağınız anlaşılıyor; "fonksiyonda bir nesne yarat" gibi ifadeler.

"işlevinde bir nesne yarat" " new Obj;nerede nesneyi değere göre döndür" gibi seslerObj a; return a;

Obj a;ve Obj* a = new Obj;çok, çok farklı şeylerdir; İlki doğru kullanılmadığı ve anlaşılmadığı takdirde bellek bozulmasına yol açabilir ve ikincisi doğru kullanılmadığı ve anlaşılmadığı takdirde bellek sızıntılarına neden olabilir.


8
Dönüş değeri optimizasyonu (RVO), derleyicinin yığın çerçevesi üzerinde bir seviye üste dönen bir nesne oluşturduğu, özellikle gereksiz nesne kopyalarından kaçındığı iyi tanımlanmış bir semantiktir. Bu, C ++ 17'de zorunlu hale getirilmeden çok önce desteklenmiş iyi tanımlanmış bir davranıştır. 10-15 yıl önce bile, tüm büyük derleyiciler bu özelliği destekledi ve tutarlı bir şekilde yaptı.

@ Bey, fiziksel, düşük seviye bellek yönetiminden bahsetmiyorum ve bellek şişmesi veya hızını tartışmadım. Cevabımda özellikle gösterdiğim gibi, mantıksal verilerden bahsediyorum. Mantıksal olarak , bir nesnenin değerini sağlamak, derleyicinin nasıl uygulandığına veya sahnelerin arkasında hangi montajın kullanıldığına bakılmaksızın bir kopyasını oluşturur. Perdenin arkasındaki düşük seviye şeyler bir şeydir ve dilin mantıksal yapısı ve davranışı başkadır; birbirleriyle ilişkilidirler, ama aynı şey değillerdir - her ikisi de anlaşılmalıdır.
Aaron,

6
cevabınız "C ++ 'da bir nesnenin değerini döndürmek nesnenin bir kopyasını verir" diyor RVO bağlamında tamamen yanlıştır - nesne doğrudan arama konumunda inşa edilir ve hiçbir kopya yapılmaz. Sen kopya kurucu silme ve nesneyi döndürerek bu test edebilirsiniz inşa edilmiştir returnaçıklamada RVT için bir gerekliliktir. Ayrıca, anahtar kelime newve işaretçiler hakkında konuşmaya devam edersiniz . Sorunu anlamadığınıza veya RVO'ya veya muhtemelen her ikisine de inanıyorsunuz.

-7

Pieter B, en az şaşkınlığı tavsiye etmede kesinlikle haklıdır.

Özel sorunuza cevap vermek için, bu (en muhtemel) C ++ 'da ne anlama geliyor std::unique_ptr?

Sebebi, bunun bir C ++ geliştiricisi için olan bitenden daha açık olmasıdır .

Her ne kadar yaklaşımınız işe yarayacak olsa da, aslında olmadığında, nesnenin küçük bir değer türü olduğunu etkin bir şekilde işaret ediyorsunuz. Bunun da ötesinde, arayüz soyutlama olasılığını ortadan kaldırıyorsunuz. Bu, şu andaki amaçlarınız için uygun olabilir, ancak matrislerle uğraşırken genellikle çok faydalıdır.

Diğer dillerden geliyorsanız, tüm işaretlerin başlangıçta kafa karıştırıcı olabileceğini takdir ediyorum. Ancak bunları kullanmayarak, kodunuzu daha açık hale getirdiğinizi varsaymamaya dikkat edin. Uygulamada, bunun tersi muhtemeldir.


Roma'da romalılar gibi davran.

14
Bu, dinamik ayırmalar yapmayan türler için iyi bir cevap değildir. OP'nin kullanım durumundaki doğal şeyi hissettiği değere göre geri dönmek, nesnelerinin arayan tarafında otomatik saklama süresine sahip olduğunu gösterir. Basit, çok büyük olmayan nesneler için, naif bir kopya-geri dönüş değeri uygulaması bile, dinamik bir tahsisattan daha hızlı büyüklük emirleri olacaktır. (Öte yandan, işlev bir kap döndürürse, benzersiz bir işaretçi döndürmek değere göre naif bir derleyicinin getirisine kıyasla avantajlı olabilir.)
Peter A. Schneider

9
@Matt Farkında değilsen, bunun en iyi uygulama olmadığını. Gereksiz yere bellek tahsisi yapmak ve kullanıcılara işaretçi anlamını zorlamak kötüdür.
nwp

5
Her şeyden önce, akıllı işaretçiler kullanılırken std::make_unique, bir std::unique_ptrdoğrudan değil , geri dönmelidir . İkincisi, RVO bazı ezoterik, satıcıya özel optimizasyonlar değil: standarda dahil edilmiştir. Olmadığında bile, yaygın olarak desteklenmiş ve beklenen davranışlardı. std::unique_ptrİlk önce bir işaretçiye ihtiyaç duyulmadığında bir şey döndürmenin bir anlamı yoktur .

4
@Snowman: "Olmadığı zaman" yoktur. Sadece son zamanlarda zorunlu hale gelmesine rağmen , her C ++ standardı [N] RVO'yu tanıdı ve etkinleştirmek için konaklama yaptı (örneğin, derleyiciye, kopya yapıcısının dönüş değeri üzerinde kullanılmasına izin vermese bile, her zaman açıkça izin verildi. görünür yan etkileri var).
Jerry Coffin,
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.