C ++ derleme sürelerini hızlandırmak için hangi teknikler kullanılabilir?


249

C ++ derleme sürelerini hızlandırmak için hangi teknikler kullanılabilir?

Bu soru, Stack Overflow soru C ++ programlama stiline bazı yorumlarda geldi ve hangi fikirlerin olduğunu duymak isterim.

İlgili bir soru gördüm , C ++ derlemesi neden bu kadar uzun sürüyor? ancak bu pek çok çözüm sağlamaz.


1
Bize biraz bağlam verebilir misiniz? Yoksa çok genel cevaplar mı arıyorsunuz?
Pirolistik


Genel cevaplar. Birçok kişi tarafından yazılmış gerçekten büyük bir kod tabanım var. Buna nasıl saldırılacağına dair fikirler iyi olurdu. Ayrıca yeni derlenmiş kodlar için derlemeleri hızlı tutmaya yönelik öneriler de ilginç olacaktır.
Scott Langham

Genellikle yapı zaman ilgili kısmı derleyici tarafından ancak yapı komut tarafından kullanılmadığından emin Bildirimi
Thi gg

1
Bu sayfayı gözden kaçırdım ve herhangi bir ölçümden bahsetmedim. Aldığım her girdi satırına bir zaman damgası ekleyen küçük bir kabuk komut dosyası yazdım, böylece sadece 'make' çağrısında boru oluşturabilirim. Bu, yalnızca zaman damgalarını karşılaştırarak hangi hedeflerin en pahalı olduğunu, toplam derleme veya bağlantı süresini vb. Bu yaklaşımı denerseniz, paralel damgalamalarda zaman damgalarının yanlış olacağını unutmayın.
John P

Yanıtlar:


257

Dil teknikleri

Pimpl Deyim

Bir göz atın Pimpl deyim burada ve burada da bir olarak bilinen opak işaretçi veya sap sınıfları. Sadece derlemeyi hızlandırmakla kalmaz, aynı zamanda fırlatmayan takas işleviyle birleştirildiğinde istisna güvenliği de artırır . Pimpl deyimi, başlıklar arasındaki bağımlılıkları azaltmanızı ve yapılması gereken yeniden derleme miktarını azaltmanızı sağlar.

İleri Beyanlar

Mümkün olan yerlerde, ileri bildirimler kullanın . Derleyicinin bunun yalnızca SomeIdentifierbir yapı ya da işaretçi ya da başka bir şey olduğunu bilmesi gerekiyorsa, tanımın tamamını dahil etmeyin, derleyiciyi gereğinden fazla iş yapmaya zorlayın. Bunun basamaklı bir etkisi olabilir ve bu da olması gerekenden daha yavaş olur.

I / O akışları, özellikle inşa yavaşlatmak için bilinmektedir. Bir başlık dosyasında bunlara ihtiyacınız varsa, yalnızca uygulama dosyasında başlığı <iosfwd>yerine <iostream>#include ve # include'u deneyin <iostream>. <iosfwd>Başlık ileriye bildirimleri yalnızca tutar. Maalesef diğer standart başlıkların ilgili açıklama başlıkları yoktur.

İşlev imzalarında, değere göre değere başvurmayı tercih et. Bu, başlık dosyasında ilgili tür tanımlarını # ekleme gereksinimini ortadan kaldıracak ve yalnızca türü ileri bildirmeniz gerekecektir. Elbette, belirsiz hataları önlemek için const olmayan referanslara const referanslarını tercih edin, ancak bu başka bir soru için bir sorundur.

Muhafız Koşulları

Başlık dosyalarının tek bir çeviri birimine birden çok kez dahil edilmesini önlemek için koruma koşullarını kullanın.

#pragma once
#ifndef filename_h
#define filename_h

// Header declarations / definitions

#endif

Hem pragma hem de ifndef kullanarak, düz makro çözümünün taşınabilirliğini ve bazı derleyicilerin pragma oncedirektif varlığında yapabileceği derleme hızı optimizasyonunu elde edersiniz .

Karşılıklı bağımlılığı azaltın

Kod tasarımınız ne kadar modüler ve daha az birbirine bağımlıysa, genel olarak her şeyi yeniden derlemeniz gerekecektir. Ayrıca, takip edilebilecek daha az şey olması nedeniyle, derleyicinin herhangi bir blokta aynı anda yapması gereken iş miktarını da azaltabilirsiniz.

Derleyici seçenekleri

Önceden Derlenmiş Üstbilgiler

Bunlar, birçok çeviri birimi için dahil edilen başlıkların ortak bir bölümünü derlemek için kullanılır. Derleyici bir kez derler ve dahili durumunu kaydeder. Bu durum daha sonra aynı başlık kümesiyle başka bir dosyayı derlemeye başlamak için hızlı bir şekilde yüklenebilir.

Önceden derlenmiş başlıklara yalnızca nadiren değiştirilen öğeleri eklediğinizden emin olun, aksi takdirde tam yeniden oluşturma işlemlerini gerektiğinden daha sık yapabilirsiniz. Bu STL başlıkları ve diğer kütüphane dosyaları için iyi bir yerdir .

