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

を実行して,登録しておく必要がある.

Last modified:2007/11/15 17:23:31
Keyword(s):
References:[LinuxEx]