F. Kusuhara at and Shiohara Labo.
英語 Top / 日本語

Linux(CentOS 5)で計算機ネットワーク

last update on Dec. 25, 2007

はじめに

このページの内容は個人的な備忘録として作ったものですが,ひょっとしたら誰かの参考になるかもしれないと思って公開しているものです。なるべく間違いはないように気をつけてはいますが,(言い訳ですが)講義の資料や教科書用に作ったわけではないので間違いや勘違い(特に用語の使い方など)を含んだままの可能性が高いものです。ここを参考にしたせいで損害が生じたり恥をかいたりしても責任をもてませんので悪しからずご了承下さい。

目指しているのは研究室で誰も使わなくなったPCを集めてきて計算機ネットワーク(大げさ)を組むことです。ユーザー管理の統合から始まって,ジョブ管理,最終的にはPCクラスタとして機能させ並列計算を行うことを目標にします。

なぜ,こんなことをする気になったのか・・・。あいているPCにLinuxをインストールし,Fortranで組んだプログラムで解析を行う環境を作ってみたものの,いかんせんPCのパワーがないことは否めず,パラメトリックに解析したりするとやたらと時間がかかります。幸い使わないPCが2台ほど増えたのでこいつらにもLinuxをインストールし,計算機に仕立てることにしました。

複数のPCでパラメトリックな解析を平行して行おうとすると,すべてのPCで作ったプログラムをコンパイルし,プログラムを修正したらすべてのPCで同じ作業を繰り返し・・・,そのうちどれが最新バージョンかわからなくなったりしてめんどうです。また,解析ケースにより解析時間に差がつく上にPCの能力にも差があるため,効率よくパラメトリック解析を進めていくためには実行が終わったPCから順に次の解析を始めていくジョブ管理システムがほしくなってきました。そこで,複数のLinuxの計算機でネットワークを組み,ファイルの共有,ユーザー管理統合,ジョブ管理システムの導入などをしていくことにしました。

また,やや古いPCのため積んでいるメモリの容量が小さめでちょっと大きなモデルの解析を行おうとするとすぐにメモリ不足に陥ってしまう状況のため,これもなんとかしたいな,と。メモリを増設するのもしたくないし(←お金もないし),ということでここはPCクラスタを組んで各PCのメモリに分散してデータを配置できるメモリ分散型の並列計算を試みることにしました。

以下に進む前に,まず以前にしたのと同じように(詳細はFortran90で構造解析)2台目以降のPCをセットアップします。OSはCentOS 5です。ただし,ネットワーク関係の設定は以下の通りとし,以下に説明するように /home はネットワーク内でファイル共有をするので,Samba(Windowsファイルサーバー)は2台目以降では不要となります。また,/usr/local もファイル共有しておけば,1台目のPCで /usr/local(もしくは /opt)以下にインストールしたソフトは2台目以降ではインストールする必要はありません。

このページの先頭

last update on Feb. 18, 2008

ネットワークの構成と設定

gateway

構築するネットワークですが,右図のようにすでにセットアップしてあるdrunk00をサーバーにしてその下にぶら下げる形で計算用のPC(クライアント)を追加していくことにします。クライアントは研究室のネットワークへはdrunk00をゲートウェイにして接続することになります。ローカルなネットワーク内の計算用のPCは本来は外部のネットワークと直接通信できる必要はありませんが,ソフトのインストールやシステムのアップデートに不便なので IPマスカレードという仕組みをサーバーに実装することにします。

もちろん,すべての計算用PCを並列に研究室のネットワーク内に設置してもかまいませんが,ノード間の通信にrshを使う点などを考慮してこのようにすることにしました。

ホスト名とIPアドレスの設定

ホスト名とIPアドレス

各PCのネットワークの設定は以下のようにします。今後,1台目(drunk00)を計算機ネットワークのサーバー,2台目以降(drunk01,drunk02,...)をクライアント呼ぶことにします。

ホスト名 IPアドレス ゲートウェイ
サーバー(eth0:研究室側) drunk00.drunks 192.168.0.100 (環境に合わせて適宜)
サーバー(eth1:計算機側) 192.168.1.100 (設定しない)
クライアント1(eth0) drunk01.drunks 192.168.1.101 192.168.1.100
クライアント2(eth0) drunk02.drunks 192.168.1.102 192.168.1.100

一度設定したホスト名を変更するには /etc/sysconfig/network 内の HOSTNAME と,下記 etc/hosts (127.0.0.1で始まる行も)を編集します。編集したらシステムを再起動しておきます(たぶん必要)。

IPアドレスの設定

IPアドレスとゲートウェイアドレスは /etc/sysconfig/network-scripts/ifcfg-eth0 などの IPADDR,GATEWAY を編集するか,system-config-networkを実行すれば変更できます。ネーム解決は /etc/resolv.conf を計算機ネットワークの外側のネットワーク(今回の場合研究室のネットワーク)にあわせて適宜(クライアントもサーバーと同じでたぶんだいじょうぶ)設定します。

ただ,system-config-networkを実行すると直接ファイルを編集して設定したネットワーク関係の設定(/etc/hostsなど)が消えてしまう場合があるので注意が必要です。これは /etc/sysconfig/networking/profile/default 以下のファイルもあわせて編集すれば回避できるかもしれません。←未確認

設定後,各設定ファイルは次のようになっているはずです。

(サーバーの /etc/sysconfig/network-scripts/ifcfg-eth0)

DEVICE=eth0
BOOTPROTO=none
IPADDR=192.168.0.100
NETMASK=255.255.255.0
ONBOOT=yes
GATEWAY=192.168.0.1(環境にあわせて適宜)
TYPE=Ethernet

(サーバーの /etc/sysconfig/network-scripts/ifcfg-eth1)

DEVICE=eth1
BOOTPROTO=none
IPADDR=192.168.1.100
NETMASK=255.255.255.0
ONBOOT=yes
TYPE=Ethernet

(クライアントの /etc/sysconfig/network-scripts/ifcfg-eth0)

DEVICE=eth0
BOOTPROTO=none
IPADDR=192.168.1.101 or 192.168.1.102
NETMASK=255.255.255.0
ONBOOT=yes
GATEWAY=192.168.1.100
TYPE=Ethernet

設定を変更した場合は,設定後ネットワークを再起動します。

# /sbin/service network restart

/etc/hostsの設定

お互いをIPアドレスではなくホスト名もしくはエイリアス名で指定できるように,サーバーおよびクライアントの /etc/hosts に以下を追記します。

192.168.1.100[tab]drunk00.drunks[tab]drunk00
192.168.1.101[tab]drunk01.drunks[tab]drunk01
192.168.1.102[tab]drunk02.drunks[tab]drunk02

サーバー(ゲートウェイ)の設定

以下は,基本的にNIC2枚挿しによる ゲートウェイ サーバー構築を参考にしています。

IPv4 フォワーディングを有効にする

サーバー(drunk00)で IPv4 Forwarding が機能するように /etc/sysctl.conf を次のように変更します。

net.ipv4.ip_forward=0
↓
net.ipv4.ip_forward=1

ネットワークを再起動します。

# /sbin/service network restart

セキュリティレベルの設定

一般的にはコマンドでフィルタリングルールを追加・削除をし確認を行いますが,iptables コマンド,テーブルやチェーン,書式を理解するほど勉強するのもたいへんなので,ここでは「セキュリティレベル」 ツールを利用することにします。

# yum install system-config-securitylevel(インストールされていない場合)
# system-config-securitylevel-tui

ここで,セキュリティレベル(ファイアウォール)を有効に,SElinuxは無効にします。また,カスタマイズにすすんで信頼できるデバイスの eth1 にチェックをいれ,受信を許可の SSH と Samba にチェックをいれます。設定が終了すると /etc/sysconfig/iptables に設定が書き込まれます。

この状態では,drunk00に設置したファイアウォールは外部(研究室側)からは SSH と Samba に関する通信のみ受け付け,内部(計算機側)からはすべての通信を無条件に通します。

IPマスカレードの設定

iptables に新しい natテーブル PREROUTINGチェーンを追加します。以下の iptables コマンドを実行します。

# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

ptables 起動時に変更が有効になるように以下のコマンドで /etc/sysconfig/iptables に書き出します。

# /sbin/service iptables save

なお,これ以降に最初に使った system-config-securitylevel ツールを使うとせっかく設定したフィルターの内容(IPマスカレードのためのnatテーブル)が消えてしまいます。

このページの先頭

last update on Dec. 25, 2007

NFSによるファイル共有の設定

NFS(Network File System)を使ってファイルの共有をします。この設定によりクライアントからサーバー上のファイルを直接参照できるようになります。

/home を共有することで計算機ネットワークのどのマシンにログインしても,サーバー上のホームディレクトリ以下のファイルを同じように扱えます。また,/usr/local 以下にインストールしたプログラムをクライアント上でも実行可能になります。

サーバーの設定

サーバー(drunk00)をNFSサーバーとして設定します。/etc/exports に以下を追加します。

/home[tab---->]drunk01(rw,root_squash,sync)
/home[tab---->]drunk02(rw,root_squash,sync)
/usr/local[tab]drunk01(rw,no_root_squash,sync)
/usr/local[tab]drunk02(rw,no_root_squash,sync)

NFSサーバーの起動,自動起動設定をします。

# /sbin/service nfs start
# /sbin/chkconfig nfs on

クライアントの設定

クライアント(drunk01,02)でサーバーが export しているファイルをマウントするための設定を行います。マシンの起動時に自動でマウントされるように /eetc/fstab に以下を追加します。

