13

可能であれば、CPU 固有の機能を使用する Python 拡張機能があります。これは、実行時チェックによって行われます。ハードウェアが命令をサポートしている場合はPOPCNT、内部ループの 1 つの実装を選択し、SSSE3 が利用可能な場合は別の実装を選択し、それ以外の場合はパフォーマンス クリティカルなカーネルの汎用バージョンにフォールバックします。(約 95% 以上の時間がこのカーネルで費やされます。)

残念ながら、私が予期していなかった失敗モードがあります。-mssse3andを使用し-O3てすべての C コードをコンパイルしますが、その-mssse3オプションが必要なファイルは 1 つだけです。その結果、他のファイルは、SSSE3 が存在することを期待してコンパイルされます。これにより、次の行でセグメンテーション違反が発生します。

start_target_popcount = (int)(query_popcount * threshold);

コンパイラfisttplが SSSE3 命令である を使用したためです。結局、SSSE3が存在すると仮定するように言いました。

最近、私のパッケージの Debian パッケージャーがこの問題に遭遇しました。これは、テスト マシンには GCC を理解-mssse3してコードを生成する GCC があるためですが、マシン自体には古い CPU があり、それらの指示がありません。

Debian メンテナーがそのディストリビューションに使用できる、古いマシンと新しいマシンで同じバイナリが機能するソリューションが必要です。

理想的には、-mssse3オプションを使用してコンパイルされるファイルは 1 つだけです。私の CPU 固有のセレクター コードはこのファイルの一部ではないため、CPU がサポートしない限り、SSSE3 コードは実行されません。

distutilsただし、一連のコンパイラ オプションが 1 つのファイルに固有のものであることを確認する方法がわかりません。
それは可能ですか?

4

3 に答える 3

6

A very ugly solution would be to create two (or more Extension) classes, one to hold the SSSE3 code and the other for everything else. You could then tidy the interface up in the python layer.

c_src = [f for f in my_files if f != 'ssse3_file.c']

c_gen = Extension('c_general', sources=c_src,
                 libraries=[], extra_compile_args=['-O3'])

c_ssse3 = Extension('c_ssse_three', sources=['ssse3_file.c'],
                 libraries=[], extra_compile_args=['-O3', '-mssse3'])

and in an __init__.py somewhere

from c_general import *
from c_ssse_three import *

Of course you don't need me to write out that code! And I know this isn't DRY, I look forward to reading a better answer!

于 2013-03-20T15:46:22.707 に答える
3

5年が経ちましたが、「CC」ラッパーよりも気に入ったソリューションを見つけました。

「build_ext」コマンドは、self.compiler インスタンスを作成します。compiler.compile() メソッドは、コンパイルするすべてのソース ファイルのリストを取得します。基本クラスはいくつかのセットアップを行い、実際のファイルごとのコンパイル手順を実装する具体的なコンパイラ サブクラス用の compiler._compile() フックを持ちます。

その時点でコードをインターセプトできるほど安定していると感じました。

distutils.command.build_ext.build_ext から新しいコマンドを派生させました。これは self.compiler._compile を微調整して、バインドされたクラス メソッドをインスタンスにアタッチされた 1 回限りの関数でラップします。

class build_ext_subclass(build_ext):
    def build_extensions(self):

        original__compile = self.compiler._compile
        def new__compile(obj, src, ext, cc_args, extra_postargs, pp_opts):
            if src != "src/popcount_SSSE3.c":
                extra_postargs = [s for s in extra_postargs if s != "-mssse3"]
            return original__compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
        self.compiler._compile = new__compile
        try:
            build_ext.build_extensions(self)
        finally:
            del self.compiler._compile

次に、このコマンド クラスを使用するように setup() に指示しました。

setup(
   ...
   cmdclass = {"build_ext": build_ext_subclass}
)
于 2018-04-11T10:39:58.793 に答える