web-dev-qa-db-ja.com

2 GBを超えるコードでのGCCコンパイルエラー

合計で約2.8 GBのオブジェクトコードの膨大な数の関数があります(残念ながら、回避方法はありません、科学計算...)

それらをリンクしようとすると、(予想)relocation truncated to fit: R_X86_64_32Sエラーが発生します。コンパイラフラグ-mcmodel=mediumを指定することで回避したいと考えています。私が制御している他にリンクされているすべてのライブラリは、-fpicフラグでコンパイルされます。

それでも、エラーは解決せず、リンク先のライブラリの一部はPICでコンパイルされていないと想定しています。

エラーは次のとおりです。

/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o: In function `_start':
(.text+0x12): relocation truncated to fit: R_X86_64_32S against symbol `__libc_csu_fini'     defined in .text section in /usr/lib64/libc_nonshared.a(elf-init.oS)
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o: In function `_start':
(.text+0x19): relocation truncated to fit: R_X86_64_32S against symbol `__libc_csu_init'    defined in .text section in /usr/lib64/libc_nonshared.a(elf-init.oS)
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crti.o: In function    `call_gmon_start':
(.text+0x7): relocation truncated to fit: R_X86_64_GOTPCREL against undefined symbol      `__gmon_start__'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtbegin.o: In function `__do_global_dtors_aux':
crtstuff.c:(.text+0xb): relocation truncated to fit: R_X86_64_PC32 against `.bss' 
crtstuff.c:(.text+0x13): relocation truncated to fit: R_X86_64_32 against symbol `__DTOR_END__' defined in .dtors section in /usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtend.o
crtstuff.c:(.text+0x19): relocation truncated to fit: R_X86_64_32S against `.dtors'
crtstuff.c:(.text+0x28): relocation truncated to fit: R_X86_64_PC32 against `.bss'
crtstuff.c:(.text+0x38): relocation truncated to fit: R_X86_64_PC32 against `.bss'
crtstuff.c:(.text+0x3f): relocation truncated to fit: R_X86_64_32S against `.dtors'
crtstuff.c:(.text+0x46): relocation truncated to fit: R_X86_64_PC32 against `.bss'
crtstuff.c:(.text+0x51): additional relocation overflows omitted from the output
collect2: ld returned 1 exit status
make: *** [testsme] Error 1

そして私がリンクするシステムライブラリ:

-lgfortran -lm -lrt -lpthread

問題を探すための手がかりはありますか?

編集:まず第一に、議論に感謝します...少し明確にするために、私はこのような何百もの関数を持っています(それぞれが別々のオブジェクトファイルで約1MBのサイズです):

double func1(std::tr1::unordered_map<int, double> & csc, 
             std::vector<EvaluationNode::Ptr> & ti, 
             ProcessVars & s)
{
    double sum, prefactor, expr;

    prefactor = +s.ds8*s.ds10*ti[0]->value();
    expr =       ( - 5/243.*(s.x14*s.x15*csc[49300] + 9/10.*s.x14*s.x15*csc[49301] +
           1/10.*s.x14*s.x15*csc[49302] - 3/5.*s.x14*s.x15*csc[49303] -
           27/10.*s.x14*s.x15*csc[49304] + 12/5.*s.x14*s.x15*csc[49305] -
           3/10.*s.x14*s.x15*csc[49306] - 4/5.*s.x14*s.x15*csc[49307] +
           21/10.*s.x14*s.x15*csc[49308] + 1/10.*s.x14*s.x15*csc[49309] -
           s.x14*s.x15*csc[51370] - 9/10.*s.x14*s.x15*csc[51371] -
           1/10.*s.x14*s.x15*csc[51372] + 3/5.*s.x14*s.x15*csc[51373] +
           27/10.*s.x14*s.x15*csc[51374] - 12/5.*s.x14*s.x15*csc[51375] +
           3/10.*s.x14*s.x15*csc[51376] + 4/5.*s.x14*s.x15*csc[51377] -
           21/10.*s.x14*s.x15*csc[51378] - 1/10.*s.x14*s.x15*csc[51379] -
           2*s.x14*s.x15*csc[55100] - 9/5.*s.x14*s.x15*csc[55101] -
           1/5.*s.x14*s.x15*csc[55102] + 6/5.*s.x14*s.x15*csc[55103] +
           27/5.*s.x14*s.x15*csc[55104] - 24/5.*s.x14*s.x15*csc[55105] +
           3/5.*s.x14*s.x15*csc[55106] + 8/5.*s.x14*s.x15*csc[55107] -
           21/5.*s.x14*s.x15*csc[55108] - 1/5.*s.x14*s.x15*csc[55109] -
           2*s.x14*s.x15*csc[55170] - 9/5.*s.x14*s.x15*csc[55171] -
           1/5.*s.x14*s.x15*csc[55172] + 6/5.*s.x14*s.x15*csc[55173] +
           27/5.*s.x14*s.x15*csc[55174] - 24/5.*s.x14*s.x15*csc[55175] +
           // ...
           ;

        sum += prefactor*expr;
    // ...
    return sum;
}

オブジェクトsは比較的小さく、必要な定数x14、x15、...、ds0、...などを保持しますが、tiは外部ライブラリからdoubleを返すだけです。ご覧のとおり、csc[]は事前に計算された値のマップであり、次の形式の別のオブジェクトファイル(それぞれ約1 MBのサイズで数百)で評価されます。

void cscs132(std::tr1::unordered_map<int,double> & csc, ProcessVars & s)
{
    {
    double csc19295 =       + s.ds0*s.ds1*s.ds2 * ( -
           32*s.x12pow2*s.x15*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x15*s.x35*s.x45*s.mWpowinv2 -
           32*s.x12pow2*s.x25*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x25*s.x35*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x25*s.x35*s.x45*s.mWpowinv2 +
           32*s.x12pow2*s.x34*s.mbpow4*s.mWpowinv2 +
           32*s.x12pow2*s.x34*s.x35*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x34*s.x45*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x35*s.mbpow4*s.mWpowinv2 +
           32*s.x12pow2*s.x35pow2*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x35pow2*s.x45*s.mWpowinv2 +
           64*s.x12pow2*s.x35*s.x45*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x35*s.x45pow2*s.mWpowinv2 -
           64*s.x12*s.p1p3*s.x15*s.mbpow4*s.mWpowinv2 +
           64*s.x12*s.p1p3*s.x15pow2*s.mbpow2*s.mWpowinv2 +
           96*s.x12*s.p1p3*s.x15*s.x25*s.mbpow2*s.mWpowinv2 -
           64*s.x12*s.p1p3*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
           64*s.x12*s.p1p3*s.x15*s.x45*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x25*s.mbpow4*s.mWpowinv2 +
           32*s.x12*s.p1p3*s.x25pow2*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x25*s.x35*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x25*s.x45*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x45*s.mbpow2 +
           64*s.x12*s.x14*s.x15pow2*s.x35*s.mWpowinv2 +
           96*s.x12*s.x14*s.x15*s.x25*s.x35*s.mWpowinv2 +
           32*s.x12*s.x14*s.x15*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.x14*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
           64*s.x12*s.x14*s.x15*s.x35pow2*s.mWpowinv2 -
           32*s.x12*s.x14*s.x15*s.x35*s.x45*s.mWpowinv2 +
           32*s.x12*s.x14*s.x25pow2*s.x35*s.mWpowinv2 +
           32*s.x12*s.x14*s.x25*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.x14*s.x25*s.x35pow2*s.mWpowinv2 -
           // ...

       csc.insert(cscMap::value_type(192953, csc19295));
    }

    {
       double csc19296 =      // ... ;

       csc.insert(cscMap::value_type(192956, csc19296));
    }

    // ...
}

それについてです。最後のステップは、これらのすべてのfunc[i]を呼び出して、結果を合計するだけです。

これがかなり特別で珍しいケースであるという事実に関して:はい、そうです。これは、素粒子物理学のために高精度の計算をしようとするときに人々が対処しなければならないものです。

EDIT2:また、x12、x13などは実際には定数ではないことも追加する必要があります。これらは特定の値に設定され、これらのすべての関数が実行されて結果が返され、x12、x13などの新しいセットが選択されて次の値が生成されます。そして、これは10 ^ 5から10 ^ 6回行わなければなりません...

EDIT3:これまでの提案と議論に感謝します...どういうわけか、コード生成時にループをロールアップしようとします。正直に言うと、これを正確に行う方法はわかりませんが、これが最善の策です。

ところで、私は「これは科学計算です-最適化する方法はありません」の後ろに隠れようとしませんでした。このコードの基礎は、私が実際にアクセスできない「ブラックボックス」から出てきたものであり、さらに、単純な例では全体がうまく機能し、実際に何が起こるかに圧倒されていると感じているだけですワールドアプリケーション...

EDIT4:それで、コンピューター代数システムの式を単純化することで、csc定義のコードサイズを約1分の1に減らすことができました( Mathematica )。また、コードを生成する前に他のいくつかのトリックを適用して(この部分を約100MBに縮小する)、さらに1桁程度小さくする方法もあります。このアイデアがうまくいくことを願っています。

あなたの答えに関連して:CASがあまり役に立たないfuncsでループを再びロールバックしようとしていますが、すでにいくつかのアイデアがあります。たとえば、式をx12, x13,...などの変数で並べ替え、cscsをPythonで解析し、それらを相互に関連付けるテーブルを生成します。少なくともこれらの部分をループとして生成できますこれがこれまでのところ最善の解決策であると思われるため、これを最良の回答としてマークします。

ただし、VJoの功績も評価したいと思います。 GCC 4.6は実際に動作しはるかに優れています、より小さなコードを生成し、高速です。ラージモデルの使用は、コードでそのまま機能します。技術的にはこれが正解ですが、コンセプト全体を変更する方がはるかに優れたアプローチです。

あなたの提案と助けてくれてありがとう。誰かが興味を持っているなら、準備ができ次第、最終結果を投稿するつもりです。

備考:他のいくつかの回答に対するいくつかのコメント:私が実行しようとしているコードは、単純な関数/アルゴリズムの拡張と愚かな不必要な展開に由来するものではありません。実際に起こることは、私たちが始めるものはかなり複雑な数学オブジェクトであり、それらを数値計算可能形式にすることでこれらの式が生成されることです。問題は、実際には基礎となる物理理論にあります。中間式の複雑さは階乗的にスケーリングしますが、これはよく知られていますが、これらすべてを物理的に測定可能なもの、つまり観測可能なものに組み合わせると、式の基礎を形成するほんの一握りの非常に小さな関数に要約されます。 (この点に関しては、一般的にonlyが利用可能です ansatz これは「摂動論」と呼ばれます)別のレベルへの仮説。これはもはや分析的に実行可能ではなく、必要な機能の基礎は不明です。そこで、このように総当たり攻撃を試みます。最善の方法ではありませんが、最終的に手元の物理学を理解する助けになることを願っています...

最終編集:すべての提案のおかげで、Mathematicaとfuncsのコードジェネレーターの修正を使用して、コードサイズをかなり小さくすることができました。トップの答えの線に沿ってやや:)

Mathematicaでcsc関数を単純化して、92MBに下げました。これは既約の部分です。最初の試行は永遠にかかりましたが、いくつかの最適化の後、単一のCPUで約10分で実行されます。

funcsへの影響は劇的でした。これらのコード全体のサイズは約9MBにまで減少したため、コードの合計は100MBの範囲になりました。最適化をオンにするのが理にかなっており、実行は非常に高速です。

繰り返しになりますが、あなたの提案に感謝します、私は多くを学びました。

108
bbtrb

そのため、このテキストを生成するプログラムが既にあります。

_prefactor = +s.ds8*s.ds10*ti[0]->value();
expr = ( - 5/243.*(s.x14*s.x15*csc[49300] + 9/10.*s.x14*s.x15*csc[49301] +
       1/10.*s.x14*s.x15*csc[49302] - 3/5.*s.x14*s.x15*csc[49303] -...
_

そして

_double csc19295 =       + s.ds0*s.ds1*s.ds2 * ( -
       32*s.x12pow2*s.x15*s.x34*s.mbpow2*s.mWpowinv2 -
       32*s.x12pow2*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
       32*s.x12pow2*s.x15*s.x35*s.x45*s.mWpowinv2 -...
_

右?

すべての関数が同様の「フォーマット」を持っている場合(nの数をm回乗算して結果を追加する-または同様のもの)、これを行うことができると思います:

  • 文字列の代わりにオフセットを出力するようにジェネレータープログラムを変更します(つまり、文字列 "s.ds0"の代わりにoffsetof(ProcessVars, ds0)を生成します
  • そのようなオフセットの配列を作成します
  • 上記の配列と構造体ポインターのベースアドレスを受け入れ、結果を生成するエバリュエーターを記述します

Array + evaluatorは関数の1つと同じロジックを表しますが、評価されるのはコードのみです。配列は「データ」であり、実行時に生成されるか、ディスクに保存してiチャンクまたはメモリマップファイルで読み取ることができます。

Func1の特定の例では、sおよびcscのベースアドレス、および定数とオフセットの表現のようなベクトルにアクセスできる場合に、エバリュエーターを使用して関数を書き換える方法を想像してください。 _x14_、_ds8_、および_csc[51370]_にアクセスするには、ベースアドレスに追加する必要があります

膨大な数の関数に渡す実際のデータを処理する方法を記述する「データ」の新しい形式を作成する必要があります。

53
Andrei

Linuxで使用されるx86-64 ABI は、GOTおよびPLTの64ビット再配置タイプを含む、このようなサイズ制限を回避するための「ラージモデル」を定義します。 (セクション4.4.2の表と、それらの使用方法を示す3.5.5の命令シーケンスを参照してください。)

関数は2.8 GBを占有しているため、gccは大きなモデルをサポートしていないため、運が悪くなります。できることは、コードを再編成して、動的にリンクする共有ライブラリーに分割できるようにすることです。

それが不可能な場合、誰かが示唆したように、データをコードに入れる(コンパイルしてリンクする)代わりに、それは巨大なので、実行時にロードすることができます(通常のファイルとして、またはmmapすることができます)。

[〜#〜] edit [〜#〜]

ラージモデルはgcc 4.6でサポートされているようです( このページ を参照)。あなたはそれを試すことができますが、上記はコードの再編成についてまだ適用されます。

45
BЈовић

その側のプログラムでは、コードのキャッシュミスが実行時のループのコストを超える可能性が非常に高くなります。コードジェネレーターに戻って、評価したいもの(つまり、Dキャッシュに収まる可能性のあるもの)のコンパクト表現を生成し、インタープリターで実行することをお勧めしますあなたのプログラムで。また、かなりの数の操作が残っている小さなカーネルを除外して、それらを解釈されたコードの「命令」として使用できるかどうかも確認できます。

37
bdonlan

このエラーは、データではなくコードが多すぎるために発生します!これは、たとえば__libc_csu_fini(関数)から参照される_startそして再配置は収まるように切り捨てられます。この意味は _start(プログラムの真のエントリポイント)は、2GBの範囲しかないSIGNED 32ビットオフセットを介してその関数を呼び出そうとしています。オブジェクトコードの合計量は約2.8 GBなので、事実を確認してください。

データ構造を再設計できれば、巨大な式を単純なループとして書き直すことで、コードの多くを「圧縮」できます。

また、csc[]別のプログラムで、結果をファイルに保存し、必要に応じてそれらをロードします。

21
zvrba

あなたがやりたいことをする別の方法があるべきだと誰もが同意すると思います。数百メガバイト(ギガバイト?)のコードをコンパイルし、数ギガバイトサイズの実行可能ファイルにリンクして実行すると、非常に効率が悪いように思えます。

あなたの問題を正しく理解しているなら、あなたはある種のコードジェネレーターGを使用して、入力としてマップfunc1...Nを受け取る関数csc1...Mを生成します。あなたがしたいことは、csc1...Mを計算し、異なる入力に対して1,000,000回のループを実行し、毎回s = func1 + func2 + ... + funcNを見つけることです。ただし、fucn1...Ncsc1...Mの関係を指定しませんでした。

それがすべて当てはまる場合、問題を別の方法で頭の中で回すことができるはずです。これは、はるかに管理しやすく、場合によってはさらに高速になる可能性があります(つまり、マシンのキャッシュを実際に機能させる)。

オブジェクトファイルサイズの実際的な問題に加えて、現在のプログラムは、データへのアクセスをローカライズせず(巨大なマップが多すぎる)、ローカライズされたコード実行がない(非常に長い関数が多すぎる)ため、効率的ではありません。

プログラムを3つのフェーズに分けてみてください:フェーズ1はcsc1...Mをビルドして保存します。フェーズ2は、一度に1つのfuncを構築し、各入力で1,000,000回実行し、結果を保存します。フェーズ3では、1,000,000回の実行ごとに、保存されたfunc1...N結果の結果の合計を見つけます。このソリューションの良いところは、複数の独立したマシン間で簡単に並列化できることです。

編集:@ bbtrb、1つのfuncと1つのcscを利用可能にできますか?それらは非常に規則的で圧縮可能であるようです。たとえば、func1は、それぞれ1つの係数、sの変数への2つのインデックス、およびcscへの1つのインデックスで構成される式の合計のように見えます。したがって、ニースループに減らすことができます。完全な例を使用できるようにすると、長い式ではなくループに圧縮する方法が見つかるはずです。

15
AlefSin

エラーを正しく読んだ場合、制限を超えてしまうのは、初期化されたデータセクションです(コードの場合、はるかに多くのエラーが発生します)。グローバルデータの大きな配列がありますか?その場合、プログラムが動的に割り当てられるようにプログラムを再構築します。データが初期化されている場合、構成ファイルから読み取ります。

ところでこれを見て:

(.text + 0x20): `main 'への未定義の参照