drunk00:/home[tab---->]/home[tab---->]nfs[tab]defaults[tab]0 0
drunk00:/usr/local[tab]/usr/local[tab]nfs[tab]defaults[tab]0 0

手動で /etc/fstab に記述されたファイルをマウントするには次のようにします。

# mount -a
このページの先頭

last update on Feb. 18, 2008

LDAPによるユーザー管理の統合

LDAP(Lightweight Directory Access Protocol)を使ってユーザー情報の一元管理を行います。ユーザーの登録情報を各クライアントで保存しておくのではなく,サーバー(LDAPサーバー)に登録しておき各クライアントは必要に応じてサーバーに照会を行うようにします。これによりLDAPサーバーにユーザーを登録すればすべてのクライアントでログインできますし,もちろんパスワードの変更などもすべてのクライアントで反映されます。

また,せっかくなのでWindowsとのファイル共有(Samba)のユーザー認証もLDAPサーバーに照会するようにし,ユーザー管理を統合することにします。

以下は基本的にFedoraで自宅サーバー構築Windowsネットワーク用統合認証サーバー構築(OpenLDAP+Samba)を参考にしています。ただ,Samba用LDAPサーバー操作ツール(smbldap-tools)はCentOS 5では現在バージョンが0.9.2('07年12月現在)になり上記サイトの説明とは設定のしかたが変わっています。

システムのユーザー管理をLDAPへ移行

サーバーの設定

LDAPサーバーのインストールをします。

# yum install openldap openldap-servers openldap-clients

LDAPサーバーの設定をします。まず,LDAPサーバー管理者パスワード(MD5暗号化形式)取得します。

# slappasswd -s パスワード -h {MD5}
{MD5}************************ (以下で使用します)

/etc/openldap/slapd.conf を次のように編集します。

(ldbm and/or bdb database definitions セクションを編集)
database        bdb
suffix          "dc=drunk00,dc=drunks"
rootdn          "cn=Manager,dc=drunk00,dc=drunks"
rootpw          {MD5}************************ (slappasswdで取得したもの)

(access control policy セクションに追加)
access to attrs=userPassword
        by self write
        by dn="cn=Manager,dc=drunk00,dc=drunks" write
        by anonymous auth
        by * none
access to *
        by dn="cn=Manager,dc=drunk00,dc=drunks" write
        by self write
        by * read

LDAPサーバーを起動します。

# /sbin/service ldap start

LDAPサーバーが自動で起動するように設定しておきます。

# /sbin/chkconfig ldap on

既存ユーザ情報をLDAPサーバーへ移行します。

1) LDAPサーバー初期情報登録

ユーザ情報移行ツール設定ファイル /usr/share/openldap/migration/migrate_common.ph を編集します。

$DEFAULT_BASE = "dc=drunk00,dc=drunks"

LDAPサーバー初期情報登録用ファイル base.ldif を次の内容で作成します

dn: dc=drunk00,dc=drunks
objectClass: dcObject
objectClass: organization
o: drunks
dc: drunk00

dn: cn=Manager,dc=drunk00,dc=drunks
objectClass: organizationalRole
cn: Manager

dn: ou=People,dc=drunk00,dc=drunks
objectClass: organizationalUnit
ou: People

dn: ou=Group,dc=drunk00,dc=drunks
objectClass: organizationalUnit
ou: Group

LDAPサーバーに初期情報(データベースの構造)を登録します。(LDAPサーバー管理者パスワードを聞かれます。)

# ldapadd -h localhost -x -D "cn=Manager,dc=drunk00,dc=drunks" -W -f base.ldif

2) 既存ユーザー情報をLDAPサーバーへ登録

一般ユーザー情報を passwd から抽出します。(以下は一般ユーザーのIDが1000番台の場合)

# grep ":1[0-9][0-9][0-9]" /etc/passwd > passwd

一般ユーザー情報のLDAPサーバー登録用ファイル作成します。

# /usr/share/openldap/migration/migrate_passwd.pl passwd > passwd.ldif

一般ユーザ情報をLDAPサーバーへ登録します。(LDAPサーバー管理者パスワードを聞かれます。)

# ldapadd -h localhost -x -D "cn=Manager,dc=drunk00,dc=drunks" -W -f passwd.ldif

3) 既存グループ情報をLDAPサーバーへ登録

一般ユーザーグループ情報を抽出します。

# grep "users" /etc/group > group
# grep ":1[0-9][0-9][0-9]" /etc/group >> group

一般ユーザーグループ情報のLDAPサーバー登録用ファイルを作成します。

# /usr/share/openldap/migration/migrate_group.pl group > group.ldif

一般ユーザーグループ情報をLDAPサーバーへ登録します。(LDAPサーバー管理者パスワードを聞かれます。)

# ldapadd -h localhost -x -D "cn=Manager,dc=drunk00,dc=drunks" -W -f group.ldif

最後に,設定に使った登録用ファイル(*.ldif)は削除しておいたほうがよいでしょう。

クライアントの設定

クライアント(drunk01,drunk02)にLDAP(openldap)をインストールします。

# yum install openldap

サーバー(drunk00)も同時にLDAPクライアントとしても動作するため,サーバー,クライアントの全マシンで以下の設定をします。まず,ユーザー認証方式にLDAP認証を追加するのに必要なソフトをインストールします。

# yum install nss_ldap
# yum install authconfig

ユーザー認証方式にLDAP認証を追加します。ユーザー認証管理用のソフトを起動して以下の設定をします。

なお,万が一設定に間違いがあるとマシンに一切ログインできなくなる可能性もあるため(マシンに(sshなどのリモートではなく)コンソールから直接ログインできる場合はだいじょうぶ),rootでログインした状態のターミナルを1つ維持したまま作業することをお勧めします。

# authconfig-tui
  • 認証の設定でユーザー情報の'LDAPを使用'をチェック
  • 認証の'LDAP認証を使用'をチェック
  • LDAPの設定で'TLSを使用'はチェックしない
  • サーバー: drunk00 (TLSを使わない場合はエイリアス名でかまいません)
  • ベースDN: dc=drunk00,dc=drunks

ここまでで、Linuxでのユーザ認証は従来のアカウントファイル(/etc/passwd)にユーザー情報があれば従来どおりにアカウントファイルでユーザー認証が行われ、アカウントファイルになくてLDAP サーバーにユーザー情報があればLDAPサーバーでユーザー認証が行えるようになっているはずです。

ユーザーの管理を一元化するためには管理用のユーザー(今回はsake)を除いて /etc/passwd に登録された一般ユーザーの情報は抹消し,一般ユーザーの情報はLDAPサーバーにのみあるようにしておいたほうがよいと思います。

Sambaのユーザー認証をLDAPへ移行

Samba はサーバーのみで稼動させているため,以下の作業はサーバーのみで行います。

1) LDAPサーバーの設定

Samba用LDAPスキーマを所定のディレクトリへコピーします。

# cp /usr/share/doc/samba-*/LDAP/samba.schema /etc/openldap/schema/

LDAPサーバー設定ファイル /etc/openldap/slapd.conf に以下を追加します。

include         /etc/openldap/schema/samba.schema

access to attrs=SambaLMPassword
        by self write
        by dn="cn=Manager,dc=drunk00,dc=drunks" write
        by anonymous auth
        by * none
access to attrs=SambaNTPassword
        by self write
        by dn="cn=Manager,dc=drunk00,dc=drunks" write
        by anonymous auth
        by * none

LDAPサーバーを再起動します。

# /sbin/service ldap restart

2) Sambaの設定

Sambaの設定ファイル /etc/samba/smb.conf に以下を追加します。

admin users = Administrator
passdb backend = ldapsam:ldap://localhost
ldap suffix = dc=drunk00,dc=drunks
ldap admin dn = cn=Manager,dc=drunk00,dc=drunks
ldap user suffix = ou=People
ldap group suffix = ou=Group
ldap machine suffix = ou=Computers
ldap passwd sync = yes

LDAPサーバー管理者パスワードをSambaへ登録します。

# smbpasswd -w LDAPサーバー管理者パスワード

Sambaを再起動します。

# /sbin/service smb restart

3) Samba用LDAPサーバー操作ツールのインストール

Sambaのインストール時に作成されているSamba用LDAPサーバー操作ツール(スクリプト)を /usr/local/sbin へコピーします。

# cp /usr/share/doc/samba-*/LDAP/smbldap-tools-*/smbldap-populate /usr/local/sbin/
# cp /usr/share/doc/samba-*/LDAP/smbldap-tools-*/smbldap-passwd /usr/local/sbin/
# cp /usr/share/doc/samba-*/LDAP/smbldap-tools-*/smbldap-user* /usr/local/sbin/
# cp /usr/share/doc/samba-*/LDAP/smbldap-tools-*/smbldap-group* /usr/local/sbin/
# cp /usr/share/doc/samba-*/LDAP/smbldap-tools-*/smbldap_tools.pm /usr/local/sbin/

Samba用LDAPサーバー操作ツールに実行権限を付加します。

# chmod +x /usr/local/sbin/smbldap-*

Samba用LDAPサーバー操作ツールの設定ファイルを /etc/smbldap-tools へコピーします。

# mkdir /etc/smbldap-tools
# cp /usr/share/doc/samba-*/LDAP/smbldap-tools-*/smbldap*.conf /etc/smbldap-tools/

Samba用LDAPサーバー操作ツールの設定ファイルのアクセス権を変更します。

# chmod 644 /etc/smbldap-tools/smbldap.conf
# chmod 600 /etc/smbldap-tools/smbldap_bind.conf

