35

私はPython用のC++拡張機能をプログラミングしており、distutilsを使用してプロジェクトをコンパイルしています。プロジェクトが成長するにつれて、プロジェクトの再構築にはますます時間がかかります。ビルドプロセスをスピードアップする方法はありますか?

distutilsでは(のように)並列ビルドmake -jは不可能であることを読みました。より高速なdistutilsの良い代替手段はありますか?

python setup.py buildまた、ソースファイルを1つだけ変更した場合でも、呼び出すたびにすべてのオブジェクトファイルが再コンパイルされることに気付きました。これが当てはまるのでしょうか、それとも私がここで何か間違ったことをしているのでしょうか?

それが役立つ場合は、ここに私がコンパイルしようとしているファイルのいくつかがあります:https ://gist.github.com/2923577

ありがとう!

4

4 に答える 4

39
  1. CC="ccache gcc"ソースが変更されていない場合、ビルドを大幅に高速化する環境変数を使用してビルドしてみてください。(奇妙なことに、distutils はCCC++ ソース ファイルにも使用します)。もちろん、ccache パッケージをインストールします。

  2. 複数のコンパイル済みオブジェクト ファイルからアセンブルされた単一の拡張機能があるため、distutils にモンキー パッチを適用して、それらを並行してコンパイルできます (これらは独立しています)。これを setup.py に入れます (必要に応じて調整します)。N=2

    # monkey-patch for parallel compilation
    def parallelCCompile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None):
        # those lines are copied from distutils.ccompiler.CCompiler directly
        macros, objects, extra_postargs, pp_opts, build = self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs)
        cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
        # parallel code
        N=2 # number of parallel compilations
        import multiprocessing.pool
        def _single_compile(obj):
            try: src, ext = build[obj]
            except KeyError: return
            self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
        # convert to list, imap is evaluated on-demand
        list(multiprocessing.pool.ThreadPool(N).imap(_single_compile,objects))
        return objects
    import distutils.ccompiler
    distutils.ccompiler.CCompiler.compile=parallelCCompile
    
  3. 完全を期すために、複数の拡張機能がある場合は、次のソリューションを使用できます。

    import os
    import multiprocessing
    try:
        from concurrent.futures import ThreadPoolExecutor as Pool
    except ImportError:
        from multiprocessing.pool import ThreadPool as LegacyPool
    
        # To ensure the with statement works. Required for some older 2.7.x releases
        class Pool(LegacyPool):
            def __enter__(self):
                return self
    
            def __exit__(self, *args):
                self.close()
                self.join()
    
    def build_extensions(self):
        """Function to monkey-patch
        distutils.command.build_ext.build_ext.build_extensions
    
        """
        self.check_extensions_list(self.extensions)
    
        try:
            num_jobs = os.cpu_count()
        except AttributeError:
            num_jobs = multiprocessing.cpu_count()
    
        with Pool(num_jobs) as pool:
            pool.map(self.build_extension, self.extensions)
    
    def compile(
        self, sources, output_dir=None, macros=None, include_dirs=None,
        debug=0, extra_preargs=None, extra_postargs=None, depends=None,
    ):
        """Function to monkey-patch distutils.ccompiler.CCompiler"""
        macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
            output_dir, macros, include_dirs, sources, depends, extra_postargs
        )
        cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
    
        for obj in objects:
            try:
                src, ext = build[obj]
            except KeyError:
                continue
            self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
    
        # Return *all* object filenames, not just the ones we just built.
        return objects
    
    
    from distutils.ccompiler import CCompiler
    from distutils.command.build_ext import build_ext
    build_ext.build_extensions = build_extensions
    CCompiler.compile = compile
    
于 2012-11-01T12:11:31.980 に答える
1

リンクで提供した限られた例では、言語の機能のいくつかについて誤解していることは明らかです。たとえば、 にgsminterface.hは多くの名前空間 level がありますstaticが、これはおそらく意図的ではありません。そのヘッダーを含むすべての翻訳単位は、そのヘッダーで宣言されたすべてのシンボルに対して独自のバージョンをコンパイルします。これの副作用は、コンパイル時間だけでなく、リンカーがこれらすべてのシンボルを処理する必要があるため、コードの肥大化 (より大きなバイナリ) とリンク時間です。

再コンパイルする前に毎回クリーンアップするかどうかなど、まだ答えられていないビルド プロセスに影響する多くの質問があります。これは、ビルド プロセスの結果をキャッシュccacheするツールであり、変更されていない翻訳単位に対してのみプリプロセッサが実行されるようにします。ヘッダー内のほとんどのコードを維持し続ける限り、ヘッダーの変更はそれを含むすべての翻訳単位を変更するため、これはあまり利点を提供しないことに注意してください。(あなたのビルドシステムがわからないので、きれいになるかどうかはわかりません)make clean; make targetpython setup.py build

それ以外の場合、プロジェクトはそれほど大きくないように見えるので、コンパイルに数秒以上かかったとしたら驚くでしょう。

于 2012-06-13T12:02:04.170 に答える