Make
Compile
普通はgccで十分ですが,intelなどが提供しているcompilerはよりtune upされ,高速です.freeのgcc+lapack+blasとintelのicc+mklを比べておきます.
okubo1 bob/MatrixInverse> gcc lapack.c -llapack -lblas -lg2c -o lapack1 okubo1 bob/MatrixInverse> ./lapack1 1000 1000 [dim] 1.4200 [sec] #LAPACK okubo1 bob/MatrixInverse> /usr/opt/intel/cc/9.1.042/bin/icc lapack.c -L/usr/opt/intel/mkl/8.1.1/lib/32 -lmkl_lapack -lmkl_ia32 -lguide -o lapack2 okubo1 bob/MatrixInverse> ./lapack2 1000 1000 [dim] 0.1400 [sec] #LAPACK
約10倍の差.DGESVで1000x1000の演算をおこなうとtime x MFlops = 668.5 という定数になります.
Make
makeやlibraryなどは,compileの手間・時間を省く知識の集大成.一見ややこしいことも,なぜするのかを考えて理解すると筋が通るはず.基本は「あるものは利用せよ」あるいは「二度手間をかけるな」.巨大なプログラムも簡単なコードの寄せ集め.先ずは簡単なコードでheader, include, library, makeなどの基本を理解せよ.覚える必要はない...
header fileのinclude
関数が増えてくると,その宣言をいちいちするのが面倒になる.header fileは関数宣言を一括しておこなうことができる.header fileにちゃんと宣言しておけば,それをincludeするだけで宣言部を省略することができる.<stdio.h>などもheader file. 先ずは普通のプログラム.
[BobsNewPBG4:~/Desktop/test2] bob% gcc prog.c [BobsNewPBG4:~/Desktop/test2] bob% ./a.out Hello world!! [BobsNewPBG4:~/Desktop/test2] bob% cat prog.c #include <stdio.h> int main(void){ printf("Hello world!!\n"); exit(0); }
関数を沢山持った場合.
[BobsNewPBG4:~/Desktop/test2] bob% cat prog.c #include <stdio.h> void f1(void); void f2(void); int main(void){ f1(); f2(); exit(0); } void f1(void){ printf("Hello world from f1!!\n"); return; } void f2(void){ printf("Hello world from f2!!\n"); return; } [BobsNewPBG4:~/Desktop/test2] bob% gcc prog.c [BobsNewPBG4:~/Desktop/test2] bob% ./a.out Hello world from f1!! Hello world from f2!!
header fileを使った場合.
[BobsNewPBG4:~/Desktop/test2] bob% cat prog.c #include "prog.h" int main(void){ f1(); f2(); exit(0); } void f1(void){ printf("Hello world from f1!!\n"); return; } void f2(void){ printf("Hello world from f2!!\n"); return; } [BobsNewPBG4:~/Desktop/test2] bob% gcc prog.c [BobsNewPBG4:~/Desktop/test2] bob% ./a.out Hello world from f1!! Hello world from f2!! [BobsNewPBG4:~/NumRecipeEx05/Make] bob% cat prog.h #include <stdio.h> void f1(void); void f2(void);
分割コンパイル
コードを2つにわけた場合.prog.hは前と同じ.
[BobsNewPBG4:~/NumRecipeEx05/Make] bob% cat prog.c #include "prog.h" int main(void){ f1(); f2(); exit(0); } void f1(void){ printf("Hello world from f1!!\n"); return; } [BobsNewPBG4:~/NumRecipeEx05/Make] bob% cat prog2.c #include "prog.h" void f2(void){ printf("Hello world from f2!!\n"); return; }
コンパイルと実行.
[BobsNewPBG4:~/NumRecipeEx05/Make] bob% gcc prog.c prog2.c [BobsNewPBG4:~/NumRecipeEx05/Make] bob% ./a.out Hello world from f1!! Hello world from f2!!
make
実際のexec file(実行ファイル)の作成は,
source(*.c) -> objects(*.o) -> exec(a.out)
という手順になっている.
gcc -c prog.cでprog.oが gcc -o prog prog.o prog2.oでprogと名付けたexec fileができる.
これを自動的にやってくれるのが,make. Makefileにその手順を記述する.タブとspaceが違うので注意.
[BobsNewPBG4:~/Desktop/test2] bob% cat Makefile prog: prog.o prog2.o gcc -o prog prog.o prog2.o prog.o: prog.c prog.h gcc -c prog.c prog2.o: prog2.c prog.h gcc -c prog2.c
makeはMakefileを参照しながら,必要最低限のcompileを実行する.全部のファイルを再compileするには,touch *.cでディレクトリー内のcファイルの最終変更時間を最新にする.
[BobsNewPBG4:~/NumRecipeEx05/Make] bob% touch *.c [BobsNewPBG4:~/NumRecipeEx05/Make] bob% make gcc -c prog.c gcc -c prog2.c gcc -o prog prog.o prog2.o [BobsNewPBG4:~/NumRecipeEx05/Make] bob% ./a.out Hello world from f1!! Hello world from f2!!
.cや.hの依存関係はgcc -MMで作成可能.
[BobsNewPBG4:~/NumRecipeEx05/Make] bob% gcc -MM prog.c prog2.c prog.o: prog.c prog.h prog2.o: prog2.c prog.h
autoconf, automakeで,より複雑なMakefileを作成可能.
archive
よく使う関数をまとめておくのにライブラリが使われる.
ar
- -- create and maintain library archives
- ライブラリアーカイブの作成と維持.
ar -r libfile.a prog2.o
- r(置換),d(削除),q(追加), x(取出), t(リスト表示)
ranlib(必要がない場合もある)
- 高速リンクのためのtable __.SYMDEF SORTED を作成.
ranlib libfile.a
- ar -sでも同じ
nm libfile.a
でテーブルの中身を見ることができる.
libを使ったコンパイル
gcc -o prog prog.o libfile.a
makeによる自動化
[BobsNewPBG4:~/NumRecipeEx05/Make] bob% cat Makefile CC = gcc LIB=libfile.a prog: prog.o $(LIB) $(CC) -o $@ prog.o -L. -lfile prog.o: prog.c prog.h $(CC) -c $(CFLAGS) $*.c prog2.o: prog2.c prog.h $(CC) -c $(CFLAGS) $*.c $(LIB): prog2.o ar -r $(LIB) prog2.o ranlib $(LIB)
あるいは
[BobsNewPBG4:~/NumRecipeEx05/Make] bob% cat Makefile CC = gcc OBJS = prog.o prog2.o LIB=libfile.a LIBO = prog2.o CFLAGS = -Wall -g #CFLAGS = -O3 prog: $(OBJS) $(LIB) $(CC) -o $@ $(CFLAGS) $(OBJS) $(LIB) prog.o: prog.c prog.h $(CC) -c $(CFLAGS) $*.c prog2.o: prog2.c prog.h $(CC) -c $(CFLAGS) $*.c $(LIB): $(LIBO) rm -f $(LIB) ar -r $(LIB) $(LIBO) ranlib $(LIB)
makeを実行している様子は,以下の通り.
[BobsNewPBG4:~/NumRecipeEx05/Make] bob% make gcc -c -Wall -g prog.c prog.c: In function `main': prog.c:6: warning: implicit declaration of function `exit' gcc -c -Wall -g prog2.c rm -f libfile.a ar -r libfile.a prog2.o ar: creating archive libfile.a ranlib libfile.a gcc -o prog -Wall -g libfile.a prog.o prog2.o
Path(パス,経路)
コマンドやライブラリ,manなど特殊なファイルが置かれているディレクトリを探しやすいように,あらかじめ明示的に宣言することに相当します.代表的なのがコマンドサーチパスで,環境変数PATHで設定します.
設定されている環境変数の表示
setenv echo $PATH
パスの設定
使っているシェルによって設定の仕方が違います.何を使っているかは以下のコマンド.
[BobsNewPBG4:~/XML] bob% echo $SHELL /bin/tcsh
csh, tcsh
setenv PATH ~/bin:$PATH
bash
PATH=~/bin:$PATH export PATH
コマンド,ライブラリの確認
which, whereis
ld
オブジェクトとライブラリのリンク(結合).
gcc -lm Coin.c
とかの-lmというのは何というのをよく聞かれるが,それは以下の通り.
-l(library_name) lib(library_name).a
となっている
- ライブラリのサーチパス(捜すdirectory)は-Lで明示.ここの例では,
gcc -o prog prog.o -L. -lfile
- -L. -lfileなどはprog.oより後にないとだめ.
- -lmとはどこかにあるlibm.aというライブラリをリンクしなさいという命令.
.aと.so
.aは静的なライブラリで,exec fileの中に取り込まれる.一方,.soは動的なライブラリで,実行時にシステムによって自動的に取り込まれる.実行時に
./lapack2: error while loading shared libraries: libguide.so: cannot open shared object file: No such file or directory
というのは,*.soがありませんというエラー.*.soに明示的にpathを通す必要がある.
intelのmklを通すときには,/etc/ld.so.confに
/opt/intel/mkl/8.1.1/lib/32
を追加して,
ldconfig -v
を実行して,登録しておく必要がある.
Keyword(s):
References:[LinuxEx]