必要な perl モジュールをインストールします。(下のようにsmbldap-toolsをyumでインストールすれば自動的にインストールされるのでそのほうが楽かもしれません。)

# yum install perl-LDAP
# yum install perl-Net-SSLeay
# yum install perl-IO-Socket-SSL
# yum install perl-Crypt-SmbHash
# yum install perl-Digest-SHA1
# yum install perl-Unicode-MapUTF8 --enablerepo=fedora-extras

Samba用LDAPサーバー操作ツールのセットアップは,FC6用のリポジトリ fedora-extra からSamba用LDAPサーバー操作ツール(smbldap-tools)をインストールことででもできます。この場合,上記の perl-LDAP など不足しているものがあれば自動的にインストールされます。また,操作ツールのスクリプトは /usr/sbin に,設定ファイルは /etc/smbldap-tools にインストールされます。

# yum install smbldap-tools --enablerepo=fedora-extras

4) Samba用LDAPサーバー操作ツールの設定

LDAPサーバーのSID取得

# net getlocalsid
SID for domain DRUNKER00 is: S-1-5-21-##########-##########-##########

Samba用LDAPサーバー操作ツール設定ファイル /etc/smbldap-tools/smbldap.conf を編集する。

(General Configuration セクション)
SID='S-1-5-21-##########-##########-##########';  (netコマンドで取得したSID)

(LDAP Configuration セクション)
ldapTLS="0"
suffix="dc=drunk00,dc=drunks"
sambaUnixIdPooldn="sambaDomainName=DRUNKER00,${suffix}"
hash_encrypt="MD5"
#crypt_salt_format="%s" (コメントアウト)

(Unix Accounts Configuration セクション)
defaultUserGid="100" (デフォルトを users(gid=100) にする)
#defaultMaxPasswordAge="45" (コメントアウト:パスワードの有効期限の無効化)

(SAMBA Configuration セクション)
userSmbHome=""
userProfile=""
#userHomeDrive="H:" (コメントアウト)
#userScript="logon.bat" (コメントアウト)
#mailDomain="idealx.com" (コメントアウト)

Samba用LDAPサーバー操作ツール設定ファイル /etc/smbldap-tools/smbldap_bind.conf を編集する。

#slaveDN="cn=Manager,dc=idealx,dc=org"
#slavePw="secret"
masterDN="cn=Manager,dc=drunk00,dc=drunks"
masterPw="パスワード" (平文で書きます。ファイルのアクセス権の変更を忘れずに)

5) Samba用LDAPサーバー初期情報を登録

Samba用LDAPサーバーを初期化します。

# smbldap-populate

Administrator(NTドメイン管理者)のパスワードを設定します。

# smbldap-passwd Administrator

既存のSambaアカウントをLDAPへ移行します。

# pdbedit -i smbpasswd:/etc/samba/smbpasswd -e ldapsam:ldap://localhost

デフォルトのユーザーグループ users を Samba に登録します。

# smbldap-groupmod -a users

Samba用LDAPサーバー操作ツール(smbldap-tools)によるユーザー管理

システムのユーザー管理用コマンド(useraddなど)の頭に'smbldap-'をつけたコマンドにより管理を行います。用意されているものは次の通りです。コマンドのオプションなどは元のコマンドを参考にして下さい。特有のオプションとしては'-a'があります。これは'smbldap-useradd','smbldap-usermod','smbldap-groupadd','smbldap-groupmod'などでSambaのユーザー(グループ)の属性を付与するものです。

smbldap-useradd ユーザーの新規作成
smbldap-userdel ユーザーの削除
smbldap-userinfo ユーザー情報変更
smbldap-usermod ユーザー情報変更
smbldap-groupadd グループの新規作成
smbldap-groupdel グループの削除
smbldap-groupmod うループの情報変更
smbldap-passwd ユーザーのパスワード変更
smbldap-usershow ユーザーの登録情報の表示
smbldap-groupshowグループの登録情報の表示

例えば,ユーザーを追加(Sambaにも登録)し,パスワードを設定するには次のようにします。

# smbldap-useradd -a -m beer
# smbldap-passwd beer
Changing UNIX and samba passwords for beer
New password:
Retype new password:

ユーザーの抹消は次の通りです。

# smbldap-userdel -r beer

ここまでの設定では,smbldap-toos はサーバー上でのみ使えるので,ユーザーの管理はサーバー上でのみ行う必要があります。

一般ユーザーのパスワード管理

この状態で一般ユーザーがパスワードをしようと思ったときに使えるコマンドと変更されるパスワードの関係は下の通りです。ユーザー情報の変更を行う usermod と smbldap-usermod の関係も passwd と smbldap-passwd の関係に同じです。

コマンド実行できる場所変更されるパスワード
サーバークライアントシステム
(LDAP上)
Samba
passwd×
smbpasswd×
smbldap-passwd×

一般ユーザーの数が少なく目の届く範囲にしかいないのであれば,ユーザーにサーバー上で smbpasswd か smbldap-passwd(一般ユーザーは通常 /usr/local/sbin には PATH が通っていないので /usr/local/sbin/smbldap-passwd と実行)を使うように十分周知しておけばよいでしょう。

クライアントマシンに smbldap-tools をインストール

やはりクライント上でもユーザー管理や一般ユーザーのパスワード変更などを行いたい場合は,以下の手順でクライアントに smbldap-tools をインストールします。

まず,サーバーから smbldap-tools の設定ファイルをコピーしてきます。スクリプトは /usr/local/sbin に配置されていれば NFS により共有されているので設定だけすれば使えます。(サーバーに yum で smbldap-tools をインストールした場合はクライアントにも同様にインストールします。)

# mkdir /etc/smbldap-tools
# cd /etc/smbldap-tools
# scp sake@drunk00:/etc/smbldap-tools/smbldap.conf .
# scp sake@drunk00:/usr/share/doc/samba-*/LDAP/smbldap-tools-*/smbldap_bind.conf .
# chmod 600 /etc/smbldap-tools/smbldap_bind.conf

必要な perl モジュールをインストールします。(yum で smbldap-tools をインストールすれば自動的にインストールされるのでそのほうが楽かもしれません。)

# yum install perl-LDAP
# yum install perl-Net-SSLeay
# yum install perl-IO-Socket-SSL
# yum install perl-Crypt-SmbHash
# yum install perl-Digest-SHA1
# yum install perl-Unicode-MapUTF8 --enablerepo=fedora-extras

設定ファイル /etc/smbldap-tools/smbldap.conf を編集します。(サーバー(drunk00)ではmasteLDAP,slaveLDAPとも"127.0.0.1"(localhost)になっていますが,クライアントマシンと同じ以下の設定でもかまいません。)

slaveLDAP="drunk00"
masterLDAP="drunk00"

設定ファイル /etc/smbldap-tools/smbldap_bind.conf も編集します。(サーバーと同じ)

#slaveDN="cn=Manager,dc=idealx,dc=org"
#slavePw="secret"
masterDN="cn=Manager,dc=drunk00,dc=drunks"
masterPw="パスワード" (平文で書きます。ファイルのアクセス権の変更を忘れずに)

これで,どのクライアント上でも smbldap-tools を使用可能です。ただし,NFS の設定で root はクライアントマシンから NFS で共有されたファイルを操作できないようにしてあるので,'useradd -m'や'userdell -r'などは失敗します。

LDAPサーバーとの通信にTLSを利用する

以上の設定では,LDAPクライアントとLDAPサーバーの通信は暗号化されていません。Samba はサーバー上にのみあるし,Samba用LDAPサーバー操作ツール(smbldap-tools)をLDAPサーバーが稼動しているサーバー(drunk00)だけにインストールしている場合は,それらは localhost(127.0.0.1) を呼び出すだけなのでよしとするにしても,クライアントマシンのユーザー認証時やクライアントマシンで smbldap-tools を使用したときに暗号化されていない通信が行われるのは,いくらセキュリティにはあまり配慮しなくてもよしとするといってもやや不安です。そこでLDAPサーバーとの通信に暗号化されたTLSを使うように設定を変更します。

まあ,PCが1台だけでLDAPの導入がシステムとSambaのユーザー管理の統合だけにあるならあまり問題ないでしょうし,今回のように管理用のサーバーの内側に計算用のクライアントが配置されている場合はそこまで気を使う必要もないかもしれません。

まず,インストールされていなければサイト証明書等を作成するのに必要な openssl をインストールします。

# yum install openssl

サーバー証明書の作成

1) 自己認証局(プライベートCA)の構築

opensslの設定ファイル /etc/pki/tls/openssl.cnf 次のように編集し,デフォルトの設定を変更してしまいます。

[ CA default ]
default_days    = 3650 (10年にしてしまいます)

[ req_distinguished_name ]
countryName_default             = JP
stateOrProvinceName_default     = Tokyo
localityName_default            = (空欄にする)
0.organizationName_default      = Private

自己認証局を構築するためのスクリプト /etc/pki/tls/misc/CA を編集し,デフォルトの環境変数を変更してしまいます。

DAYS="-days 3650"       # 10 years
CADAYS="-days 3650"     # 10 years

opensslのインストール時に作成された古いCAを削除します。

# rm -rf /etc/pki/CA

自己認証局(プライベートCA)を構築します。国名(Country Name),県名(State or Province Name),組織名(Organization Name),共有名(Common Name)は空欄にできません。

