大きなコードベースの(コンパイル)の問題に対処するにはどうすればよいですか?


10

コーディングはできますが、大規模なプロジェクトでの作業経験はまだありません。私がこれまでに行ったことは、数秒でコンパイルされる小さなプログラム(アルゴリズム、プログラミングの原則、アイデア、パラダイムなどのさまざまなc / c ++の演習、または単にapiを試す...)をコーディングするか、コンパイルが不要なスクリプト言語(python、php、js)で作成。

問題は、スクリプト言語でコーディングするとき、何かがうまくいったかどうかを試したいときはいつでも、スクリプトを実行して何が起こるかを確認することです。うまくいかない場合は、コードを変更し、スクリプトを再度実行してもう一度試すことができます。必要な結果が得られるまでそれを続けます。私のポイントは、待つ必要がないということです。コンパイルするものは何でも、そのため、大きなコードベースを取得、変更、何かを追加、または単に操作するのは非常に簡単です。変更を即座に確認できます。

例として、Wordpressを取り上げます。プラグインを作成する方法を理解するのはとても簡単です。最初に単純な「Hello World」プラグインを作成することから始め、次に管理パネルの単純なインターフェースを作成してAPIに慣れ、次にそれを構築してより複雑なものにし、その間にいくつかの外観を変更します回.. WPと同じくらい大きなものを何度も再コンパイルしなければならないという考えは、マイナーな変更を行うたびに、「動作するかどうか」と「動作するかどうか」を試すために、効率が悪く、遅くて間違っているように思えます。

さて、コンパイルされた言語で書かれたプロジェクトでどうすればいいですか?いくつかのオープンソースプロジェクトに貢献したいのですが、この質問は私を悩ませ続けています。状況はおそらくプロジェクトごとに異なり、事前に賢明に考えられていたもののいくつかは何らかの方法で「モジュラー」になる一方で、他のものは何度も再コンパイルする必要がある1つの大きなblobになるだけです。

これが適切に行われる方法について、もっと知りたいのですが。これに対処するためのいくつかの一般的な実践、アプローチ、およびプロジェクト設計(パターン?)は何ですか?この「モジュール性」はプログラマーの世界でどのように呼ばれていますか?これについてもっと知るために何をググる必要がありますか?プロジェクトが最初の思考の比率から大きくなり、しばらくすると面倒になることがよくありますか?うまく設計されていないプロジェクトの長いコンパイルを回避する方法はありますか?どういうわけかそれらをモジュール化する方法(開発中にプログラムの重要でない部分を除外する可能性があります(他のアイデア?))?

ありがとう。


4
Ob。XKCDと関連するthinkgeek Tシャツ * 8 ')
Mark Booth

1
十分な予算のある大規模なプロジェクトで作業している場合は、ビルドサーバーでコンパイルを実行できます:)
SoylentGray

@チャド-私はそれを知っていますが、それは私の家のgnu / linuxデスクトップマシンであり、現時点では私です:)
pootzko

@チャドOK、それであなたは私たちがJava(または他のコンパイルされた言語)のバルクを処理するために専用サーバーが必要だと言っているのですか?それは全体のがらくたです
Kolob Canyon

1
@KolobCanyon-いいえ、私はあなたがそれらを必要とするであろうあなたが働くことができるスケールがあると言っています。また、テストの高速コンパイルと自動化に専用のオンデマンドVMを用意するのは簡単で、規模がそれほど大きくないため、十分に安価です。
SoylentGray

回答:


8

言われたように、小さな変更を加えるたびにプロジェクト全体を再コンパイルすることはありません。代わりに、変更されたコードの一部と、それに依存するすべてのコードのみを再コンパイルします

C / C ++では、コンパイルは非常に簡単です。あなたは、コンパイルマシンコードに各ソースファイルを変換して、あなたは(私たちは、彼らがファイル* .oのオブジェクトを呼び出す)リンク一つの大きな実行可能ファイルにすべてのオブジェクトファイルを。

MainMaで述べたように、一部のライブラリは個別のファイルに組み込まれており、実行時に実行可能ファイルと動的にリンクされます。これらのライブラリは、Unixでは共有オブジェクト(* .so)と呼ばれ、Windowsではダイナミックリンクライブラリ(DLL)と呼ばれます。ダイナミックライブラリには多くの利点があります。その1つは、ソースコードが効果的に変更されない限り、コンパイル/リンクする必要がないことです。

以下を支援するビルド自動化ツールがあります。

  • ソースツリーの異なる部分間の依存関係を指定します。
  • 変更された部分でのみ、時間厳守で慎重な編集を開始します。

最も有名なもの(make、ant、maven、...)は、最後のコンパイル以降にコードのどの部分が変更されたか、そして正確にどのオブジェクト/バイナリを更新する必要があるかを自動的に検出できます。

ただし、これには「ビルドスクリプト」を作成するための(比較的小さな)コストが伴います。これは、ターゲットとその依存関係の定義、必要なコンパイラーと使用するオプションの定義、ビルド環境の定義、ライブラリパスなど、ビルドに関するすべての情報を含むファイルです。Makefile( Unixの世界では一般的です)、またはbuild.xml(Javaの世界では非常に人気があります)。これは彼らがすることです。


