なぜ私のやり方がうまくいかないのか
まず、UNIX の共有ライブラリは、アーカイブ ライブラリの動作を模倣するように設計されています (アーカイブ ライブラリは最初にありました)。特に、あなたが 03
を持っている場合 と 14
、両方ともシンボル 29
を定義 の場合、最初にロードされたライブラリが勝ちます:37
へのすべての参照 プログラム内のどこからでも (46
を含む) ) 55
にバインドされます 69
の定義 .
これは、プログラムを 78
に対してリンクした場合にどうなるかを模倣しています および 82
、両方のアーカイブ ライブラリが同じシンボル 97
を定義した場所 .アーカイブ リンクの詳細については、こちらをご覧ください。
上記から明らかなように、101
の場合 と 113
同じ記号のセットを定義する (行う )、および 122
の場合 最初にプロセスにロードされ、次に 134
からのルーチンがロードされます 決して
第二に、あなたは 145
以来、それを正しく判断しました 157
への直接リンク 、および 162
以降 172
への直接リンク 、 184
が保証されています
しかし、あなたは誤って 196
と決めました より良いですが、そうではありません:205
小さい バイナリ (私のシステムでは 11K。210
の 2.4MB と比較してください) )、そしてそれが行うことはほぼすべて 223
です 237
の .これは 241
で見るのは簡単です 出力:
strace -e trace=execve /usr/bin/Rscript --default-packages=base --vanilla /dev/null
execve("/usr/bin/Rscript", ["/usr/bin/Rscript", "--default-packages=base", "--vanilla", "/dev/null"], [/* 42 vars */]) = 0
execve("/usr/lib/R/bin/R", ["/usr/lib/R/bin/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null", "--args"], [/* 43 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=89625, si_status=0, si_utime=0, si_stime=0} ---
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=89626, si_status=0, si_utime=0, si_stime=0} ---
execve("/usr/lib/R/bin/exec/R", ["/usr/lib/R/bin/exec/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null", "--args"], [/* 51 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=89630, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
つまり、スクリプトの実行が開始されるまでに 254
ロードされ、263
271
の依存関係として読み込まれます 実際にはしない
それを機能させることはまったく可能ですか
おそらく。考えられる解決策は 2 つあります:
<オール>287
のふりをする 実際は 299
です 309
全体を再構築します 319
に対するパッケージ .
#1 については、320
する必要があります 、それから 337
のコピーを確認してください 346
を設定することにより、システムの前に検出されます
これは私にとってはうまくいくようです:
mkdir /tmp/libblas
# pretend that libc.so.6 is really libblas.so.3
cp /lib/x86_64-linux-gnu/libc.so.6 /tmp/libblas/libblas.so.3
LD_LIBRARY_PATH=/tmp/libblas /usr/bin/Rscript /dev/null
Error in dyn.load(file, DLLpath = DLLpath, ...) :
unable to load shared object '/usr/lib/R/library/stats/libs/stats.so':
/usr/lib/liblapack.so.3: undefined symbol: cgemv_
During startup - Warning message:
package ‘stats’ in options("defaultPackages") was not found
エラーが発生したことに注意してください (私の「ふり」 357
実際には 363
のコピーであるため、期待されるシンボルを定義していません ).
375
のバージョンを確認することもできます この方法でロードされています:
LD_DEBUG=libs LD_LIBRARY_PATH=/tmp/libblas /usr/bin/Rscript /dev/null |& grep 'libblas\.so\.3'
91533: find library=libblas.so.3 [0]; searching
91533: trying file=/usr/lib/R/lib/libblas.so.3
91533: trying file=/usr/lib/x86_64-linux-gnu/libblas.so.3
91533: trying file=/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server/libblas.so.3
91533: trying file=/tmp/libblas/libblas.so.3
91533: calling init: /tmp/libblas/libblas.so.3
#2 について、あなたはこう言いました:
<ブロック引用>テストしたいマシンにルート アクセス権がないため、OpenBLAS への実際のリンクは不可能です。
385
をビルドできる場合 、確かに 398
の独自のバージョンを構築することもできます .
更新:
<ブロック引用>libblas.so.3 と libopenblas.so.0 が同じシンボルを定義していると最初に述べましたが、これはどういう意味ですか?それらは異なる SONAME を持っていますが、システムでそれらを区別するには不十分ですか?
記号と 409
何も持っていない
410
からの出力にシンボルが表示されます と 428
. 433
に関連する記号 446
など 、両方のライブラリに表示されます。
454
についての混乱 たぶん Windows から来ています。 461
Windows 上の s は、まったく異なる設計になっています。特に 478
の場合 シンボル 488
をインポートします 492
から 、両方 シンボルの名前 (508
) と 519
そのシンボルのインポート元 (521
) は 533
に記録されます s インポート テーブル。
これにより、547
を簡単に取得できます import 550
567
から 、 575
の間 580
から同じシンボルをインポートします .
ただし、これによりライブラリの挿入が難しくなり、アーカイブ ライブラリの動作とはまったく異なる動作をします (Windows でも)。
どちらの設計が全体的に優れているかについては意見が分かれていますが、どちらのシステムもモデルを変更することはありません。
UNIX が Windows スタイルのシンボル バインディングをエミュレートする方法があります。594
を参照してください。 dlopen の man ページにあります。注意:これらは危険に満ちており、UNIX の専門家を混乱させる可能性があり、広く使用されておらず、実装のバグがある可能性があります。
アップデート 2:
<ブロック引用>R をコンパイルしてホーム ディレクトリにインストールするということですか?
はい。
<ブロック引用>次に、それを呼び出したいときは、自分のバージョンの実行可能プログラムへのパスを明示的に指定する必要があります。そうしないと、代わりにシステム上のものが呼び出される可能性がありますか?または、このパスを環境変数 $PATH の最初の位置に配置して、システムをごまかすことはできますか?
どちらの方法でも機能します。
********************
解決策 1:
********************
雇用されたロシア人のおかげで、私の問題はついに解決されました.調査には、Linux システムのデバッグとパッチ適用に関する重要なスキルが必要です 、そしてこれは私が学んだ大きな資産だと思います。ここに解決策を投稿し、元の投稿のいくつかの点を修正します。
1 R の呼び出しについて
最初の投稿で、R を起動するには 2 つの方法があると述べました。 または 619
.しかし、私はそれらの違いを誤って誇張しています。重要な Linux デバッグ機能 626
を使用して、起動プロセスを調査してみましょう。 (639
を参照) )。シェルでコマンドを入力した後、実際には多くの興味深いことが起こっています。
strace -e trace=process [command]
プロセス管理に関係するすべてのシステム コールを追跡します。その結果、プロセスの fork、wait、および実行のステップを監視できます。マニュアルページには記載されていませんが、@Employed Russian は 646
のサブクラスのみを指定できることを示しています。 、たとえば、657
実行ステップのために。
662
の場合
~/Desktop/dgemm$ time strace -e trace=execve R --vanilla < /dev/null > /dev/null
execve("/usr/bin/R", ["R", "--vanilla"], [/* 70 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5777, si_status=0, si_utime=0, si_stime=0} ---
execve("/usr/lib/R/bin/exec/R", ["/usr/lib/R/bin/exec/R", "--vanilla"], [/* 79 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5778, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
real 0m0.345s
user 0m0.256s
sys 0m0.068s
677
の間
~/Desktop/dgemm$ time strace -e trace=execve Rscript --default-packages=base --vanilla /dev/null
execve("/usr/bin/Rscript", ["Rscript", "--default-packages=base", "--vanilla", "/dev/null"], [/* 70 vars */]) = 0
execve("/usr/lib/R/bin/R", ["/usr/lib/R/bin/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null"], [/* 71 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5822, si_status=0, si_utime=0, si_stime=0} ---
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5823, si_status=0, si_utime=0, si_stime=0} ---
execve("/usr/lib/R/bin/exec/R", ["/usr/lib/R/bin/exec/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null"], [/* 80 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5827, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
real 0m0.063s
user 0m0.020s
sys 0m0.028s
689
も使用しています 起動時間を測定します。注意してください
698
702
よりも約 5.5 倍高速です .その理由の 1 つは、711
です。 726
の間、起動時に 6 つのデフォルト パッケージを読み込みます 1 つの 730
のみを読み込みます コントロールによるパッケージ:745
.ただし、この設定がなくてもはるかに高速です。753
に向けられます 、そして私の最初の投稿で、私はすでに 762
を悪用しました この実行可能ファイルが 775
をロードすることを示します 、 788
にリンクされています . @Employed Russian の説明によると、先にロードした BLAS ライブラリが勝つため、私の元の方法が機能するわけがありません。791
を正常に実行するには 、素晴らしいを使用しました ファイル 805
必要に応じて入力ファイルおよび出力ファイルとして。例:812
824
の間、入力ファイルを要求します 両方を要求します。コマンドをスムーズに実行し、出力をきれいにするために、null デバイスを両方に供給します。 null デバイスは物理的に存在するファイルですが、驚くほど機能します。そこから読み取る場合、何も含まれていません。書き込み中は、すべてを破棄します。2.チート R
839
以来 とにかくロードされますが、私たちができる唯一のことは、このライブラリの独自のバージョンを提供することです。元の投稿で述べたように、root アクセス権があれば、 848
を使用することで、これは非常に簡単です。 、システムLinuxがこの切り替えを完了するのに役立つように.しかし、@Employed Russian は、root アクセスなしでシステムをごまかす素晴らしい方法を提供しています:R が起動時にどのように BLAS ライブラリを見つけているかを調べて、システムのデフォルトが見つかる前に私たちのバージョンをフィードするようにしましょう! 共有ライブラリがどのように検出されロードされるかを監視するには、環境変数 853
を使用します .
プレフィックス 864
を持つ多くの Linux 環境変数があります。 、870
に記載されているとおり .これらの変数は実行可能ファイルの前に割り当てることができるため、プログラムの実行機能を変更できます。便利な変数には次のものがあります:
884
ランタイム ライブラリの検索パスを設定します。895
共有ライブラリのルックアップとロードを追跡するため;907
プログラムによってロードされたすべてのライブラリを表示するため (914
と同様に動作します) );921
他のすべてのライブラリが検索される前に、最初にライブラリをプログラムに強制的に挿入するため;932
と942
1 のプロファイリング用 指定された共有ライブラリ。セクション 3.4.1.1 sprof を読んだ R ユーザー R 拡張機能の記述については、これが R 内からコンパイルされたコードのプロファイリングに使用されることを思い出してください。
952
の使用 見ることができます:
~/Desktop/dgemm$ LD_DEBUG=help cat
Valid options for the LD_DEBUG environment variable are:
libs display library search paths
reloc display relocation processing
files display progress for input file
symbols display symbol table processing
bindings display information about symbol binding
versions display version dependencies
scopes display scope information
all all previous options combined
statistics display relocation statistics
unused determined unused DSOs
help display this help message and exit
To direct the debugging output into a file instead of standard output a filename can be specified using the LD_DEBUG_OUTPUT environment variable.
ここで、963
の使用に特に関心があります。 .たとえば、
~/Desktop/dgemm$ LD_DEBUG=libs Rscript --default-packages=base --vanilla /dev/null |& grep blas
5974: find library=libblas.so.3 [0]; searching
5974: trying file=/usr/lib/R/lib/libblas.so.3
5974: trying file=/usr/lib/i386-linux-gnu/i686/sse2/libblas.so.3
5974: trying file=/usr/lib/i386-linux-gnu/i686/cmov/libblas.so.3
5974: trying file=/usr/lib/i386-linux-gnu/i686/libblas.so.3
5974: trying file=/usr/lib/i386-linux-gnu/sse2/libblas.so.3
5974: trying file=/usr/lib/i386-linux-gnu/libblas.so.3
5974: trying file=/usr/lib/jvm/java-7-openjdk-i386/jre/lib/i386/client/libblas.so.3
5974: trying file=/usr/lib/libblas.so.3
5974: calling init: /usr/lib/libblas.so.3
5974: calling fini: /usr/lib/libblas.so.3 [0]
R プログラムが 978
を見つけてロードしようとしたさまざまな試みを示しています . 989
の独自のバージョンを提供できれば 、そして R が最初にそれを見つけることを確認してください。それから問題は解決されます。
まずはシンボリックリンク 990
を作ってみましょう OpenBLAS ライブラリ 1006
への作業パス 、次にデフォルトの 1019
を展開します 私たちの作業パスで(そしてエクスポート それ):
~/Desktop/dgemm$ ln -sf libopenblas.so libblas.so.3
~/Desktop/dgemm$ export LD_LIBRARY_PATH = $(pwd):$LD_LIBRARY_PATH ## put our working path at top
ライブラリの読み込みプロセスをもう一度確認してみましょう:
~/Desktop/dgemm$ LD_DEBUG=libs Rscript --default-packages=base --vanilla /dev/null |& grep blas
6063: find library=libblas.so.3 [0]; searching
6063: trying file=/usr/lib/R/lib/libblas.so.3
6063: trying file=/usr/lib/i386-linux-gnu/i686/sse2/libblas.so.3
6063: trying file=/usr/lib/i386-linux-gnu/i686/cmov/libblas.so.3
6063: trying file=/usr/lib/i386-linux-gnu/i686/libblas.so.3
6063: trying file=/usr/lib/i386-linux-gnu/sse2/libblas.so.3
6063: trying file=/usr/lib/i386-linux-gnu/libblas.so.3
6063: trying file=/usr/lib/jvm/java-7-openjdk-i386/jre/lib/i386/client/libblas.so.3
6063: trying file=/home/zheyuan/Desktop/dgemm/libblas.so.3
6063: calling init: /home/zheyuan/Desktop/dgemm/libblas.so.3
6063: calling fini: /home/zheyuan/Desktop/dgemm/libblas.so.3 [0]
すごい! R をだますことに成功しました。
3. OpenBLAS を試す
~/Desktop/dgemm$ Rscript --default-packages=base --vanilla mmperf.R
GFLOPs = 8.77
これで、すべてが期待どおりに機能します!
4. 1028
の設定を解除します (念のため)
1032
の設定を解除することをお勧めします 使用後。
~/Desktop/dgemm$ unset LD_LIBRARY_PATH
********************
解決策 2:
********************
ここでは、環境変数 1042
を利用する別のソリューションを提供します。 ソリューション 1 に記載 . 1053
の使用 Cライブラリ1061
の前であっても、他のプログラムの前に特定のライブラリをプログラムに強制的にロードするため、より「残忍」です。 !これは、Linux 開発における緊急のパッチ適用によく使用されます。
元の投稿のパート 2 に示されているように 、共有 BLAS ライブラリ 1076
SONAME を持っています 1089
. SONAME は動的ライブラリ ローダーが実行時に検索する内部名であるため、1097
へのシンボリック リンクを作成する必要があります。 このSONAMEで :
~/Desktop/dgemm$ ln -sf libopenblas.so libopenblas.so.0
次にエクスポートします:
~/Desktop/dgemm$ export LD_PRELOAD=$(pwd)/libopenblas.so.0
フルパス 1104
へ 1111
にフィードする必要があります 1121
の場合でも、ロードが成功した場合 1135
の下にあります .
1141
を起動します 1152
までに何が起こるかを確認してください :
~/Desktop/dgemm$ LD_DEBUG=libs Rscript --default-packages=base --vanilla /dev/null |& grep blas
4860: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4860: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4865: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4868: calling fini: /home/zheyuan/Desktop/dgemm/libopenblas.so [0]
4870: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4869: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4867: calling fini: /home/zheyuan/Desktop/dgemm/libopenblas.so [0]
4860: find library=libblas.so.3 [0]; searching
4860: trying file=/usr/lib/R/lib/libblas.so.3
4860: trying file=/usr/lib/i386-linux-gnu/i686/sse2/libblas.so.3
4860: trying file=/usr/lib/i386-linux-gnu/i686/cmov/libblas.so.3
4860: trying file=/usr/lib/i386-linux-gnu/i686/libblas.so.3
4860: trying file=/usr/lib/i386-linux-gnu/sse2/libblas.so.3
4860: trying file=/usr/lib/i386-linux-gnu/libblas.so.3
4860: trying file=/usr/lib/jvm/java-7-openjdk-i386/jre/lib/i386/client/libblas.so.3
4860: trying file=/usr/lib/libblas.so.3
4860: calling init: /usr/lib/libblas.so.3
4860: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4874: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4876: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4860: calling fini: /home/zheyuan/Desktop/dgemm/libopenblas.so [0]
4860: calling fini: /usr/lib/libblas.so.3 [0]
ソリューション 1 で見たものとの比較 独自のバージョンの 1164
で R をごまかす 、私たちはそれを見ることができます
1174
最初に読み込まれるため、1180
によって最初に検出されます;1193
以降1208
が見つかりました1218
の検索と読み込みを続けます .ただし、これは「先着順」では効果がありません 元の回答で説明されているルール
よし、すべて動作するので、1223
をテストします プログラム:
~/Desktop/dgemm$ Rscript --default-packages=base --vanilla mmperf.R
GFLOPs = 9.62
結果の 9.62 は、以前の解で見た 8.77 よりも大きいのは、単なる偶然です。 OpenBLAS を使用するためのテストとして、より正確な結果を得るために実験を何度も実行することはありません。
そしていつものように、最後に環境変数を設定解除します:
~/Desktop/dgemm$ unset LD_PRELOAD