# cd /etc/pki/tls/misc
# ./CA -newca
CA certificate filename (or enter to create)
[Enter]
Making CA certificate ...
Generating a 1024 bit RSA private key
...++++++
...........................++++++
writing new private key to '../../CA/private/./cakey.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [JP]:[Enter]
State or Province Name (full name) [Tokyo]:[Enter]
Locality Name (eg, city) []:[Enter]
Organization Name (eg, company) [Private]:[Enter]
Organizational Unit Name (eg, section) []:[Enter]
Common Name (eg, your name or your server's hostname) []:drunk00.drunks
Email Address []:[Enter]

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:[Enter]
An optional company name []:[Enter]
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for ../../CA/private/./cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
...
Write out database with 1 new entries
Data Base Updated

2) サーバーの秘密鍵とサイト証明書署名要求の作成

以下の作業は /etc/pki/CA/certs/server で行います。

# mkdir /etc/pki/CA/certs/server
# cd /etc/pki/CA/certs/server

サーバーの秘密鍵を作成します。

# openssl genrsa -des3 -out server_withpass.key 1024
Generating RSA private key, 1024 bit long modulus
...........++++++
.................++++++
e is 65537 (0x10001)
Enter pass phrase for server_withpass.key:(パスワード入力)
Verifying - Enter pass phrase for server_withpass.key:(パスワード入力)

できた秘密鍵(server_withpass.key)から,パスフレーズを削除した秘密鍵(server.key)を作成します。

# openssl rsa -in server_withpass.key -out server.key
Enter pass phrase for server_withpass.key:(パスワード入力)
writing RSA key

サイト証明書署名要求(csr.pem)を作成します。同一ホストの自己認証局を利用する場合,以下の情報はCA構築時とまったく同じ内容で作成しなければいけません。

# openssl req -new -key server_withpass.key -out csr.pem
Enter pass phrase for server_withpass.key:(パスワード入力)
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
CCountry Name (2 letter code) [JP]:[Enter]
State or Province Name (full name) [Tokyo]:[Enter]
Locality Name (eg, city) []:[Enter]
Organization Name (eg, company) [Private]:[Enter]
Organizational Unit Name (eg, section) []:[Enter]
Common Name (eg, your name or your server's hostname) []:drunk00.drunks
Email Address []:[Enter]

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:[Enter]
An optional company name []:[Enter]

3) CAによるサーバー証明書の作成

上で作成したサーバーの署名要求にCAが署名し,サーバーの証明書を作成します。以下の作業は /etc/pki/tls/misc で行います。

# cd /etc/pki/tls/misc/
# openssl ca -in ../../CA/certs/server/csr.pem -out ../../CA/certs/server/server.pem
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for ../../CA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
...
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

LDAPサーバーの設定

LDAPサーバー設定ファイル /etc/openldap/slapd.conf を編集して以下を追加します。

TLSCACertificateFile /etc/pki/CA/cacert.pem
TLSCertificateFile /etc/pki/CA/certs/server/server.pem
TLSCertificateKeyFile /etc/pki/CA/certs/server/server.key

LDAPサーバーを再起動します。

# /sbin/service ldap restart

LDAPクライアントの設定

サーバー上のCAの証明書ファイル cacert.pem を,クライアントの /etc/openldap/cacerts にコピーします。

サーバーマシン(drunk00)では

# cp /etc/pki/CA/cacert.pem /etc/openldap/cacerts/

クライアントマシンでは(sakeは管理用ユーザー)

# scp sake@drunk00:/etc/pki/CA/cacert.pem /etc/openldap/cacerts/

認証方法の設定をします。(サーバーマシンではサーバー名は localhost のままでも可)

# authconfig-tui
  • LDAPの設定で'TLSを使用'をチェック
  • サーバー: drunk00.drunks(LDAPサーバーのサーバーのサイト証明書に同じ)

Sambaの設定

Samba の認証を TLS に対応させるには,Samba の設定ファイル /etc/samba/smb.conf に以下を追加します。

ldap ssl = start_tls

もし,LDAPサーバーとSambaが別のマシンで動いている場合は, /etc/samba/smb.conf の次の部分を正しく設定します。

passdb backend = ldapsam:ldap://drunk00.drunks(LDAPサーバーのサイト証明書に同じ)

Sambaを再起動します。

# /sbin/service smb restart

smbldap-tools の設定

smbldap-tools をクライアントマシンでも使い,smbldap-toolsを TLS に対応させるには,ツールを使うクライアントのサイト証明も必要になります。具体的な作業は次のようになります。

クライアントにもサーバーと同様にCAを構築します。実行前に忘れずに設定ファイル /etc/pki/tls/openssl.cnf と CA構築のスクリプト /etc/pki/tls/misc/CA の編集も行います。

# rm -rf /etc/pki/CA
# cd /etc/pki/tls/misc
# ./CA -newca

クライアントの秘密鍵(client.key)とサイト証明書署名要求(clcsr.pem)を作成します。クライアントの秘密鍵もパスフレーズを削除しておかないと smbldap-tools を使うたびにパスフレーズを聞かれます。

# mkdir /etc/pki/CA/certs/client
# cd /etc/pki/CA/certs/client
# openssl genrsa -des3 -out client_withpass.key 1024
# openssl rsa -in client_withpass.key -out client.key
# openssl req -new -key client_withpass.key -out clcsr.pem

署名要求にCAが署名し,クライアントのサイト証明書(client.pem)を作成します。

# cd /etc/pki/tls/misc/
# openssl ca -in ../../CA/certs/client/clcsr.pem -out ../../CA/certs/client/client.pem

smbldap-tools の設定ファイル /etc/smbldap-tools/smbldap.conf を次のように編集します。

slaveLDAP="drunk00.drunks"(LDAPサーバーのサイト証明書に同じ)
masterLDAP="drunk00.drunks"(LDAPサーバーのサイト証明書に同じ)

ldapTLS="1"
verify="require"
cafile="/etc/openldap/cacerts/cacert.pem"
clientcert="/etc/pki/CA/certs/client/client.pem"
clientkey="/etc/pki/CA/certs/client/client.key"
このページの先頭

last update on Feb. 18, 2008

リモート・シェルの設定

以下で設定するジョブスケジューラ TORQUE やクラスタでの並列計算に用いる mpich では,管理ノード(サーバーマシン)および各サブノード(クライアントマシン)の間でリモード・シェル (rsh あるいは SSH) が動作可能な環境である必要があります。しかもrsh,SSH 共に 「パスワード(パスフレーズ)認証無し」 でアクセスできる環境でなければなりません。

もちろんセキュアな環境を目指すならSSHを使うという選択肢もありますが,今回は計算機用クライアントは簡易とはいえサーバーのファイアウォールの内側にありますし,クラスタにおいては SSH の環境では通信のオーバヘッドが rsh に比較して大きいようなので,今回はrshを用いることにします。

rshの設定

以下の設定は基本的に管理ノードを含む全ノードで行います。

まず,rsh をインストールします。

# yum install rsh rsh-server

rshによるアクセスを許可するマシン(ホスト)の設定を行います。/etc/host.equiv に次のように記述します。この場合,記述されたマシンごとアクセスを許可したことになります。ユーザーごとに許可する場合は各ユーザーのホームディレクトリに .rhosts を作り同様に記述します。(.rhosts ではホスト名の後に [tab] で区切って許可するユーザー名を記述できます。)

drunk00
drunk01
drunk02

xinetd サービスの設定を行います。/etc/xinetd.d/rsh を次のように編集します。

service shell
{
...
  disable = no
}

xinetd を再起動します。

# /sbin/service xinetd restart

root にも rsh を許可する

/etc/host.equiv では root の rsh によるアクセスは許可されません。設定は以下の手順で可能で,設定しておくと何かと便利ですがセキュリティ上はどでかいセキュリティホールとなるのであまりおすすめではありません

まず,リモートアクセスを受け入れるマシン(ホスト)の /root に /etc/hosts.equiv と同じ内容で .rhosts を作成します。(/etc/hosts.equiv をコピーしてくればよい)

# cp /etc/hosts.equiv /root/.rhosts

/etc/securetty に rsh を追加します。

# echo rsh >> /etc/securetty

こうしておけば管理ノード(サーバー)から計算ノード(クライアント)の設定や再起動などが可能です。ただし,前述のようにセキュリティ上は極めてよろしくないので,管理ノードをゲートウェイにして計算ノードはその内側に配置する場合などを除いて設定しないほうがよいと思います。

参考:ssh をパスワード無しで使う

すべてのPCを並列に1つ上位のネットワークの下に並列にぶら下げる場合など,rsh では不安な場合には SSH を使うこともできます。SSH をパスワード(パスワード)無しで使うには,公開鍵暗号方式というのを用いて認証を行います。ssh を使うことで通信は暗号化されるはずですが,乗っ取られたらそのユーザーの権限でできることは何でもできてしまうので,特に秘密鍵の管理には注意しましょう。ここで書いたものはこうやったら動いたというだけで,セキュリティ上好ましくないものを含んでいる可能性も大きいので注意してください。

サーバー上で秘密鍵(id_dsa)と公開鍵(id_dsa.pub)を作ります。作業は各ユーザーが ~/.ssh(一度でもsshを使ったら作成されているはず)で行います。

$ mkdir ~/.ssh (なかったら)
$ chmod 700 ~/.ssh (他人は読み取れないようにする)
$ cd ~/.ssh
$ ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/home/wine/.ssh/id_dsa):[Enter]
Enter passphrase (empty for no passphrase):[Enter](パスフレーズは入力しない)
Enter same passphrase again:[Enter]
Your identification has been saved in /home/wine/.ssh/id_dsa.
Your public key has been saved in /home/wine/.ssh/id_dsa.pub.
The key fingerprint is:
##:##:##:##:##:##:##:##:##:##:##:##:##:##:##:## wine@drunk00.drunks