ccache , işleri hızlandırmak için önbellekleme tekniklerinden yararlanan bir başka yardımcı programdır.

Paralellik Kullan

Birçok derleyici / IDE aynı anda derleme yapmak için birden fazla çekirdek / CPU kullanımını destekler. In GNU Make (genellikle GCC ile kullanılır), kullanmak -j [N]seçeneği. Visual Studio'da, tercihler altında paralel olarak birden fazla proje oluşturmasına izin veren bir seçenek vardır. Yalnızca proje düzeyinde paralellik yerine dosya düzeyinde paralellizm /MPseçeneğini de kullanabilirsiniz .

Diğer paralel yardımcı programlar:

Daha Düşük Bir Optimizasyon Düzeyi Kullanın

Derleyici ne kadar çok optimizasyon yapmaya çalışırsa, o kadar çok çalışması gerekir.

Paylaşılan Kütüphaneler

Daha az değiştirilen kodunuzu kütüphanelere taşımak derleme süresini azaltabilir. Paylaşılan kitaplıkları ( .soveya .dll) kullanarak bağlantı süresini de azaltabilirsiniz.

Daha Hızlı Bilgisayar Alın

Daha fazla RAM, daha hızlı sabit sürücüler (SSD'ler dahil) ve daha fazla CPU / çekirdek derleme hızında fark yaratacaktır.


11
Önceden derlenmiş başlıklar mükemmel değildir. Bunları kullanmanın bir yan etkisi, gerektiğinden daha fazla dosya almanızdır (çünkü her derleme birimi aynı önceden derlenmiş başlığı kullanır), bu da tam yeniden derlemeleri gerekenden daha sık zorlayabilir. Sadece akılda tutulması gereken bir şey.
jalf

8
Modern derleyicilerde, #ifndef bir kez #pragma kadar hızlıdır (dahil etme koruması dosyanın üstünde olduğu sürece). Yani derleme hızı açısından #pragma'ya bir kez faydası yok
jalf

7
2008 değil, sadece VS 2005'iniz olsa bile, .cpp düzeyinde paralel oluşturmayı etkinleştirmek için derleme seçeneklerinde / MP anahtarı ekleyebilirsiniz.
macbirdie

6
SSD'ler bu cevap yazıldığında oldukça pahalıydı, ancak bugün C ++ derlerken en iyi seçim. Derlerken çok sayıda küçük dosyaya erişirsiniz. Bu, SSD'lerin sunduğu birçok IOPS gerektirir.
MSalters

14
İşlev imzalarında, değere göre değere başvurmayı tercih et. Bu, başlık dosyasında ilgili tür tanımlarını dahil etme gereğini ortadan kaldıracaktır. Bu yanlıştır , değere göre geçen bir işlevi bildirmek için tam türe sahip olmanız gerekmez, yalnızca bu işlevi uygulamak veya kullanmak için tam türe ihtiyacınız vardır. , ancak çoğu durumda (yalnızca aramaları yönlendirmiyorsanız) yine de bu tanıma ihtiyacınız olacaktır.
David Rodríguez - dribeas

43

Çok tempüle bir C ++ kütüphanesi olan STAPL projesinde çalışıyorum. Arada sırada, derleme süresini azaltmak için tüm teknikleri tekrar gözden geçirmeliyiz. Burada kullandığımız teknikleri özetledim. Bu tekniklerin bazıları yukarıda listelenmiştir:

En çok zaman alan bölümleri bulma

Sembol uzunlukları ve derleme süresi arasında kanıtlanmış bir korelasyon olmamasına rağmen, daha küçük ortalama sembol boyutlarının tüm derleyicilerde derleme süresini iyileştirebileceğini gözlemledik. Yani ilk hedefleriniz kodunuzdaki en büyük sembolleri bulmaktır.

Yöntem 1 - Sembolleri boyuta göre sırala

nmKomutları, sembolleri boyutlarına göre listelemek için kullanabilirsiniz :

nm --print-size --size-sort --radix=d YOUR_BINARY

Bu komutta --radix=dboyutları ondalık sayılarla görmenizi sağlar (varsayılan hex'dir). Şimdi en büyük sembole bakarak, karşılık gelen sınıfı kırabileceğinizi belirleyin ve bir temel sınıftaki şablonlanmamış parçaları çarpanlarına ayırarak veya sınıfı birden çok sınıfa bölerek yeniden tasarlamaya çalışın.

Yöntem 2 - Sembolleri uzunluğa göre sıralama

Normal nmkomutu çalıştırabilir ve sembolleri uzunluklarına göre sıralamak için sık kullandığınız komut dosyasına ( AWK , Python vb.) Ekleyebilirsiniz . Deneyimlerimize dayanarak, bu yöntem adayları yöntem 1'den daha iyi hale getirmede en büyük sorunu tanımlar.

Yöntem 3 - Templight'ı kullanma

" Templight , şablon örneklemelerinin zamanını ve bellek tüketimini belirlemek ve şablon örnekleme işlemine introspeksiyon kazanmak için etkileşimli hata ayıklama oturumları gerçekleştirmek için Clang tabanlı bir araçtır".

Templight'ı LLVM ve Clang'a ( talimatlar ) bakarak ve Templight yamasını uygulayarak yükleyebilirsiniz. LLVM ve Clang için varsayılan ayar hata ayıklama ve iddialardadır ve bunlar derleme sürenizi önemli ölçüde etkileyebilir. Templight'ın her ikisine de ihtiyacı var gibi görünüyor, bu yüzden varsayılan ayarları kullanmanız gerekiyor. LLVM ve Clang yükleme işlemi yaklaşık bir saat kadar sürmelidir.

Düzeltme ekini uyguladıktan sonra templight++, kodunuzu derlemek için yükleme sırasında belirttiğiniz oluşturma klasöründe kullanabilirsiniz.

Bunun PATH'nizde olduğundan emin olun templight++. Şimdi derlemek CXXFLAGSiçin Makefile'nize veya komut satırı seçeneklerinize aşağıdaki anahtarları ekleyin :

CXXFLAGS+=-Xtemplight -profiler -Xtemplight -memory -Xtemplight -ignore-system

Veya

templight++ -Xtemplight -profiler -Xtemplight -memory -Xtemplight -ignore-system

Derleme tamamlandıktan sonra, aynı klasörde bir .trace.memory.pbf ve .trace.pbf dosyası oluşturulur. Bu izleri görselleştirmek için bunları diğer biçimlere dönüştürebilen Templight Araçlarını kullanabilirsiniz . Templight-convert'i yüklemek için bu talimatları izleyin . Genellikle callgrind çıktısını kullanırız. Projeniz küçükse GraphViz çıktısını da kullanabilirsiniz:

$ templight-convert --format callgrind YOUR_BINARY --output YOUR_BINARY.trace

$ templight-convert --format graphviz YOUR_BINARY --output YOUR_BINARY.dot

Oluşturulan callgrind dosyası kullanılarak açılabilir , en çok zaman / bellek tüketen örneği izleyebileceğiniz kcachegrind .

Şablon örneklerinin sayısını azaltma

Şablon örneklemelerinin sayısını azaltmak için kesin bir çözüm olmamasına rağmen, yardımcı olabilecek birkaç kural vardır:

Birden fazla şablon bağımsız değişkenine sahip refactor sınıfları

Örneğin, bir sınıfınız varsa,

template <typename T, typename U>
struct foo { };

ve her ikisi de T ve U10 farklı seçenek olabilir, bu soyut farklı bir sınıfa kod ortak parçasıdır gidermek için 100. Bir şekilde bu sınıfın olası şablon instantiations artmıştır. Diğer yöntem miras inversiyonunu kullanmaktır (sınıf hiyerarşisini tersine çevirmek), ancak bu tekniği kullanmadan önce tasarım hedeflerinizin tehlikeye atılmadığından emin olun.

Tek tek çeviri birimlerine yeniden şablonlanmamış kod

Bu tekniği kullanarak ortak bölümü bir kez derleyebilir ve daha sonra diğer TU'larınızla (çeviri birimleri) bağlayabilirsiniz.

Harici şablon örneklerini kullan (C ++ 11'den beri)

Bir sınıfın tüm olası örneklerini biliyorsanız, bu tekniği farklı bir çeviri birimindeki tüm vakaları derlemek için kullanabilirsiniz.

Örneğin, içinde:

enum class PossibleChoices = {Option1, Option2, Option3}

template <PossibleChoices pc>
struct foo { };

Bu sınıfın üç olası örneği olabileceğini biliyoruz:

template class foo<PossibleChoices::Option1>;
template class foo<PossibleChoices::Option2>;
template class foo<PossibleChoices::Option3>;

Yukarıdakini bir çeviri birimine yerleştirin ve başlık dosyanızda sınıf tanımının altında extern anahtar sözcüğünü kullanın:

extern template class foo<PossibleChoices::Option1>;
extern template class foo<PossibleChoices::Option2>;
extern template class foo<PossibleChoices::Option3>;

Ortak bir örnek kümesiyle farklı testler derliyorsanız bu teknik size zaman kazandırabilir.

NOT: MPICH2 bu noktada açık örneklemeyi yoksayar ve her zaman tüm derleme birimlerinde örneklenmiş sınıfları derler.

Birlik derlemelerini kullanma

Birlik derlemelerinin ardındaki fikir, kullandığınız tüm .cc dosyalarını tek bir dosyaya dahil etmek ve bu dosyayı yalnızca bir kez derlemektir. Bu yöntemi kullanarak, farklı dosyaların ortak bölümlerini eski haline getirmekten kaçınabilirsiniz ve projenizde çok sayıda ortak dosya varsa, muhtemelen disk erişimlerinden de tasarruf edersiniz.

Örnek olarak, diyelim ki üç dosya var varsayalım foo1.cc, foo2.cc, foo3.ccve hepsi dahil tupledan STL . foo-all.ccŞuna benzeyen bir tane oluşturabilirsiniz :

#include "foo1.cc"
#include "foo2.cc"
#include "foo3.cc"

Bu dosyayı yalnızca bir kez derlersiniz ve muhtemelen üç dosya arasındaki ortak örneklemeleri azaltırsınız. İyileştirmenin anlamlı olup olmadığını tahmin etmek genellikle zordur. Ancak açık bir gerçek, yapılarınızda paralelliğinizi kaybedeceğinizdir (artık üç dosyayı aynı anda derleyemezsiniz).

Ayrıca, bu dosyalardan herhangi biri çok fazla bellek alırsa, derleme bitmeden önce belleğiniz tükenebilir. GCC gibi bazı derleyicilerde, bellek yetersizliği nedeniyle derleyiciniz ICE (Dahili Derleyici Hatası) olabilir. Yani tüm artıları ve eksileri bilmiyorsanız bu tekniği kullanmayın.

Önceden derlenmiş başlıklar

Önceden derlenmiş üstbilgiler (PCH'ler), üstbilgi dosyalarınızı bir derleyici tarafından tanınan bir ara gösterimde derleyerek size çok zaman kazandırabilir. Önceden derlenmiş başlık dosyaları oluşturmak için, yalnızca başlık dosyanızı normal derleme komutunuzla derlemeniz gerekir. Örneğin, GCC'de:

$ g++ YOUR_HEADER.hpp

Bu , aynı klasörde YOUR_HEADER.hpp.gch file( .gchGCC'deki PCH dosyalarının uzantısıdır) oluşturur. Bu, YOUR_HEADER.hppbaşka bir dosyaya eklerseniz , derleyicinin daha önce aynı klasör YOUR_HEADER.hpp.gchyerine kullanacağınız anlamına gelir YOUR_HEADER.hpp.

Bu teknikle ilgili iki sorun vardır:

  1. Önceden derlenen başlık dosyalarının sabit olduğundan ve değişmeyeceğinden emin olmalısınız ( her zaman makefile'ınızı değiştirebilirsiniz )
  2. Derleme birimi başına yalnızca bir PCH ekleyebilirsiniz (derleyicilerin çoğunda). Bu, önceden derlenecek birden fazla başlık dosyanız varsa, bunları bir dosyaya (örn all-my-headers.hpp.) Dahil etmeniz gerektiği anlamına gelir . Ancak bu, yeni dosyayı tüm yerlere eklemeniz gerektiği anlamına gelir. Neyse ki, GCC'nin bu soruna bir çözümü var. Kullanın -includeve yeni başlık dosyasını verin. Bu tekniği kullanarak farklı dosyaları virgülle ayırabilirsiniz.

Örneğin:

g++ foo.cc -include all-my-headers.hpp

Adsız veya anonim ad alanları kullanın

Adsız ad alanları (adsız ad alanları olarak da bilinir) oluşturulan ikili boyutları önemli ölçüde azaltabilir. Adsız ad alanları iç bağlantı kullanır, yani bu ad alanlarında oluşturulan semboller diğer TU (çeviri veya derleme birimleri) tarafından görülmez. Derleyiciler genellikle adsız ad alanları için benzersiz adlar oluşturur. Bu, foo.hpp dosyanız varsa:

namespace {

template <typename T>
struct foo { };
} // Anonymous namespace
using A = foo<int>;

Ve bu dosyayı iki TU'ya dahil ediyorsunuz (iki .cc dosyası ve ayrı olarak derleyin). İki foo şablonu örneği aynı olmayacaktır. Bu, Tek Tanımlama Kuralını (ODR) ihlal eder . Aynı nedenle, başlık dosyalarında adsız ad alanlarının kullanılması önerilmez. .ccİkili dosyalarınızda sembollerin görünmesini önlemek için bunları dosyalarınızda kullanmaktan çekinmeyin . Bazı durumlarda, bir .ccdosyanın tüm dahili ayrıntılarının değiştirilmesi , oluşturulan ikili boyutlarda% 10'luk bir azalma gösterdi.

Görünürlük seçeneklerini değiştirme

Daha yeni derleyicilerde sembollerinizi Dinamik Paylaşılan Nesneler'de (DSO'lar) görünür veya görünmez olarak seçebilirsiniz. İdeal olarak, görünürlüğün değiştirilmesi derleyici performansını, bağlantı zamanı optimizasyonlarını (LTO'lar) ve oluşturulan ikili boyutları artırabilir. GCC'deki STL başlık dosyalarına bakarsanız, yaygın olarak kullanıldığını görebilirsiniz. Görünürlük seçimlerini etkinleştirmek için kodunuzu işlev başına, sınıf başına, değişken başına ve daha da önemlisi derleyici başına değiştirmeniz gerekir.

Görünürlük yardımıyla, onları özel olarak değerlendirdiğiniz sembolleri oluşturulan paylaşılan nesnelerden gizleyebilirsiniz. GCC'de, varsayılan veya gizli -visibilityderleyicinizin seçeneğine geçerek sembollerin görünürlüğünü kontrol edebilirsiniz . Bu, bir anlamda isimsiz isim alanına benzer, ancak daha ayrıntılı ve müdahaleci bir şekilde.

Vaka başına görünürlükleri belirtmek isterseniz, işlevlerinize, değişkenlerinize ve sınıflarınıza aşağıdaki nitelikleri eklemeniz gerekir:

__attribute__((visibility("default"))) void  foo1() { }
__attribute__((visibility("hidden")))  void  foo2() { }
__attribute__((visibility("hidden")))  class foo3   { };
void foo4() { }

GCC'deki varsayılan görünürlük varsayılan (herkese açık) şeklindedir, yani yukarıdakileri paylaşılan bir kütüphane ( -shared) yöntemi olarak derlerseniz foo2ve sınıf foo3diğer TU'larda görünmez ( foo1ve foo4görünür olur). Derleme yaparsanız -visibility=hiddensadece foo1görünür. Hatta foo4saklı olurdu.

Görünürlük hakkında daha fazla bilgiyi GCC wiki'de okuyabilirsiniz .


33

"İçeriden Oyunlar, Bağımsız Oyun Tasarımı ve Programlama" dan şu makaleleri öneriyorum:

Evet, oldukça eski - gerçekçi sonuçlar almak için her şeyi en son sürümlerle (veya sizin için mevcut sürümlerle) yeniden test etmeniz gerekecek. Her iki durumda da, fikirler için iyi bir kaynaktır.


17

Geçmişte benim için oldukça iyi çalışan bir teknik: birden fazla C ++ kaynak dosyasını bağımsız olarak derlemeyin, aksine tüm diğer dosyaları içeren bir C ++ dosyası oluşturun:

// myproject_all.cpp
// Automatically generated file - don't edit this by hand!
#include "main.cpp"
#include "mainwindow.cpp"
#include "filterdialog.cpp"
#include "database.cpp"

Tabii ki bu, kaynaklardan herhangi birinin değişmesi durumunda dahil edilen tüm kaynak kodunu yeniden derlemeniz gerektiği anlamına gelir, böylece bağımlılık ağacı daha da kötüleşir. Ancak, birden fazla kaynak dosyasını bir çeviri birimi olarak derlemek daha hızlıdır (en azından MSVC ve GCC ile yaptığım deneylerde ) ve daha küçük ikili dosyalar oluşturur. Ayrıca derleyiciye optimizasyon için daha fazla potansiyel verildiğinden şüpheleniyorum (aynı anda daha fazla kod görebildiğinden).

Bu teknik çeşitli durumlarda kırılır; örneğin, iki veya daha fazla kaynak dosyasının aynı ada sahip bir global işlev bildirmesi durumunda derleyici kurtarılacaktır. Yine de diğer tekniklerin hiçbirinde anlatılan bu tekniği bulamadım, bu yüzden burada bahsediyorum.

KDE Projesi , değerinden dolayı, 1999'dan beri aynı tekniği aynı zamanda optimize edilmiş ikili dosyalar oluşturmak için kullandı (muhtemelen bir sürüm için). Yapılandırma komut dosyasına geçiş çağrıldı --enable-final. Arkeolojik çıkarlar dışında bu özelliği açıklayan gönderimi kazdım : http://lists.kde.org/?l=kde-devel&m=92722836009368&w=2


2
Gerçekten aynı şey olup olmadığından emin değilim, ancak VC ++ ( msdn.microsoft.com/en-us/library/0zza0de8%28VS.71%29.aspx ) 'de "Tüm program optimizasyonu" nu açmayı tahmin ediyorum Çalışma zamanı performansı üzerinde önerdiğinizle aynı etki. Ancak derleme zamanı, yaklaşımınızda kesinlikle daha iyi olabilir!
Philipp

1
@Frerich: OJ'nin cevabında bahsedilen Birlik yapılarını tanımlıyorsunuz. Onları toplu yapı ve usta yapı olarak da gördüm.
idbrii

Peki bir UB, WPO / LTCG ile nasıl karşılaştırılır?
paulm

Bu, yalnızca bir kerelik derlemeler için yararlıdır, düzenleme, oluşturma ve test etme arasında geçiş yaptığınız geliştirme sırasında değil. Modern dünyada dört çekirdek normdur, belki birkaç yıl sonra çekirdek sayısı önemli ölçüde daha fazladır. Derleyici ve bağlayıcı birden fazla iş parçacığı kullanamıyorsa, dosya listesi belki de uygun bir tamsayı olan (sistem belleğine ve makinenin nasıl kullanıldığına bağlı olarak) <core-count> + Nparalel olarak derlenen alt listelere bölünebilir N.
FooF

15

Bu konuda Büyük Ölçekli C ++ Yazılım Tasarımı (John Lakos tarafından yazılmıştır) başlıklı bir kitap var .

Kitap ön tarihleri ​​şablonlar, bu nedenle kitabın içeriği "şablonlar kullanarak da derleyici yavaşlatabilir" ekleyin.


Kitap genellikle bu tür konularda ifade edilir, ancak benim için bilgi seyrekti. Temel olarak ileri bildirimleri olabildiğince kullanmak ve bağımlılıkları çözmek. Bu, pimpl deyimini kullanmanın çalışma zamanı dezavantajlarına sahip olduğunu açıkça gösteriyor.
gast128

@ gast128 Amacı, artımlı yeniden derlemeye izin veren kodlama deyimlerini kullanmak, yani bir yerde biraz kaynak değiştirirseniz, her şeyi yeniden derlemek zorunda kalmayacağınızı düşünüyorum.
ChrisW

15

Ben sadece benim diğer cevap bağlantı olacak: Nasıl derleme süresi ve Visual C ++ projeleri (yerel C ++) için bağlantı süresini azaltırsınız? . Eklemek istediğim, ancak sıklıkla sorunlara neden olan başka bir nokta, önceden derlenmiş başlıklar kullanmaktır. Ancak, lütfen bunları neredeyse hiç değişmeyen parçalar için kullanın (GUI araç seti başlıkları gibi). Aksi takdirde, sonunda sizi kurtardıklarından daha fazla zamana mal olurlar.

Başka bir seçenek, GNU make ile çalışırken -j<N>seçeneği açmaktır:

  -j [N], --jobs[=N]          Allow N jobs at once; infinite jobs with no arg.

3Burada çift çekirdekli olduğumdan beri genellikle var . Daha sonra, aralarında bağımlılık olmaması kaydıyla, farklı çeviri birimleri için derleyicileri paralel olarak çalıştıracaktır. Tüm nesne dosyalarını birbirine bağlayan tek bir bağlayıcı işlemi olduğundan bağlantı paralel olarak yapılamaz.

Ancak bağlayıcının kendisi iş parçacığı olabilir ve ELF bağlayıcının yaptığı budur. ELF nesne dosyalarını eskisinden daha hızlı bir büyüklükte bağladığı söylenen dişli C ++ kodunu optimize etti (ve aslında binutils'e dahil edildi ).GNU gold ld


Tamam evet. Üzgünüm bu soru aradığımda ortaya çıkmadı.
Scott Langham

üzgün olmana gerek yoktu. Visual C ++ içindi. sorunuz herhangi bir derleyici için gibi görünüyor. yani bu iyi :)
Johannes Schaub - litb

12

İşte bazıları:

  • Çok işlemli bir iş başlatarak tüm işlemci çekirdeklerini kullanın ( make -j2iyi bir örnektir).
  • Kapatın veya optimizasyonları düşürmek (örneğin, GCC çok daha hızlı ile -O1daha -O2ya -O3).
  • Önceden derlenmiş başlıklar kullanın .

12
Bilginize, çekirdeklerden daha fazla işlem başlatmanın genellikle daha hızlı olduğunu düşünüyorum. Örneğin dört çekirdekli sistemde tipik olarak -j4 değil -j8 kullanıyorum. Bunun nedeni, bir işlem G / Ç'de engellendiğinde diğerinin derlenebilmesidir.
Bay Fooz

@MrFooz: Bunu birkaç yıl önce bir i7-2700k (4 çekirdek, 8 iş parçacığı, sabit bir çarpan ayarladım) üzerinde Linux çekirdeğini (RAM depolama biriminden) derleyerek test ettim. Kesinlikle en iyi sonucu unuttum, ancak önerdiğiniz gibi, -j12etrafa -j18göre oldukça hızlıydı -j8. Bellek bant genişliği sınırlayıcı faktör haline gelmeden önce kaç tane çekirdeğiniz olabileceğini merak ediyorum ...
Mark K Cowan

@MarkKCowan birçok faktöre bağlıdır. Farklı bilgisayarların çok farklı bellek bant genişlikleri vardır. Bugünlerde üst düzey işlemcilerle, bellek veriyolunu doyurmak için birden fazla çekirdek gerekiyor. Ayrıca, I / O ve CPU arasında bir denge vardır. Bazı kodların derlenmesi çok kolaydır, diğer kodlar yavaş olabilir (örneğin birçok şablonda). Mevcut -jkuralım gerçek çekirdek sayısının 2 katı.
Bay Fooz

11

Yukarıdaki tüm kod numaralarını uyguladıktan sonra (ileri bildirimler, genel başlıklardaki üstbilginin dahil edilmesini en aza indirgemek, Pimpl ile uygulama dosyasının içindeki çoğu ayrıntıyı zorlamak ...) ve başka hiçbir şey dil açısından elde edilemez, derleme sisteminizi düşünün . Linux kullanıyorsanız, distcc (dağıtılmış derleyici) ve ccache (önbellek derleyici) kullanmayı düşünün .

İlki distcc, önişlemci adımını yerel olarak yürütür ve ardından çıktıyı ağdaki ilk kullanılabilir derleyiciye gönderir. Ağdaki tüm yapılandırılmış düğümlerde aynı derleyici ve kitaplık sürümlerini gerektirir.

İkincisi, ccache, bir derleyici önbelleğidir. Önişlemciyi yeniden yürütür ve ardından önişlemci dosyasının aynı derleyici parametreleriyle önceden derlenmiş olup olmadığını bir iç veritabanı (yerel dizinde tutulur) ile kontrol edin. Eğer öyleyse, derleyicinin ilk çalışmasından ikili ve çıktıyı açar.

Her ikisi de aynı anda kullanılabilir, böylece ccache'nin yerel bir kopyası yoksa, ağı distcc ile başka bir düğüme gönderebilir veya başka bir işlem yapmadan çözümü enjekte edebilir.


2
Distcc'nin tüm yapılandırılmış düğümlerde aynı kütüphane sürümlerini gerektirdiğini düşünmüyorum . distcc derleme sadece uzaktan yapar, bağlantı değil. Ayrıca önceden işlenmiş kodu tel üzerinden gönderir , böylece uzak sistemde bulunan başlıkların önemi yoktur.
Frerich Raabe

9

Üniversiteden çıktığımda gördüğüm ilk gerçek prodüksiyona değer C ++ kodu bu başlıklar #ifndef ... #endif yönergelerine sahipti. Bu kapsayıcı şeyler hakkındaki kodu çok naif bir tarzda yazan adama sordum ve büyük ölçekli programlama dünyasına tanıtıldım.

Konuya geri dönersek, yinelenen başlık tanımlarını önlemek için yönergeler kullanmak, derleme sürelerini azaltma konusunda öğrendiğim ilk şeydi.


1
eski ama altın. bazen bariz unutulur.
alcor

1
'muhafızları
ekle

8

Daha fazla RAM.

Birisi başka bir cevapta RAM sürücüleri hakkında konuştu. Bunu bir 80286 ve Turbo C ++ (yaş gösteriyor) ile yaptım ve sonuçlar olağanüstü. Makine çöktüğünde veri kaybı gibi.


DOS'ta çok fazla belleğe sahip
olamazsınız

6

Mümkün olduğunda ileri bildirimleri kullanın. Bir sınıf bildirimi yalnızca bir işaretçiyi veya bir türe başvuruyu kullanıyorsa, yalnızca iletiyi bildirebilir ve türün üstbilgisini uygulama dosyasına ekleyebilirsiniz.

Örneğin:

// T.h
class Class2; // Forward declaration

class T {
public:
    void doSomething(Class2 &c2);
private:
    Class2 *m_Class2Ptr;
};

// T.cpp
#include "Class2.h"
void Class2::doSomething(Class2 &c2) {
    // Whatever you want here
}

Daha az içerir, yeterince yaparsanız önişlemci için çok daha az iş demektir.


Bu aynı başlık birden fazla çeviri birimine dahil edildiğinde önemli değil mi? Yalnızca bir çeviri birimi varsa (şablonlar kullanıldığında olduğu gibi), bunun bir etkisi olmayacaktır.
AlwaysLearning

1
Sadece bir çeviri birimi varsa, neden bir başlığa koymaya zahmet ediyorsunuz? İçeriği sadece kaynak dosyaya koymak daha anlamlı olmaz mıydı? Başlıkların tamamı, birden fazla kaynak dosyası tarafından eklenmesinin olası olmadığı değil mi?
Evan Teran


5

kullanım

#pragma once

başlık dosyalarının üst kısmında olduğundan, bir çeviri birimine birden çok kez eklendiyse, başlık metni yalnızca bir kez dahil edilir ve ayrıştırılır.


2
Her ne kadar yaygın olarak desteklense de #pragma bir kez standart değildir. Bkz en.wikipedia.org/wiki/Pragma_once
ChrisInEdmonton

7
Ve bu günlerde, düzenli dahil korumaları aynı etkiye sahiptir. Dosyanın en üstünde oldukları sürece, derleyici bunları bir kez #pragma olarak ele alma yeteneğine sahiptir
jalf


4
  • Bilgisayarınızı yükseltin

    1. Dört çekirdekli (veya çift dörtlü sistem) edinin
    2. Çok fazla RAM alın.
    3. Dosya G / Ç gecikmelerini büyük ölçüde azaltmak için bir RAM sürücüsü kullanın. (IDE ve SATA RAM sürücüleri yapan ve sabit sürücüler gibi davranan şirketler vardır).
  • Sonra diğer tüm tipik önerileriniz var

    1. Varsa önceden derlenmiş başlıkları kullanın.
    2. Projenizin bölümleri arasındaki bağlantı miktarını azaltın. Bir başlık dosyasını değiştirmek genellikle tüm projenizin yeniden derlenmesini gerektirmez.

4

RAM sürücüsü kullanma hakkında bir fikrim vardı . Projelerim için sonuçta o kadar fazla fark yaratmadığı ortaya çıktı. Ama sonra yine de oldukça küçükler. Dene! Ne kadar yardımcı olduğunu duymak isterim.


Huh. Neden birisi bunu aşağı oyladı? Yarın deneyeceğim.
Scott Langham

1
Beklenmedik bir düşüş olmasını bekliyorum çünkü asla büyük bir fark yaratmıyor. Yeterli kullanılmamış RAM'iniz varsa, işletim sistemi bunu akıllıca bir disk önbelleği olarak kullanır.
MSalters

1
@MSalters - ve ne kadar "yeterli" olur? O teori, ama RAMDRIVE kullanarak nedense biliyor mu aslında ciddi oranda artıracağını. Git şekil ...
Vilx-

1
projenizi derlemek ve yine de girdi ve geçici dosyaları önbelleğe almak için yeterli. Açıkçası GB'deki taraf doğrudan proje boyutunuza bağlı olacaktır. Daha eski işletim sistemlerinde (özellikle WinXP) dosya önbelleklerinin oldukça tembel olduğu ve RAM'in kullanılmadığı unutulmamalıdır.
MSalters

Şüphesiz, eğer bir sürü yavaş IO yapmak yerine zaten ramdaysa, ram sürücüsü daha hızlıdır, o zaman ram'dalar mı? (değişen dosyalar için yükselme tekrarı - bunları diske geri yazın vb.).
paulm

3

Dinamik bağlantı (.so), statik bağlantıdan (.a) çok daha hızlı olabilir. Özellikle yavaş bir ağ sürücünüz olduğunda. Bunun nedeni, .a dosyasında işlenmesi ve yazılması gereken kodun tümüne sahip olmanızdır. Ayrıca, diske çok daha büyük bir yürütülebilir dosya yazılması gerekir.


dinamik bağlantı birçok bağlantı zamanı optimizasyonunu önler, bu nedenle çıktı birçok durumda daha yavaş olabilir
phuclv

3

Derleme zamanı hakkında değil, derleme zamanı hakkında:

  • Yapı dosyalarınız üzerinde çalışırken aynı dosyaları yeniden oluşturmanız gerekirse ccache kullanın

  • Yapmak yerine ninja-build kullanın. Şu anda bir proje ~ 100 kaynak dosyaları ile derliyorum ve her şey ccache tarafından önbelleğe alınır. 5 dakika, ninja 1'den az gerekir.

Ninja dosyalarınızı cmake ile oluşturabilirsiniz -GNinja.


3

Zamanını nerede geçiriyorsun? CPU bağlı mısınız? Bellek bağlı mı? Disk bağlı mı? Daha fazla çekirdek kullanabilir misiniz? Daha fazla RAM? RAID'e ihtiyacınız var mı? Mevcut sisteminizin verimliliğini artırmak mı istiyorsunuz?

Gcc / g ++ altında, baktı var ccache ? Çok make clean; makeşey yapıyorsanız yardımcı olabilir .


2

Daha hızlı sabit diskler.

Derleyiciler diske birçok (ve büyük olasılıkla) dosya yazar. Tipik sabit disk yerine SSD ile çalışma ve derleme süreleri çok daha düşüktür.



2

Arama gecikmesi yüksek olduğundan, ağ paylaşımları yapınızı büyük ölçüde yavaşlatır. Boost gibi bir şey için, ağ paylaşım diskimiz oldukça hızlı olmasına rağmen benim için büyük bir fark yarattı. Bir oyuncak derleme zamanı Bir ağ paylaşımından yerel bir SSD'ye geçtiğimde yaklaşık 1 dakikadan 1 saniyeye çıktı.


2

Çok çekirdekli bir işlemciniz varsa, hem Visual Studio (2005 ve üstü) hem de GCC çok işlemcili derlemeleri destekler. Kesinlikle donanıma sahipseniz etkinleştirmeniz gereken bir şeydir.


2
@Fellman, Diğer cevaplardan bazılarına bakın - -j # seçeneğini kullanın.
strager

1

Bir "teknik" olmasa da, birçok kaynak dosyasıyla Win32 projelerinin "Merhaba Dünya" boş projemden daha hızlı nasıl derlendiğini anlayamadım. Umarım bu benim yaptığım gibi birine yardımcı olur.

Visual Studio'da, derleme sürelerini artırmak için bir seçenek Artımlı Bağlama'dır ( / INCREMENTAL ). Link-time Code Generation ( / LTCG ) ile uyumsuz olduğundan, sürüm derlemeleri yaparken artımlı bağlantıyı devre dışı bırakmayı unutmayın.


1
Link-time Code Generation'ı devre dışı bırakmak iyi bir öneri değildir çünkü birçok optimizasyonu devre dışı bırakır. Yalnızca /INCREMENTALhata ayıklama modunda etkinleştirmeniz gerekir
phuclv

1

Visual Studio 2017'den başlayarak, neyin zaman alacağı hakkında derleyici metriklerine sahip olma olanağına sahipsiniz.

Bu özellikleri proje özellikleri penceresindeki C / C ++ -> Komut satırına (Ek Seçenekler) ekleyin: /Bt+ /d2cgsummary /d1reportTime

Daha bilgi olabilir bu yazı .


0

Statik yerine dinamik bağlantı kullanmak derleyiciyi daha hızlı hissetmenizi sağlar.

T Cmake kullanıyorsanız özelliği etkinleştirin:

set(BUILD_SHARED_LIBS ON)

Build Release, statik bağlantı kullanarak daha optimize alabilirsiniz.

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.