2
Ant(Java)は、再コンパイルが必要なものを判別できません。変更されたソースコードを再コンパイルして、ジョブの些細な部分を処理しますが、クラスの依存関係をまったく理解しません。私たちはそれをIDEに依存しており、メソッドのシグネチャが呼び出しコードの変更を必要としない方法で変更された場合、それらは失敗します。
ケビンクライン

@kevincline私は2番目にこれをbuild.xml実行します
Kolob Canyon

7

プロジェクト全体を毎回再コンパイルする必要はありません。たとえば、C / C ++アプリケーションの場合、ライブラリ(WindowsではDLL)に分離され、すべてのライブラリが個別にコンパイルされる可能性があります。

プロジェクト自体は通常、専用サーバーで毎日コンパイルされます。これらは毎晩のビルドです。このプロセスには、コンパイル時間だけでなく、単体テスト、他のテスト、および他のプロセスの実行に費やされた時間も含まれるため、長い時間がかかる可能性があります。


3
それをすべて再コンパイルしない場合、トレビュシェットで
SoylentGray

5

これまでのすべての答えも暗に言っていると思いますが、大規模なソフトウェアプロジェクトはほとんどの場合、はるかに小さな部分に分割されます。各部分は通常、独自のファイルに格納されます。

これらのピースは、オブジェクトを作成するために個別にコンパイルされます。次に、オブジェクトをリンクして、最終製品を形成します。[ある意味で、それはレゴから物を建てるようなものです。1つの大きなプラスチック片から最終的なものを成形しようとせず、代わりに小さな束を組み合わせて作ります。]

プロジェクトを個別にコンパイルされる部分に分割すると、いくつかのきちんとしたことが起こります。

インクリメンタルビルディング

まず、1つの部分を変更する場合、通常、すべての部分を再コンパイルする必要はありません。一般的に言えば、他の部分が自分の部分と相互作用する方法を変更しない限り、他の部分を再コンパイルする必要はありません。

これにより、増分ビルドのアイデアが生まれます。インクリメンタルビルドを実行すると、変更の影響を受けた部分のみが再コンパイルされます。これにより、開発時間が大幅に短縮されます。確かに、すべてが再リンクされるのを待つ必要があるかもしれませんが、それでも、すべてを再コンパイルして再リンクする必要がある場合よりも節約できます。(ところで、一部のシステム/言語はインクリメンタルリンクをサポートしているので、変更されたものだけを再リンクする必要があります。これにかかるコストは通常​​、コードのパフォーマンスとサイズの低下です。)

ユニットテスト

小さなピースを使用して2番目にできることは、ピースを組み合わせるに、ピースを個別にテストすることです。これは単体テストとして知られています。ユニットテストでは、各ユニットは、システムの他の部分と統合(結合)される前に個別にテストされます。単体テストは通常​​、システムの残りの部分を含まずにすばやく実行できるように記述されています。

テストの適用の限定的なケースは、テスト駆動開発(TDD)に見られます。この開発モデルでは、失敗したテストを修正する場合を除いて、コードの記述や変更は行われません。

簡単にする

したがって、物事を分解することは良いように思えますが、プロジェクトを構築するには多くの作業が必要であるようにも見えます。変更された部分とそれらに依存する部分を理解し、各部分をコンパイルして、すべてをリンクする必要があります。

幸いなことに、プログラマーは怠惰な*ため、仕事を簡単にするための多くのツールを発明しています。そのため、上記のタスクを自動化するために多くのツールが作成されています。これらの中で最も有名なものはすでに言及されています(make、ant、maven)。これらのツールを使用すると、最終プロジェクトを作成するためにどの部分を組み合わせる必要があるか、および部分が相互にどのように依存するかを定義できます(つまり、これを変更した場合、再コンパイルする必要があります)。その結果、1つのコマンドを発行するだけで、何を再コンパイルする必要があるかがわかり、それをコンパイルして、すべてを再リンクします。

しかし、それでも物事が相互にどのように関係しているかを理解することはまだ残っています。これは大変な作業であり、前に述べたように、プログラマーは怠惰です。したがって、彼らは別のクラスのツールを考え出しました。これらのツールは、依存関係を判断するために作成されています。多くの場合、ツールはEclipseやVisual Studioのような統合開発環境(IDE)の一部ですが、一般的なアプリケーションと特定のアプリケーション(makedep、QMake for Qtプログラム)の両方に使用されるスタンドアロンツールもあります。

*実際、プログラマーは本当に怠惰ではなく、プログラムで自動化できる反復的なタスクを実行せずに、問題に時間を費やしたいだけです。


5