公開鍵をリモートアクセスを受け入れるマシンに持っていきます(今回は /home を共有しているので必要ありません)。例えば scp を使うなら以下のようにします。

$ mkdir ~/.ssh (なかったら)
$ chmod 700 ~/.ssh (他人は読み取れないようにする)
$ scp drunk00:~/.ssh/id_dsa.pub ~/.ssh/

リモートアクセスを受け入れる側(ホスト)で公開鍵を登録します。/home を共有していない場合は,すべてのホストで行います。今回のように /home を共有している場合はどれか1つのノードで作業すればOKです。(公開鍵にはホスト名が書かれていますが,この設定だけで 計算ノード → 管理ノード の方向のリモートアクセスも可能になるようです。)

$ cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys2
$ chmod 600 ~/.ssh/authorized_keys2 (他人は読み取れないようにする)

以下で述べる TORQUE や mpich で ssh,scp を用いる場合,管理ノードを含むすべてのノードでお互いに ssh,scp が利用可能である必要があります。

また,~/.ssh/known_hosts に各ノードがホスト名で登録されていないと(エイリアス名ではNG)うまくいかないようなので,一度sshでログインしておきます。今回は /home 以下は共有されているので管理ノードを含む各ノードにログインして次のようにでもしておけばいいでしょう。(もしパスワードを聞かれたら設定に失敗しています。)

$ ssh `hostname`
The authenticity of host 'drunk00.drunks (192.168.0.100)' can't be established.
RSA key fingerprint is ae:##:##:##:##:##:##:##:##:##:##:##:##:##:##:##.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'drunk00.drunks' (RSA) to the list of known hosts.
Last login: ...
このページの先頭

last update on Jan. 8, 2008

TORQUEによるジョブ管理

複数の計算ノード(マシン)でクラスタを組んでいる場合など,どのノードが空いているかを管理し, ユーザーが投入するジョブを適切なノードで実行するためのソフトウェアをスケジューラといいます。

今回は, スケジューラに Open PBS をもとに開発されているフリーのパッケージである TORQUE(Tera-scale Open-source Resource and QUEue manager)を用いることにします。以下の設定は,基本的には備忘録/torqueを参考にしています。

TORQUEのインストールと設定

TORQUEのインストール

fedora-extras にも登録されているので yum を用いたインストールも可能ですが,うまく設定できる自信がないので今回はソースからコンパイルすることにします。

まず,Cluster Resources 社TORQUE Resource Managerからソースをダウンロードして展開し,できたディレクトリに移動します。

$ tar zxvf torque-2.1.10
$ cd torque-2.1.10

configure,makeと進みます。configure時にはインストール先(/opt/torque)も指定しておきます。また,ノード間のファイルのコピーにrcp(rshを使ったファイルのコピー)を用いるように指定します。デフォルトでは scp になるようですが,ssh がパスワード認証なしで動作するように設定しないと,TORQUEでジョブを投入した際に結果やエラーメッセージの転送に失敗します。(上述のように各ユーザーがパスワード無しでsshを使えるように設定するならば,configure時の'--with-rcp=/usr/bin/rcp'は必要ありません。)

$ ./configure --prefix=/opt/torque --with-rcp=/usr/bin/rcp
$ make

管理ノードではビルドされたすべてのコンポーネントをインストールします。

# make install

他のノードではビルドされたすべてのコンポーネントをインストールする必要がないので次のようにします。(ビルドを行ったディレクトリは共有されているのでサブノードであらためてビルドする必要はありません。)

# make install_mom install_clients

管理ノード・サブノード共通の設定

/etc/servicesに以下の行を追加します。

# services for OpenPBS (torque)
pbs             15001/tcp       # pbs server (pbs_server)
pbs_mom         15002/tcp       # mom to/from server
pbs_resmom      15003/tcp       # mom resource management requests
pbs_resmom      15003/udp       # mom resource management requests
pbs_sched       15004/tcp       # scheduler

pbs起動スクリプト /etc/init.d/pbs を以下の内容で作成します。

#!/bin/bash
#
# pbs   This script will start and stop the PBS daemons
#
# chkconfig: 345 85 85
# description: PBS is a batch versitle batch system for SMPs and clusters
#

# Source the library functions
. /etc/rc.d/init.d/functions
PBS_HOME=/var/spool/torque

# let see how we were called
case "$1" in
        start)
                echo "Starting PBS daemons: "
                if [ -x /opt/torque/sbin/pbs_mom ] ; then
                        if [ -f $PBS_HOME/mom_priv/config ] ; then
                        echo -n "Starting pbs_mom: "
                        daemon /opt/torque/sbin/pbs_mom
                        echo
                        fi
                fi
                if [ -x /opt/torque/sbin/pbs_sched ] ; then
                        if [ -d $PBS_HOME/sched_priv ] ; then
                        echo -n "Starting pbs_sched: "
                        daemon /opt/torque/sbin/pbs_sched
                        echo
                        fi
                fi
                if [ -x /opt/torque/sbin/pbs_server ] ; then
                        if [ -d $PBS_HOME/server_priv ] ; then
                        echo -n "Starting pbs_server: "
                        daemon /opt/torque/sbin/pbs_server -a true
                        echo
                        fi
                fi
        ;;
        stop)
                echo "Shutting down PBS: "
                if [ -x /opt/torque/sbin/pbs_server ] ; then
                        if [ -d $PBS_HOME/server_priv ] ; then
                        echo -n "Stopping pbs_server: "
                        killproc pbs_server
                        echo
                        fi
                fi
                if [ -x /opt/torque/sbin/pbs_sched ] ; then
                        if [ -d $PBS_HOME/sched_priv ] ; then
                        echo -n "Stopping pbs_sched: "
                        killproc pbs_sched
                        echo
                        fi
                fi
                if [ -x /opt/torque/sbin/pbs_mom ] ; then
                        if [ -f $PBS_HOME/mom_priv/config ] ; then
                        echo -n "Stopping pbs_mom: "
                        killproc pbs_mom
                        echo
                        fi
                fi
        ;;
        status)
                status pbs_server
                status pbs_mom
                status pbs_sched
        ;;
        restart)
                echo "Restarting PBS"
                $0 stop
                $0 start
                echo "done. "
        ;;
        *)
                echo "Usage: pbs { start | stop | restart | status }"
                exit 1
esac

起動スクリプトとして登録します。

# /sbin/chkconfig --add pbs
# /sbin/chkconfig pbs on

以下の内容で /etc/profile.d/pbs.sh を作成しておくと,各ユーザーが設定しなくても TORQUE の利用に必要な設定がユーザーのログイン時に行われます。

PATH=/opt/torque/bin:$PATH
PATH=/opt/torque/sbin:$PATH
export PATH

管理ノードの設定

/var/spool/torque/server_priv/nodes を下記のように作成して,ジョブを実行するノード(今回は管理ノードでも実行することにします)を登録します。

drunk00
drunk01
drunk02

/var/spool/torque/sched_priv/sched_config の設定を次のように変更します。

load_balancing: true    ALL
sort_by: no_sort        ALL

サーバプロセスを起動してデータベースファイルの初期化を行います。

# /opt/torque/sbin/pbs_server -t create

キューの設定に用いるファイル dque.con を次の内容で作成しておきます。

#
# Create and define queue dque
#
create queue dque
set queue dque queue_type = Execution
set queue dque enabled = True
set queue dque started = True
#
# Set server attributes.
#
set server scheduling = True
set server default_queue = dque
set server log_events = 511
set server mail_from = adm
set server scheduler_iteration = 600

キューを設定します。

# /opt/torque/sbin/qmgr < dque.con

一旦 pbs_server を停止します。

# killall pbs_server

実行ノードの設定

今回は管理ノードでもプログラムを実行するので,管理ノードとサブノードの両方で以下の設定をします。

/var/spool/torque/mom_priv/config に以下を記述します。

$logevent 0x1ff
$clienthost drunk00
$ideal_load 1.0
$max_load 1.2

/var/spool/torque/server_name に管理ノードのホスト名を記述します。

drunk00

プロセスの起動

各ノードで次の通り実行して必要なプロセスを起動します。

# /sbin/service pbs start

各ノードの状態は次のようにして確認します。

$ pbsnodes -a
drunk00
     state = free
     np = 1
     ntype = cluster
     status = opsys=linux,uname=Linux drunk00.drunks ...

drunk01
     state = free
     np = 1
     ntype = cluster
     status = opsys=linux,uname=Linux drunk01.drunks ...

drunk02
     state = free
     np = 1
     ntype = cluster
     status = opsys=linux,uname=Linux drunk02.drunks ...

ジョブの管理方法

ジョブ管理用のコマンドとして以下が用意されています。

qsub <ジョブスクリプト>ジョブの投入
qstatジョブの状態を表示
qdel <ジョブID>ジョブの削除

ジョブ投入の例

$ qsub test.sh -q dque -l nodes=3:ppn=1

ジョブを確認

$ qstat
Job id              Name             User            Time Use S Queue
------------------- ---------------- --------------- -------- - -----
01.drunk00          test.sh          wine                   0 R dque

ジョブを削除

$ qdel 01

なお,qsubによるジョブの投入では,投入スクリプト内に'#PBS'で始まる行に記入することでもオプションの指定ができます。

このページの先頭

last update on Jan. 8, 2008

並列計算用の環境の構築

並列計算をするとなると,プログラムを作る際にどのノードでどの計算をするかを決め,そのための通信をプログラム中で指示したりしなければなりません。ちょっと聞きかじった範囲ではノード間の通信には MPI を使うのがよいようですが,これらをきちんと勉強してプログラムを作るというのはちょっと試してみようという程度の私には荷が重過ぎます。さらに効率のよく並列化の効果があがるものを作ろうなどというのは気の遠い話です。