別の問題があると思います。

5
AProgrammer

いくつかの提案:-サイズの最適化(-Os)。インライン関数呼び出し、通常の関数呼び出しを行います。文字列プーリングを有効にします。

異なるDLLに分割してみてください(共有オブジェクト、Linuxの場合は.so、Mac OS Xの場合は.dylib)。それらがアンロードできることを確認してください。次に、必要に応じてロードするものを実装し、不要なときに解放します。

そうでない場合は、コードを異なる実行可能ファイルに分割し、それらの間で通信するために何かを使用します(パイプ、ソケット、さらにはファイルへの書き込み/読み取り)。不器用ですが、どんな選択肢がありますか?

完全に代替:- [〜#〜] jit [〜#〜] で動的言語を使用します。私の頭の真上で LuaJIT を使用し、これらの式の多くを Lua 、またはコードがゴミになる可能性のある他の言語とランタイムで書き換えますか?集めました。

LuaJITは非常に効率的で、特定の目的でC/C++を破る場合もありますが、非常に近い場合があります(ガベージコレクションが不十分なために遅くなる場合があります)。自分で確認してください:

http://luajit.org/performance_x86.html

scimark2.luaファイルをそこから取り出し、「C」バージョンと比較します(グーグルit)-多くの場合、結果は非常に近いです。

3
malkia

コードは、ある種の適応深度法を使用して数値積分を行っているように見えます。残念ながら、コードジェネレーター(またはコードジェネレーターの作成者)はstupidであり、typeパッチ。そのため、コンパイルするには大量のコードが生成され、コンパイルできたとしても、どこにも共有されていないため、実行が苦痛になります。 (オブジェクトコードの各ページをディスクからロードしなければならないことで生じる痛みを想像できますか?そのため、OSが追い出されるのは常に候補です。命令キャッシュは言うまでもありませんが、無駄になります。)

修正は、すべての展開を停止することです。この種のコードでは、共有を最大化する必要があります、より複雑なパターンでデータにアクセスするための余分な命令のオーバーヘッドが取引コストに吸収されるためとにかく(おそらく)基礎となる大規模なデータセットを使用します。また、コードジェネレーターがデフォルトでこれを行うこともあり、科学者は展開のオプションをいくつか見て(これらは速度を向上させることがあることに注意してください)、一度にすべてをオンにして、この結果の混乱を受け入れるように主張している可能性もありますマシンの実際の制限を受け入れ、デフォルトで生成される数値的に正しいバージョンを使用するのではなく、コンピューターによって。ただし、コードジェネレーターがそれを実行しない場合は、それを取得します(または既存のコードをハッキングします)。

要点: 2.8GBのコードのコンパイルとリンクは機能せず、強制的に動作させるべきではありません。 別の方法を探す

3
Donal Fellows

これらの表現は、私にとって交互のシリーズのように見えます。コードの残りの部分がどのように見えるかはわかりませんが、生成式を導き出すのはそれほど難しくないようです。特に、2.8 GBの2dKBの展開されたコードがある場合、実行時にも価値があります。

2
Brian

リンカーは、何らかの制限でこれらの制限を超えたバイナリ内で32ビットの再配置オフセットを生成しようとしています。メインプログラムのアドレススペース要件を減らしてください。

オブジェクトコードの一部または大部分を1つ以上のライブラリに分割できますか(-fpic/-fPICでコンパイルされます)?次に、これらのライブラリに対してリンクする非静的バイナリを生成します。ライブラリは個別のメモリブロックに存在し、再配置オフセットは相対(32ビット)ではなく動的/絶対(64ビット)になります。

2
ajklbahu8geag

これは、おそらくシンボリック代数および/または手動展開によって、コード生成の結果が間違っているように見えます。シンボリック操作は、式ツリーまたは計算グラフの深さで指数関数的に成長することがよく知られています。ここでは自動微分を使用できる可能性があります。これにより、コードサイズが非常に小さくなり、実行速度が劇的に向上します。

1
Jed