C / C ++ビルドを高速化するために試すことができるもののリストは次のとおりです。

  • 変更されたものだけを再構築するように設定していますか?ほとんどの環境では、これがデフォルトで行われます。ファイルが変更されている場合、またはヘッダーが変更されていない場合は、ファイルを再コンパイルする必要はありません。同様に、objs / libでリンクされているすべてのものが変更されていない場合、dll / exeを再構築する理由はありません。
  • 変更されないサードパーティのものと関連するヘッダーを一部の読み取り専用コードライブラリ領域に配置します。ヘッダーと関連するバイナリのみが必要です。あなたは必要があります決して多分一度以外のソースからこれを再構築する必要がありません。
  • すべてを再構築するとき、私の経験における2つの制限要因は、コアの数ディスク速度でした。非常に優れたHDDを備えた強力なクアッドコアハイパースレッドマシンを入手すると、パフォーマンスが向上します。ソリッドステートドライブを検討してください。安価なドライブは、優れたHDDよりも悪い場合があることに注意してください。RAIDを使用してHDDを増やすことを検討してください
  • ネットワーク上の他のワークステーション間でコンパイルを分割するIncredibuildなどの分散ビルドシステムを使用します。(ネットワークがしっかりしていることを確認してください)。
  • Unityビルドをセットアップして、ヘッダーファイルを絶えずリロードしないようにします。

私の経験では(あまり多くはありませんが)、プロジェクトが「非常に小さく」なると、ディスク速度は無関係になり始めます。次の箇条書きで言うことを考えてください。コンパイルを高速化するためにネットワークを使用しています。ディスクが大きなボトルネックだった場合、ネットワークに頼ることはあまり良い方法ではないようです。
R.マルティーニョフェルナンデス

もう1つの安価なソリューションは、tmpfsでコンパイルすることです。コンパイルプロセスがIOバウンドの場合、パフォーマンスを大幅に向上させることができます。
Artefact2

4

WPと同じくらい大きなものを何度も再コンパイルしなければならないという考えは、「動作するかどうか」と「動作/感じ方」を試すためにマイナーな変更を加えるたびに、非効率的で遅くて間違っているように思えます。

解釈された何かを実行することも非常に非効率的で遅く、そして(間違いなく)間違っています。あなたは開発者のPCの時間要件について不満を言っていますが、コンパイルしないとユーザーの PCの時間要件が発生します

さらに重要なことに、最新のシステムは非常に高度な増分再構築を実行でき、マイナーな変更のために全体を再コンパイルすることは一般的ではありません。コンパイルされたシステムには、特にUIなどに共通のスクリプトコンポーネントが含まれる場合があります。


1
私の質問は解釈されたアプローチの議論とコンパイルされたアプローチの議論の両方を意味するものではなかったと思います。代わりに、大規模な(コンパイルされた)プロジェクトの開発が適切に行われる方法についてアドバイスを求めました。ただし、増分再構築のアイデアをありがとう。
pootzko 2011

@pootzko:まあ、通訳のマイナス面について話していないときにコンパイルのマイナス面について話し合うのはかなり不公平です。
DeadMG 2009

1
いいえ、ちがいます。それは別の議論であり、私の質問とは何の関係もありません。議論すべきではないということではありません。すべきですが、ここではありません。
pootzko 2011

@pootzko:次に、コンパイルの嫌いなものを列挙するために質問の大部分を捧げるべきではありません。「大規模なプロジェクトのコンパイル時間をどのようにして減らすことができるのか」のように、もっと短くて簡潔なものを書くべきでした。
DeadMG 2009

どのように自分の質問を「すべきか」について誰かに尋ねなければならないことを知りませんでした。:OIは、自分の見解をよりよく説明するために書いたように書いたので、他の人がそれをよりよく理解し、コンパイルされた言語で同じ/類似のことを達成する方法を説明できました。私は再び-しませんでした-解釈された言語がユーザーのPCの時間要件を悪化させるかどうかを誰かに教えてもらいます。私はそれを知っています、そしてそれは私の質問とは何の関係もありません-「コンパイルされた言語でそれはどのように行われるのですか」、申し訳ありません。他の人々は、私は私の質問が明確では十分ではありませんとは思わないので、私は..かかわら尋ねなかったものを考え出しているように見える
pootzko

4
  • 部分的な再構築

プロジェクトが適切なコンパイル依存関係DAGを実装している場合、変更が影響するオブジェクトファイルのみを再コンパイルすることで問題を回避できます。

  • 複数のコンパイルプロセス

また、適切なコンパイル依存DAGを前提として、複数のプロセスを使用してコンパイルできます。コア/ CPUごとに1つのジョブが標準です。

  • 実行可能なテスト

特定のオブジェクトファイルのみをリンクするテスト用の複数の実行可能ファイルを作成できます。


2

MainMaの回答に加えて、作業中のマシンもアップグレードしました。私たちが購入した最高の購入の1つは、プロジェクト全体を再コンパイルせざるを得ないSSDでした。

別の提案は、別のコンパイラを試すことです。昔は、JavaコンパイラーからJikesに切り替えました。次に、マルチコアプロセッサーの利点を活かして、Eclipseにバンドルされているコンパイラー(名前があるかどうかはわかりません)の使用に移ります。

37,000ファイルのプロジェクトでは、これらの変更を行う前に、ゼロからコンパイルするのに約15分かかりました。変更後、2〜3分に短縮されました。

もちろん、MainMaのポイントに再度言及する価値はあります。変更を確認するたびにプロジェクト全体を再コンパイルしないでください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.