そこで何とかある程度自動的にプログラムを並列化してくれるものはないかと探してみたところ・・・,ありました。HPFです。HPF (High Performance Fortran) は,Fortranの拡張として定義される並列言語(自動並列化Fortran コンパイラ)です。ユーザーが最小限の指示文によってデータの分割配置の方法を指定すれば,残る作業(計算の分割と通信の生成)をコンパイラが自動的に行ってくれるそうです。すばらしい!

これまでもそうですが,特にここから先はあまり勉強していませんがとりあえず MPI と HPF が使えるという環境を作ってみます。

MPI環境の構築

並列計算においては各ノード間でデータのやり取りなどが必要になりますが,MPI(Message Passing Interfaced)はこれを実現するメッセージパッシング用のライブラリ規格の一つで,C または Fortran からサブプログラムとして呼び出す形で使います。また,MPICH(MPI Chameleon)はMPI規格を実装したフリーのライブラリの1つで,MPI1.1規格に完全に準拠しているそうです。詳細は開発元の米国アルゴンヌ国立研究所のページあたりを参考にして下さい。

また,MPIにはいくつかの機能が追加あるいは強化されたMPI-2という規格もあり,これに準拠したMPICH2もあります。しかし,MPICH2では計算を実行するノードでデーモンを走らせる必要があったりとめんどうそうなので,今回はMPICH(無印)にします。

同じくMPIを実装したLAM(Local Area Multicomputer)なら CentOS のリポジトリに登録されているので yum でインストールできますが,思ったような設定ができるか(どうせたいしたことはできないけど)不安なので MPICH を手動でインストールします。

MPICHのインストール

MPICH1 のホームページからダウンロードのページに進んでソースをダウンロードして展開し,できたディレクトリに移動します。

$ tar zxvf mpich.tar.gz
$ cd mpich-1.2.7p1

mpich で使用するマシンのホスト名を列挙したファイル展開してできたディレクトリの中の util/machines にある machines.LINUX を編集します。ホスト名に続いて ':'(コロン)で区切って各マシンのプロセッサー数を書くこともできます。なお,ホスト名はエイリアス名でも動くようですがコメントには hostname で表示されるホスト名にしろと書いてあります。(インストール後に設定する場合や,後からマシンを追加したい場合はインストール先のディレクトリ内の share/machines.LINUX にコピーされているのでこれを編集します。)

drunk00.drunks
drunk01.drunks
drunk02.drunks

Fortran90で書かれたプログラムにも対応できるようにFortranのコンパイラを指定(Fortran77のプログラムのコンパイルはg77,Fortran90/95のコンパイルにはgfortran)して configure します。インストール先なども指定しておいたほうがよいと思います。

$ ./configure --with-arch=LINUX --with-comm=shared --with-device=ch_p4 -prefix=/
usr/local/mpich --enable-debug -fc=g77 -f90=gfortran -cc=gcc -c++=g++

Fortran77用のコンパイラもgfortranを使う場合(-fc=gfortran)は,configure の前に環境変数 F77_GETARGDECL=" "(半角スペース) の設定が必要なようです。

$ export F77_GETARGDECL=" "
$ ./configure --with-arch=LINUX --with-comm=shared --with-device=ch_p4 -prefix=/
usr/local/mpich --enable-debug -fc=gfortran -f90=gfortran -cc=gcc -c++=g++

Intel Fortran および C コンパイラを使う場合は次の通りです。(F77_GETARGDECLの設定は必要ありません。)

$ ./configure --with-arch=LINUX --with-comm=shared --with-device=ch_p4 -prefix=/
usr/local/mpich --enable-debug -fc=ifort -f90=ifort -cc=icc -c++=icpc

なお,リモートシェルに ssh を使う場合は(上述のようにパスワード無しで利用できるように設定する必要があります),configure時にオプションに '-rsh=ssh' を追加します。

無事終わればビルドします。

$ make

インストールします。

# make install

MPICH用の設定

MPICHを使うにはインストール先である /usr/local/mpich/bin にPATHを通しておく必要があります。また,デフォルトでは一度に送受信できるデータの大きさが小さいため環境変数 P4_GLOBMEMSIZE を設定しておいたほうがよいと思います。例えば,.bash_profile にでも以下のような設定をしておくとよいでしょう。

#   set environment variable for mpich
PATH=/usr/local/mpich/bin:$PATH
export PATH
P4_GLOBMEMSIZE=268435456
export P4_GLOBMEMSIZE

HPFコンパイラ(fhpf)のインストール

HPF言語は,通常のFortranプログラムに付加する指示文として記述し,DOループ, FORALLループ, 配列構文の繰り返しを単位として並列化を行うものです。ユーザーは簡単な指示文の形式でデータの分散配置を明示的に記述することによってプログラムの並列化を行うことができます。詳細はHPF推進協議会のページなどを参考にして下さい。

フリーで使える HPF コンパイラとしては今回用いる fhpf の他,ADAPTOR(ドイツのGMD),SHPF(オーストリアのVCPC)などがあるようですが,実は後者2つにしてはインストールを試みましたがちょっとかじった程度の知識ではインストールできませんでした。

fhpfのインストール

HPF推進協議会fhpf ダウンロードサービスからLinux用のバイナリをダウンロードして,適当なディレクトリに展開するだけです。(以下の例は,/usr/local/src にソースをダウンロードして /usr/local/fhpf にインストールする場合。)

# cd /usr/local
# tar zxvf src/fhpf_V1.4.4_Linux.tgz

fhpf用の設定

fhpfはMPI環境を必要としますが,それ以外は特に設定の必要はありません。次のようにしてPAHTを通しておくと便利でしょう。

#   set environment variable for fhpf
PATH=$PATH:/usr/local/fhpf
export PATH
このページの先頭

last update on Dec. 28, 2007

HPFを使ってみる

HPFの主要な機能は,データのプロセッサへのマッピングの指定と,HPFコンパイラが自動的には最適な並列化が出来ない場合にプログラマが最適化を促進するための情報を付与する機能です。これらに必要なHPFの構文のほとんどは'!HPF$ 'という接頭文字列から始まる指示文により構成されているので,通常のFortranコンパイラには注釈行として解釈されます。

HPFの詳細な仕様やプログラミングの方法については,HPF推進協議会のサイト内にドキュメントがあるのでそちらを参照して下さい。下の2つあたりから読み始めるといいと思います。

HPFプログラムのコンパイルと実行

コンパイルの手順

fhpfは,HPFのプログラムコードをMPIライブラリの呼び出しを含んだFortranのプログラムコードに変換する source-to-source のコンパイラです。fhpfによって生成されたFortranのコードを通常のMPIプログラムと同じようにコンパイルして実行ファイルを作ります。

例えば,HPFプログラムの test.f90 をコンパイルする場合,まず fhpf を使って Fortranのコード test.mpi.f90 に変換します。(出力ファイル名を指定しなければ,この例のようにもとのファイルの拡張子 .f90 を .mpi.f90 に置き換えたファイル名になります。)

$ fhpf test.f90
fhpf V1.4.3/MPI -- HPF translator for Linux system
test.f90 -> test.mpi.f90
...

MPIライブラリの呼び出しを含むプログラムなので mpif90 でコンパイルします。出力ファイル名を指定しなければ a.out という実行ファイルができます。

$ mpif90 test.mpi.f90

並列プログラムの実行

mpirun を使って並列実行します。(この例では3プロセッサによる並列実行)

$ mpirun -np 3 ./a.out

TORQUEを介して並列計算のジョブを投入するには,まずジョブ投入用のスクリプトを作ります。例えば次のような感じです。

(TORQUEへのジョブ投入用スクリプトの例:pbs.sh)

#!/bin/sh
#PBS -N a.out
#PBS -e ERR
#PBS -o OUT
#PBS -q dque
#PBS -l nodes=3:ppn=1
cd $PBS_O_WORKDIR
NPROCS=`wc -l < $PBS_NODEFILE`
mpirun -machinefile ${PBS_NODEFILE} -np ${NPROCS} ./a.out < a.inp

TORQUEを使った場合,標準入力(キーボード)からの入力ができないので,プログラムが標準入力からの入力を求めるようになっている場合は上のスクリプトにあるように入力用のファイル(a.inp)を作って入力する必要があります。

ジョブを投入してみます。

$ qsub ./pbs.sh
01.drunk00

なお,上のスクリプトの例では標準出力(通常はコンソール画面)への出力があった場合は 'OUT' というファイルに出力されます。

HPFを使ってマトリックスの乗算を並列化

fig3

ためしにHPFを使ってマトリックスとベクトルの積を求めるプログラムを並列化してみます。

例題は右図のようなN層のせん断系に各層に高さに比例して減じていくような層間変形を生じさせる外力を求めるものです。要するに 系の剛性を [K] とし,各層の変位(層間変形ではない)を {d} としたときの,{d} を生じさせる外力 {P}=[K]{d} を求めるだけです。

Fortran90によるプログラム

Fortran90でコーディングするとこんな感じでしょうか。実行時に層数Nをコンソールから入力し,結果は frc.out に出力します。また,fortranの関数 etime によって計測した実行時間を表示するようになっています。

(Fortran90によるプログラムの例:matmlp.f90

program matmlp

  implicit none

  integer                 :: N, i
  real(8), allocatable    :: K(:,:), d(:), P(:)

  real(4)                 :: ttime, etime, tarray(2) ! 時間計測用の変数
  character(7), parameter :: out='frc.out'           ! 結果の出力先

  write(*,*) 'input the size of matrix:'
  read(*,*) N

  allocate( P(1:N), d(1:N), K(1:N,1:N) )

  K(1:N,1:N) = 0.0d0
  K(1  ,1  ) = 1.0d0
  do i=2,N
    K(i-1:i,i-1) =  K(i-1:i,i-1) + (/  1.0d0, -1.0d0 /)
    K(i-1:i,i  ) =  K(i-1:i,i  ) + (/ -1.0d0,  1.0d0 /)
  end do

  do i=1,N
    d(i) = DBLE(i) - DBLE(i*(i-1)) / DBLE(2*N)
  end do

  P = MATMUL(K, d)

  open(61,FILE=out,STATUS='replace',ACTION='write')
  write(61,'(F12.6)') P
  close(61)

  deallocate( P, d, K )

  ttime = etime(tarray)
  write(*,'(/,A15,F8.4)') 'total  time = ', ttime
  write(*,'(  A15,F8.4)') 'user   time = ', tarray(1)
  write(*,'(  A15,F8.4)') 'system time = ', tarray(2)

end program matmlp

コンパイルして層数を1000として実行してみます。(実は結果は P(i) = 1/N になります。)

$ gfortran matmlp.f90 -o matmlp
$ ./matmlp
 input the size of matrix:
1000

 total  time =   0.0330
 user   time =   0.0180
 system time =   0.0120
$ cat frc.out | less
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
:

HPFによるプログラム

では,HPFで並列化してみます。下の例では DISTRIBUTE と ALIGN を使って一次元の配列 P と d は各プロセッサ上にブロック分散し,2次元配列 K も1次元目で分散させています。また,INDEPENDENT 指定の直後のループでは処理の並列化が行われます。また,fhpfによる並列化がスムーズに行われるよう,次の部分の処理の仕方を変えてあります。

  • Kの初期化 → FORALL文を使った代入
  • Kへの値の代入 → ループを2回に分けて実行(代入する向きもKの分散の向きにあわせて変更)
  • MATMUL関数による行列とベクトルの積の計算 → 各行ごとにベクトルとベクトルの内積として計算

(HPFによるプログラムの例:matmlp_hpf.f90

program matmlp_hpf

  implicit none

  integer                 :: N, i, j
  real(8), allocatable    :: K(:,:), d(:), P(:)

  real(4)                 :: ttime, etime, tarray(2) ! 時間計測用の変数
  character(7), parameter :: out='frc.out'           ! 結果の出力先

!HPF$ DISTRIBUTE(block)     :: d
!HPF$ ALIGN (i)   WITH d(i) :: P
!HPF$ ALIGN (i,*) WITH d(i) :: K

  write(*,*) 'input the size of matrix:'
  read(*,*) N

  allocate( P(1:N), d(1:N), K(1:N,1:N) )

!HPF$ INDEPENDENT
  forall(i=1:N,j=1:N)
    K(i,j) = 0.0d0
  end forall

  K(1,1) = 1.0d0
!HPF$ INDEPENDENT
  do i=1,N-1
    K(i,i:i+1) =  K(i,i:i+1) + (/  1.0d0, -1.0d0 /)
  end do
!HPF$ INDEPENDENT
  do i=2,N
    K(i,i-1:i) =  K(i,i-1:i) + (/ -1.0d0,  1.0d0 /)
  end do

!HPF$ INDEPENDENT
  do i=1,N
    d(i) = DBLE(i) - DBLE(i*(i-1)) / DBLE(2*N)
  end do

!HPF$ INDEPENDENT
  do i=1,N
    P(i) = DOT_PRODUCT( K(i,1:N), d(1:N) )
  end do

  open(61,FILE=out,STATUS='replace',ACTION='write')
  write(61,'(F12.6)') P
  close(61)
  
  deallocate( P, d, K )

  ttime = etime(tarray)
  write(*,'(/,A15,F8.4)') 'total  time = ', ttime
  write(*,'(  A15,F8.4)') 'user   time = ', tarray(1)
  write(*,'(  A15,F8.4)') 'system time = ', tarray(2)

end program matmlp_hpf

fhpfで変換してみます。ソースは自由形式で記述してあるのでオプションを指定します。分散した配列について処理に伴って通信が発生する旨のメッセージが出ていますが,無事Fortranのコード matmlp_hpf.mpi.f90 が生成されました。

$ fhpf -Free matmlp_hpf.f90
fhpf V1.4.3/MPI -- HPF translator for Linux system
matmlp_hpf.f90 -> matmlp_hpf.mpi.f90
joo7100i-p "matmlp_hpf.f90" line:21 The loop of DO-variable j is not parallelize
d.
jes3152i-p "matmlp_hpf.f90" line:46 Communication for variable p will be generat
ed around the I/O statement.
jes3151i-p "matmlp_hpf.f90" line:42 Communication for variable d will be generat
ed out of the parallel loop.

mpif90でコンパイルして実行ファイルを作ります。

$ mpif90 matmlp_hpf.mpi.f90 -o matmlp_hpf_mpi

mpirunを使って実行してみます。とうぜんながら同じ結果です。

$ mpirun -np 3 ./matmlp_hpf_mpi
 input the size of matrix:
1000

 total  time =   0.0470
 user   time =   0.0300
 system time =   0.0170
$ cat frc.out | less
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
    0.001000
:

このHPFのソースコードはFortranコンパイラでそのままコンパイルして逐次実行の実行ファイルを作ることもできます。

$ gfortran matmlp_hpf.f90 -o matmlp_hpf_f90
$ ./matmlp_hpf_f90
 input the size of matrix:
1000

 total  time =   0.0470
 user   time =   0.0340
 system time =   0.0130

もっと配列のサイズを大きくして3つの実行速度を比べてみます。う〜ん,同じソースからFortranでコンパイルしたものに対しては並列処理による効果はでて実行速度は向上していますが,もとのFortran90のプログラムに比べると・・・。ただ,この例でも各ノードのメモリに分散している効果は出て,実行中のリソースの状態を確認してみると並列実行では各ノードでのメモリの使用量はもとのプログラムの1/3近くまで減っています。

$ ./matmlp(Fortran90でコーディング)
 input the size of matrix:
5000

 total  time =   0.6339
 user   time =   0.3759
 system time =   0.2580
$ ./matmlp_hpf_f90(HPF用にコードを変えたものをFortranとしてコンパイル)
 input the size of matrix:
5000

 total  time =   2.7986
 user   time =   2.5116
 system time =   0.2870
$ mpirun -np 3 ./matmlp_hpf_mpi(HPFでコーディングしたものを並列実行)
 input the size of matrix:
5000

 total  time =   1.8267
 user   time =   1.1458
 system time =   0.6809

HPF用にコードを変えたものを逐次実行したものがもとのFortran90のプログラムに対して著しく実行速度が低下しているのは,MATMULをDOT_PRODUCTに分割したときに K( i , : ) と d の内積の形にしてしまったためです。素直に展開すればこういう形になるとは思いますが,Fortranでは2次元配列は列に沿ってメモリ上に展開されるため,K( i , : ) ではメモリ上に飛び石状に格納された値を参照しなければならず,処理速度が著しく低下します。

ということで,マトリックスの積を求める部分のアルゴリズムを内積の計算から3項計算に変えてみます。アルゴリズムの変更にあわせて K は2次元目に沿って分散配置,一次元配列の P は分散配置しないことにしました。

(HPFによるプログラムの例2:matmlp_hpf2.f90

program matmlp_hpf2

  implicit none

  integer                 :: N, i, j
  real(8), allocatable    :: K(:,:), d(:), P(:)

  real(4)                 :: ttime, etime, tarray(2) ! 時間計測用の変数
  character(7), parameter :: out='frc.out'           ! 結果の出力先

!HPF$ DISTRIBUTE(block)     :: d
!HPF$ ALIGN (*,i) WITH d(i) :: K

  write(*,*) 'input the size of matrix:'
  read(*,*) N

  allocate( P(1:N), d(1:N), K(1:N,1:N) )

!HPF$ INDEPENDENT
  forall(i=1:N,j=1:N)
    K(i,j) = 0.0d0
  end forall

  K(1,1) = 1.0d0
!HPF$ INDEPENDENT
  do i=1,N-1
    K(i:i+1,i) =  K(i:i+1,i) + (/  1.0d0, -1.0d0 /)
  end do
!HPF$ INDEPENDENT
  do i=2,N
    K(i-1:i,i) =  K(i-1:i,i) + (/ -1.0d0,  1.0d0 /)
  end do

!HPF$ INDEPENDENT
  do i=1,N
    d(i) = DBLE(i) - DBLE(i*(i-1)) / DBLE(2*N)
  end do

  P(1:N) = 0.0d0
!HPF$ INDEPENDENT, REDUCTION(+:P)
  do i=1,N
    P(1:N) = P(1:N) + K(1:N,i) * d(i)
  end do

  open(61,FILE=out,STATUS='replace',ACTION='write')
  write(61,'(F12.6)') P
  close(61)
  
  deallocate( P, d, K )

  ttime = etime(tarray)
  write(*,'(/,A15,F8.4)') 'total  time = ', ttime
  write(*,'(  A15,F8.4)') 'user   time = ', tarray(1)
  write(*,'(  A15,F8.4)') 'system time = ', tarray(2)

end program matmlp_hpf2

まずはFortranでコンパイルして,逐次実行してみます。実行速度はだいぶ改善されています。

$ gfortran matmlp_hpf2.f90 -o matmlp_hpf2_f90
$ ./matmlp_hpf2_f90
 input the size of matrix:
5000

 total  time =   0.8689
 user   time =   0.6009
 system time =   0.2680

では fhpf を使って並列処理の実行ファイルを作成し(中間ソースコード matmlp_hpf2.mpi.f90 もたいぶ短くなりました),実行してみます。3台がかりでかろうじてもとのプログラムを1台で実行した場合より少しだけ速いようになりました。難しいですね・・・。

$ fhpf -Free matmlp_hpf2.f90
fhpf V1.4.3/MPI -- HPF translator for Linux system
matmlp_hpf2.f90 -> matmlp_hpf2.mpi.f90
joo7100i-p "matmlp_hpf2.f90" line:20 The loop of DO-variable i is not paralleliz
ed.
$ mpif90 matmlp_hpf2.mpi.f90 -o matmlp_hpf2_mpi
$ mpirun -np 3 matmlp_hpf2_mpi
 input the size of matrix:
5000

 total  time =   0.5339
 user   time =   0.3319
 system time =   0.2020

まあ,ちょっとかじった程度ではこのあたりが限界です。奥が深いしなかなかおもしろそうなので,暇があったらもう少しつっこんでやってみたいと思います。

このページの先頭

last update on Jan. 5, 2008

OpenMPによるメモリ共有型の並列化

OpenMPは共有メモリ並列計算機におけるマルチスレッド並列プログラミングのためのAPIで,最近ではPCでもデュアルコア,クアッドコアが珍しくなくなってきましたので比較的簡単に並列化を試みることが可能です。幸いにしてgcc-gfortranでも最近のバージョンではOpenMPを実装しているので簡単にプログラミングができます。

メモリ共有型なので必要なメモリの量は逐次処理の場合と同じなのでメモリの節約にはならず,今回はあまり必要はないのですがついでに試してみることにします。

OpenMPは共有メモリ型並列プログラミングのための新しい言語ではなく,Fortran(およびC/C++)の基本言語に対してOpenMP指示文を挿入することでマルチスレッド並列プログラムを開発するものです。指示文は'!$OMP'で始まり,OpenMPコンパイルが無効の場合は注釈行とみなされ無視されます。

また,OpenMPではDOループの並列化だけではなく,タスクの並列化も可能です。

OpenMPの指示構文の書式やプログラムの実行を制御するためのランタイム関数や環境変数については,インテル HPC リソース・センター : 事例とドキュメントに掲載されている「デュアルコア/マルチコア対応アプリケーション開発 OpenMP* 活用ガイド(vol. 1-4)」が参考になると思います。

なお,ランタイム関数や環境変数を用いるのひ必要なヘッダーファイル,あるいはモジュールなどは /usr/lib/gcc/i386-redhat-linux/4.1.1/finclude/ (Intel Fortran の場合は /usr/local/intel/fc/バージョン番号/include/ )にあるようです。必要に応じて /usr/local/include/ などにシンボリックリンクなどを作っておくなどしておくといいかもしれません。

OpenMPを利用したプログラムのコンパイル

gfortran の場合(ver 4.1.2で確認),オプションに'-fopenmp'を指定するだけです。出力ファイル名を指定しなければ a.out という実行ファイルができます。実行は,そのまま a.out を実行するだけです。

$ gfortran test.f90 -fopenmp
$ ./a.out

Intel Fortran の場合は次の通りです。OpenMPによる並列化をするためには'-openmp'を,並列化に関するメッセージの出力レベルの指定に'-opnemp_report[0-2]'を指定します。

$ ifort test.f90 -openmp -openmp_report2
test.f90(15) : (col. 7) remark: OpenMP DEFINED LOOP WAS PARALLELIZED.
test.f90(32) : (col. 7) remark: OpenMP multithreaded code generation for SINGLE 
was successful.
$ ./a.out

OpenMPを使ってマトリックスの乗算を並列化

本来,通常のFortranのプログラムでもコンパイル時に最適化オプション(-O[0-3])を指定すればマトリックスの乗算などはおそらく内部でベクトル化?されているはずなので,わざわざOpenMPなどを使って並列化する必要はない(というよりおそらくパフォーマンスは落ちる)のですが,ここは比較のために上でHPFで並列化したものをOpenMPを利用して書き換えてみます。

なるべくもとの matmlp.f90 のままということで matmul 関数なども使っています。配列 K の初期化(ゼロの代入)にforall文を使っている点と,K への代入のDOループを2つに分割した点が変更点です。

仕様を斜め読みした感じではこれでいい気がするのですが,Intel Fortranを使ってコンパイル時にメッセージを吐き出させるとどうも forall と matmul の部分は並列化されていないみたいです。なぜ???

(OpenMPによるプログラムの例:matmlp_omp.f90

program matmlp_omp

  implicit none

  integer                 :: N, i, j
  real(8), allocatable    :: K(:,:), d(:), P(:)

  real(4)                 :: ttime, etime, tarray(2) ! 時間計測用の変数
  character(7), parameter :: out='frc.out'           ! 結果の出力先

  write(*,*) 'input the size of matrix:'
  read(*,*) N

  allocate( P(1:N), d(1:N), K(1:N,1:N) )

!$OMP PARALLEL WORKSHARE
  forall(i=1:N,j=1:N)
    K(i,j) = 0.0d0
  end forall
!$OMP END PARALLEL WORKSHARE

  K(1,1) = 1.0d0
!$OMP PARALLEL DO
  do i=1,N-1
    K(i:i+1,i) =  K(i:i+1,i) + (/  1.0d0, -1.0d0 /)
  end do
!$OMP END PARALLEL DO
!$OMP PARALLEL DO
  do i=2,N
    K(i-1:i,i) =  K(i-1:i,i) + (/ -1.0d0,  1.0d0 /)
  end do
!$OMP END PARALLEL DO

!$OMP PARALLEL DO
  do i=1,N
    d(i) = DBLE(i) - DBLE(i*(i-1)) / DBLE(2*N)
  end do
!$OMP END PARALLEL DO

!$OMP PARALLEL WORKSHARE
  P = MATMUL(K, d)
!$OMP END PARALLEL WORKSHARE

  open(61,FILE=out,STATUS='replace',ACTION='write')
  write(61,'(F12.6)') P
  close(61)
  
  deallocate( P, d, K )

  ttime = etime(tarray)
  write(*,'(/,A15,F8.4)') 'total  time = ', ttime
  write(*,'(  A15,F8.4)') 'user   time = ', tarray(1)
  write(*,'(  A15,F8.4)') 'system time = ', tarray(2)

end program matmlp_omp

何はともあれコンパイルして実行してみます。

$ gfortran matmlp_omp.f90 -o matmlp_omp -fopenmp
$ ./matmlp_omp
 input the size of matrix:
5000

 total  time =   0.7359
 user   time =   0.4839
 system time =   0.2520

パフォーマンスはいまいちです。最適化オプション(-O3)などを併用すればもっとよくなるとは思いますが,MPIを使う場合には必要だったノード間の通信などはないにしてもスレッドを分割する際にはやはりオーバーヘッドが生じますし,メモリを共有していることからくる制約などがあるのでこの例のように並列化の粒度が小さいとなかなか効率をよくするのは難しいようです。

参考:Intel Fortran Compiler による自動並列化

Intel Fortran ではオプションを指定することで自動並列化が可能です。これはOpenMPと同じようにメモリを共有しているプロセッサ間で複数のスレッドを並列処理するものです。詳細は上でも紹介した インテル HPC リソース・センター 内のドキュメントにありますので参考にして下さい。

使い方は,コンパイル時に '-parallel' を指定するだけです。また,並列化のレベルは '-par_threshold[0-100]' で,コンパイル時のメッセージの出力レベルは '-par_report[0-3]' で制御が可能です。

matmlp.f90 について,並列化による効果の大きさにかかわらず並列化が可能なループをすべて並列化するとこんな感じになります。

$ ifort matmlp.f90 -o matmlp_par -parallel -par_threshold0 -par_report2
   procedure: matmlp
matmlp.f90(16) : (col. 3) remark: LOOP WAS AUTO-PARALLELIZED.
   parallel loop: line 16
   serial loop: line 18
matmlp.f90(19) : (col. 5) remark: LOOP WAS AUTO-PARALLELIZED.
   parallel loop: line 19
matmlp.f90(20) : (col. 5) remark: LOOP WAS AUTO-PARALLELIZED.
   parallel loop: line 20
matmlp.f90(23) : (col. 3) remark: LOOP WAS AUTO-PARALLELIZED.
   parallel loop: line 23
matmlp.f90(27) : (col. 7) remark: LOOP WAS AUTO-PARALLELIZED.
   parallel loop: line 27
   serial loop: line 27
matmlp.f90(27) : (col. 7) remark: LOOP WAS AUTO-PARALLELIZED.
   parallel loop: line 27

実行してみます。

$ ./matmlp_par
 input the size of matrix:
5000

 total  time =   1.1100
 user   time =   0.4100
 system time =   0.7000

自動並列化を指定しないで Intel Fortran でコンパイルしたものの実行時間は 0.4秒程度ですからやはり実行速度は低下しています。闇雲に並列化すればよいわけではないことがよくわかります。(ちなみに,並列化のレベルを指定しないでデフォルトで自動並列化した場合,23行目のDOループのみが並列化されます。)

このページの先頭
Fumio KUSUHARA
Department of Architecture, School of Engneering, The University of Tokyo
kusuhara at rcs.arch.t.u-tokyo.ac.jp
Copyright (c) Fumio KUSUHARA, All rights reserved.