  Linux Parallel Processing HOWTO
  Hank Dietz, pplinux@ecn.purdue.edu
  v980105, 5 January 1998
  高橋 聡 hisai@din.or.jp
  2001/5/15

  並列処理とはプログラムの実行速度を上げる考えの 1 つを指し、プログラム
  を同時に実行可能な複数の部分に分け、それぞれをプロセッサ上で動かしま
  す。プログラムを N 個のプロセッサに分けて実行すれば、 1 つのプロセッサ
  で動かすのに比べて N 倍速く実行できると考えられます。このドキュメント
  では、基本的な 4 つの並列処理の方法を検討します。いずれも Linux ユーザ
  が利用できる方法で、SMP Linux システム、ネットワークでつながっている
  Linux システムでクラスタを組む、マルチメディア命令(すなわち、MMX)を
  使った並列処理、そして Linux システムに(独立して) 搭載されている付加プ
  ロセッサを利用するというのがそれに当たります。
  ______________________________________________________________________

  目次

  1. はじめに
     1.1 何を並列処理に求めるのか？
     1.2 専門用語
     1.3 アルゴリズムの例
     1.4 このドキュメントの構成

  2. SMP Linux
     2.1 SMP のハードウェア
        2.1.1 それぞれのプロセッサは独自の L2 キャッシュを持っているか？
        2.1.2 バスの構成？
        2.1.3 メモリのインタリーブと DRAM の技術？
     2.2 共有メモリを使ったプログラミングをはじめるにあたって
        2.2.1 すべてを共有するのか、一部を共有するのか
           2.2.1.1 すべてを共有する
           2.2.1.2 一部を共有する
        2.2.2 アトミックな処理と処理の順序
        2.2.3 変数の保存のしかた
        2.2.4 ロック
        2.2.5 キャッシュ・ラインの大きさ
        2.2.6 Linux のスケジューラの問題点
     2.3 bb_threads
     2.4 LinuxThreads
     2.5 System V の共有メモリ
     2.6 メモリマップ・システムコール

  3. Linux システムでクラスタを組む
     3.1 なぜクラスタなのか？
     3.2 ネットワーク機器
        3.2.1 ArcNet
        3.2.2 ATM
        3.2.3 CAPERS
        3.2.4 イーサネット
        3.2.5 イーサネット(ファースト・イーサネット)
        3.2.6 イーサネット(ギガビット・イーサネット)
        3.2.7 FC (ファイバー・チャネル)
        3.2.8 FireWire (IEEE 1394)
        3.2.9 HiPPI と Serial HiPPI
        3.2.10 IrDA (Infrared Data Association)
        3.2.11 Myrinet
        3.2.12 Parastation
        3.2.13 PLIP
        3.2.14 SCI
        3.2.15 SCSI
        3.2.16 ServerNet
        3.2.17 SHRIMP
        3.2.18 SLIP
        3.2.19 TTL_PAPERS
        3.2.20 USB (Universal Serial Bus)
        3.2.21 WAPERS
     3.3 ネットワークに対するソフトウェアのインタフェース
        3.3.1 ソケット
           3.3.1.1 UDP プロトコル(SOCK_DGRAM)
           3.3.1.2 TCP プロトコル(SOCK_STREAM)
        3.3.2 デバイス・ドライバ
        3.3.3 ユーザレベルのライブラリ
     3.4 PVM (Parallel Virtual Machine)
     3.5 MPI (Message Passing Interface)
     3.6 AFAPI (Aggregate Function API)
     3.7 クラスタをサポートしているその他のライブラリ
        3.7.1 Condor(プロセス・マイグレーションをサポート)
        3.7.2 DFN-RPC (German Research Network - Remote Procedure Call)
        3.7.3 DQS (Distributed Queueing System)
     3.8 利用可能なクラスタの一覧
        3.8.1 Beowulf
        3.8.2 Linux/AP+
        3.8.3 Locust
        3.8.4 Midway DSM(分散共有メモリ)
        3.8.5 Mosix
        3.8.6 NOW (Network Of Workstations)
        3.8.7 Linux を使った並列処理
        3.8.8 Pentium Pro クラスタのセミナー
        3.8.9 TreadMarks DSM (分散共有メモリ)
        3.8.10 U-Net (User-level NETwork interface architecture)
        3.8.11 WWT (Wisconsin Wind Tunnel)

  4. SIMD Within A Register(例えば MMX を利用)
     4.1 SWAR の何が優れているのか？
     4.2 SWAR プログラミング入門
        4.2.1 ポリモフィックな操作
        4.2.2 分割操作
           4.2.2.1 分割命令
           4.2.2.2 コードを修正して、分割命令を使わない
           4.2.2.3 フィールド値の制御
        4.2.3 値間のやりとりと型変換操作
     4.3 Linux における MMX を使った SWAR

  5. Linux に搭載されている付加プロセッサ
     5.1 優れたホスト、それは Linux PC
     5.2 DSP してみましたか？
     5.3 FPGA と 再構成可能な論理計算回路

  6. 一般的な興味として
     6.1 プログラミング言語とコンパイラ
        6.1.1 Fortran 66/77/PCF/90/HPF/95
        6.1.2 GLU (Granular Lucid)
        6.1.3 Jade と SAM
        6.1.4 Mentat と Legion
        6.1.5 MPL(MasPar Programming Language)
        6.1.6 PAMS(Parallel Application Management System)
        6.1.7 Parallaxis-III
        6.1.8 pC++/Sage++
        6.1.9 SR (Synchronizing Resources)
        6.1.10 ZPL And IronMan
     6.2 パフォーマンスの問題
     6.3 結論―さあ、やりましょう

  7. 日本語版謝辞

  ______________________________________________________________________

  1.  はじめに

  並列処理とはプログラムの実行速度を上げる考えで、プログラムを同時に実行
  可能な複数の部分に分け、それぞれをプロセッサ上で動かします。プログラム
  を n 個のプロセッサに分けて実行すれば、1 つのプロセッサで動かすのに比
  べて n 倍速く実行できると考えられます。

  これまで複数のプロセッサを持つシステムは、特注で設計した「並列コン
  ピュータ」として世の中に存在していました。現状では、これに加えて Linux
  が SMP システム(「サーバ」として販売されていることが多い)を提供してい
  ます。これは 1 台のコンピュータで複数のプロセッサが 1 つのメモリとバス
  のインタフェースを共用しています。もちろんコンピュータを複数用意して (
  例えば Linux が動いている複数の PC を用意する)ネットワークでつないで並
  列処理クラスタを組むこともできます。Linux を使った並列演算の 3 番目の
  方法は、マルチメディア拡張命令(すなわち MMX)を使って、整数データのベク
  トルを並列に扱う方法です。最後は、専用に搭載されている並列処理計算エン
  ジンを利用するのに Linux システムをホストにしてしまう、という方法で
  す。このドキュメントでは、これらすべてのアプローチを詳しく検討していく
  ことにします。
  1.1.  何を並列処理に求めるのか？

  複数のプロセッサは確かに多くの処理を高速にしますが、並列処理によって恩
  恵を受けないアプリケーションも多数存在します。一般的には並列処理で効果
  が出るのは下記の場合に限定されます。

  o  アプリケーションが複数プロセッサを効率的に使用するよう並列化がほど
     こしてあること。ある面で、これはプログラムの一部分が独立して動作で
     き、かつその部分が独立したプロセッサで並列して動作するとみなすこと
     にほかならない。ただし、並列で実行できることが特定のシステムではか
     えって処理速度が遅くなってしまう可能性があることもわかっている。例
     えば、1 つのプロセッサで実行に 4 秒かかるプログラムがあったとする。
     そのプログラムが、4 つのマシンでそれぞれ 1 秒で実行できたとしても、
     これらのマシンが協調して動作するのに 3 秒以上かかってしまえば、結局
     は処理速度が上がったことにはならない

  o  実行したいと思っているアプリケーションが並列化されている(並列処理向
     きに手直ししてある)か、少なくとも並列処理で効果がでるように新たにア
     プリケーションに手をいれるつもりがあること

  o  並列処理に関連する事柄についの研究に興味があるか、少なくとも理解す
     る意志があること。システムで並列処理を使用することは必ずしも困難な
     ことではないが、コンピュータの利用者の大半は並列処理に詳しくなく、
     今のところ「初めての並列処理」というような書籍も存在しない。この
     HOWTO は理解するきっかけとしては恰好のものだが、すべてを網羅してい
     るわけではない

  幸いなことに上記すべてが本当であっても、Linux を利用した並列処理はスー
  パーコンピュータ並のパフォーマンスで複雑な計算や巨大なデータセットを扱
  えます。何よりも、安価なハードウェア(既に所有しているような)で実現でき
  るのが魅力です。さらに良いことには、並列に処理をするほど忙しくなけれ
  ば、並列 Linux システムで他の処理も簡単に実行できます。

  並列処理を望んでいるわけではないが、それなりにパフォーマンスの向上を達
  成したいと思っているなら、打つ手は他にまだあります。例えば、逐次実行す
  るプログラムのパフォーマンスを上げるには、より高速なプロセッサへの換装
  やメモリの増設、ディスクを IDE から Fast Wide SCSI に交換する等の方法
  があります。この点に関心があるなら、セクション 6.2 に進んでください。
  そうでなければこのまま読み進めてください。

  1.2.  専門用語

  並列処理はこれまで様々なシステムで行われてきていますが、コンピュータ
  ユーザの大部分にとって、まだそれほどなじみがあるわけではありません。そ
  こで、いろいろな並列処理について論じる前に、よく使われる用語のいくつか
  を理解しておきましょう。

     SIMD:
        SIMD(Single Instruction stream, Multiple Data stream)は並列実行
        モデルの 1 つで、プロセッサすべてが同じ演算を同時に行う。しか
        し、それぞれのプロセッサは固有のデータしか扱えない。そもそもこの
        モデルは、配列の各要素に対して同様な演算するという、ベクトルや配
        列に関連した操作を得意とする。理由は、演算すべてがもともと同期し
        ていて、SIMD プロセッサ間のやりとりが簡単かつ効率良く実装しやす
        いからである

     MIMD:
        MIMD(Multiple Instruction stream, Multiple Data stream)は並列実
        行モデルの 1 つで、基本的にプロセッサそれぞれは独立して動作す
        る。そもそもこのモデルは複数のプログラムを利用して、機能ベースで
        並列に実行することに長けている。例えば、あるプロセッサがデータ
        ベースの更新を行っている間、他のプロセッサは新たに入力された画像
        の表示を処理しているという具合に。このモデルは SIMD よりも自由度
        が高いが、悪夢のデバックが必要となる競合状態に陥る可能性があり、
        プログラムが断続的に落ちる危険性がある。これはプロセッサが再実行
        を行うタイミングの問題で、他のプロセッサのタイミングとの関係に
        よって生じる問題である

     SPMD:
        SPMD(Single Program, Multiple Data)は機能を限定した MIMD で、プ
        ロセッサすべてが同じプログラムを実行する。SIMD とは異なり、SPMD
        のコードを実行するプロセッサはプログラム中でそれぞれ異なった制御
        の流れをとる

     通信帯域幅 (Communication Bandwidth):
        通信系の帯域幅とは、転送がはじまってから単位時間当たりに転送でき
        る最大のデータ量を表す。シリアル接続の帯域幅は ボー(baud) やビッ
        ト/秒 (b/s)で表すのが普通だが、これはバイト/秒 (B/s)のだいたい
        1/10 から 1/8 に当たる。例えば 1200 ボーのモデムは、約 120 ビッ
        ト/秒伝送し、155 M ビット/秒の ATM 回線は、モデムの 130,000 倍速
        く、17 M ビット/秒伝送できる。帯域が広ければ大きなブロックのデー
        タを伝送することができ、プロセッサ間で効率良くデータがやり取りで
        きる

     通信遅延(Communication Latency):
        通信系の遅延は、ある「もの」を伝送するのにかかる最小時間である。
        これには送受信にかかるソフトウェアのオーバーヘッドも含まれてい
        る。並列処理にとって遅延はとても重要であり、これが有効な最小の粒
        度を決定する。コード単位を実行している時間が短いほど、並列実行が
        高速になる。基本的に、コード単位を実行する時間がその実行結果を返
        す時間(例えば、遅延)より短ければ、プロセッサ上で返り値を必要とす
        る連続したコードの実行は、並列実行より速くなるはずである。連続し
        た処理の実行は、通信のオーバーヘッドを避ける必要がある

     メッセージ通信(Message Passing):
        メッセージ通信は、並列システム内でプロセッサ間でやりとりするモデ
        ルの 1 つである。普通はあるプロセッサ上のソフトウェアがメッセー
        ジを作成し、相互に接続しているネットワークを経由して送られる。送
        られたメッセージは受信され、メッセージの内容を実行する必要があ
        る。メッセージを扱うオーバーヘッド(遅延)が大きいかもしれないが、
        通常はメッセージにどのくらい情報を含められるかについてはほとんど
        制限がない。したがって、メッセージ通信は広い帯域を生かしつつ、
        データを大きなブロックにすることにより、プロセッサ間でとても効率
        的にやりとりができる。しかし、メッセージ通信は時間がかかる処理な
        ので、その必要性を最小限にしなければならない。並列処理プログラム
        のデータ構造は、広くプロセッサ間にまたがっているので、それぞれの
        プロセッサが参照するデータは、ローカルなメモリ上にある必要があ
        る。この作業をデータ配置と言う

     共有メモリ(Shared Memory):
        共有メモリは、並列システムにおけるプロセッサ間のやりとりについて
        のモデルの 1 つである。Pentium をいくつか搭載しているような複数
        プロセッサマシンで Linux が動いている場合は、プロセッサは 1 つの
        メモリを物理的に共有している。したがってあるプロセッサが共有メモ
        リに値を書き込むと他のどのプロセッサからも直接アクセスが可能にな
        る。これとは別に論理的に共有しているメモリを実装しているシステム
        もある。その各々のプロセッサは独自のメモリを持ち、ローカルでない
        メモリへの参照をプロセス間通信に適宜置き換える。普通どちらの共有
        メモリの実装をとっても、メッセージ通信よりも扱いやすい。物理的に
        共有されたメモリは広い帯域幅を使えるだけでなく、遅延も少ない。し
        かしそれは複数のプロセッサが同時にバスにアクセスしないようにした
        場合だけである。したがって、データ配置はやはりパフォーマンスに重
        大な影響を与え、キャッシュの効果等でどのデータ配置が最適なのかを
        判断するのは難しい

     集合演算(Aggregate Functions):
        メッセージ通信、共有メモリ両モデルとも、通信のきっかけは単独のプ
        ロセッサが引き金となる。それとは対照的に、生来の並列処理モデルで
        ある集約演算の通信では、各プロセッサが統合され、協調して動作す
        る。そのような動作はバリア同期(barrier synchronization)と呼ばれ
        ている。個々のプロセッサは、統合されたプロセッサのそれぞれが処理
        上のある境界に達するまで待機する。プロセッサ毎にある境界に達した
        ことを示す基準値を生成することで、通信機器はそれぞれのプロセッサ
        に値を返すことが可能になる。そのプロセッサは、独自の演算で得られ
        た値をすべて収集した結果を通信機器の返り値として返すことができ
        る。それは例えば「どのプロセッサが解答を見つけたか？」という問へ
        の返答が返り値になる場合もあるし、それぞれのプロセッサからの値の
        合計が返り値になる場合もある。遅延が極めて少ない反面、プロセッサ
        毎の帯域幅も狭い傾向にある。元来、このモデルはデータ値を分散させ
        るというよりも、並列実行を制御することを第一の目的としている場合
        が多い

     集合通信(Collective Communication):
        集合演算の別名で、集合演算を複数のメッセージ通信を使って実現する
        場合によく使われる

     SMP:
        SMP(Symmetric Multi-Processor)はオペレーティング・システムの概念
        で、対等に協調して動作するプロセッサの集合を表す。つまり、プロ
        セッサそれぞれが処理の部分部分をうまい具合に処理しあう。普
        通、SMP は MIMD と共有メモリを組み合わせて実現する。IA32 を使っ
        たシステムでは、SMP は MPS(the Intel MultiProcessor
        Specification)に準拠している。将来的には、「Slot 2」…を意味する
        ことになるかもしれない

     SWAR:
        SWAR(SIMD Within A Register)はレジスタを複数の整数領域に分割する
        考え方で、レジスタ長命令を使ってこれらの分割された領域で SIMD 並
        列演算を実行する。 k ビットのレジスタとデータパスと演算装置を持
        つマシンでは、通常のレジスタ処理は SIMD の並列処理で動くことが知
        られている。つまり k/n ビットの領域の値として n 個実行できる。こ
        のタイプの並列処理化は整数レジスタや整数命令を使って実行されるの
        が一般的だが、最近のハイエンドなマイクロプロセッサの多くは特殊な
        命令を追加して、マルチメディア関連処理のパフォーマンスを向上させ
        ている。Intel や AMD、Cyrix の MMX(MultiMedia eXtensions)がそれ
        に当り、他にも Digital Alpha の MAX(MultimediA eXtensions)や
        Hewlett-Packard PA-RISC の MAX(Multimedia Acceleration
        eXtensions)、MIPS の MDMX (Digital Media eXtension "Mad Max" と
        いう)、Sun SPARC V9 の VIS (Visual Instruction Set)がある。MMX
        を採用しているベンダー 3 社を除いて、これらの命令セットすべて
        は、似ていることは似ているが、それぞれ別個のものである

     付加プロセッサ(コプロセッサ):
        本来、付加プロセッサは特別な用途向けのコンピュータで、ホストとな
        るシステムに装着され、特殊な計算を高速に実行する。例えば、PC の
        ビデオカードやオーディオカードの多くには付加プロセッサが載ってお
        り、それぞれに共通している画像処理やオーディオの DSP(Digital
        Signal Processing)処理を高速化する。またあちこちにアレイ・プロ
        セッサも存在する。こう呼ばれる理由は、アレイ状(配列状)に配置され
        た付加プロセッサ上で、数学的演算を高速に実行するからである。現実
        的には、商用のスーパーコンピュータの多くはワークステーションとい
        うホストに付加プロセッサが載ったものである

     RAID:
        RAID(Redundant Array of Inexpensive Disks)は、ディスク入出力の帯
        域幅と信頼性を向上させる技術を指す。バリエーションがいくつもある
        が、共通して鍵となる考え方が 2 つある。第 1 は、データブロックそ
        れぞれは n+k 個のディスクドライブ群にストライプされており、それ
        ぞれのドライブはデータの 1/n だけ読み書きしなければいけない…と
        いうことを利用して、1 つのドライブに対して従来の n 倍の帯域幅を
        実現することである。第 2 は、データを冗長に書くことによって、
        ディスクドライブが 1 つ壊れてもデータを復旧できることである。こ
        れは重要なことで、冗長化していないと、n+k 個のディスクドライブの
        どれか 1 つでも駄目になった場合、ファイルシステムが完全に壊れて
        しまうことになる。RAID についての概要を知りたいならば、
        <http://www.adaptec.com/worldwide/product/prodtechindex.html?cat=%2fTechnology%2fRAID>
        を、Linux システムの RAID がどのようなものがあるかを知りたけれ
        ば、 <http://linas.org/linux/raid.html> を参照のこと。RAID 専用
        のハードウェアのサポートは別として、Linux はソフトウェアで RAID
        0、1、4、 5 をサポートしており、複数のディスクを 1 台の Linux シ
        ステムで扱える。詳細は、Software RAID mini-HOWTO や the Multi-
        Disk System Tuning mini-HOWTO を参照のこと。クラスタを組んだ複数
        のマシン上で複数のディスクドライブにまたがって RAID を組むこと
        は、デフォルトではサポートされていない

        【訳註：Multi-Disk System Tuning mini-HOWTO の日本語訳は
        <http://www.linux.or.jp/JF/JFdocs/Multi-Disk-HOWTO.html> です】

     IA32:
        IA32(Intel Architecture, 32-bit)は並列処理とは無関係だが、あるプ
        ロセッサの命令セットが Intel の 386 と互換性があるかについてふれ
        ている。基本的に、Intel の 286 以後の x86 プロセッサは、32 ビッ
        トのフラットなメモリモデルを持っていて、それが IA32 の特徴となっ
        ている。AMD と Cyrix も、IA32 互換のプロセッサを数多く製造してい
        る。Linux が主として IA32 プロセッサで発展し、商品市場が IA32 を
        中心として形成された理由は、 PowerPC や Alpha、PA-
        RISC、MIPS、SPARC 他のそれ以外のプロセッサと比べて利用しやすかっ
        た点にある。きたるべき IA64(64-bit with EPIC, Explicitly
        Parallel Instruction Computing)はきっとそううまくはいかないだろ
        う。Merced と呼ばれる最初の IA64 プロセッサは 1999 年の時点では
        製造の見通しが立っていない。

        【訳註：Merced はプロセッサのコア名となり、プロセッサ名として
        は、「Itanium」が正式名称となりました。製品版は 2001 年の中ごろ
        になる予定です。既に第 2 世代コアの「McKinley」もアナウンスされ
        ていて、2001 年後半に登場する予定です。一方 AMD は、
        「ClawHammer」と呼ばれる 64 ビットチップを 2002 年後半に登場させ
        る予定です】

     市販の既成品:
        多くの並列スーパーコンピュータ企業が消えていく中、並列計算システ
        ムの必要性から市販の既成品が論じられている。Windows に傾倒してい
        るなら、市販の既成品で PC を使った並列処理技術を生かせるの
        は、SMP の Windows NT サーバと MMX を使った様々な Windows のアプ
        リケーションということになるが、その考え方には何も得るところがな
        い。市販の既成品に対する考え方の背景には、開発にかかる時間と費用
        の削減がある。したがって、便利かつ普通に入手できる市販の既成品
        は、少なくともサブシステムに対して商品市場戦略の恩恵をもたらして
        くれる。ただ戦略が目指しているものとは違った技術が効果的に利用さ
        れることのなるのだが。大抵の場合、市販の既成品を用いた並列処理と
        はクラスタを指し、ノードを普通の PC で構成する。しかしネットワー
        ク・インタフェースやソフトウェアは何かしらカスタマイズしてある…
        普通は Linux 上でフリーで利用できる(例えば、コピーレフトやパブ
        リック・ドメイン)アプリケーションが動いているが、これは市販の既
        成品ではない

  1.3.  アルゴリズムの例

  この HOWTO でこれから紹介していく様々な並列プログラミング手法を理解す
  るには、例題を見ていくことが有効な手段です。どんな単純な並列アルゴリズ
  ムであったとしても、他の様々な並列プログラミングシステムのアプローチを
  明示してくれるアルゴリズムを選ぶことで、多少なりとも比較対照が楽になり
  ます。 M.J.Quinn 氏の著書である Parallel Computing Theory And
  Practice, second edition, McGraw Hill, New York, 1994 では、円周率を計
  算する並列アルゴリズムを挙げることで、いろいろな種類の並列スーパーコン
  ピュータでのプログラミング環境(例えば nCUBE のメッセージ通信や Sequent
  の共有メモリ) を明らかにしています。この HOWTO では同じ基礎的なアルゴ
  リズムを使っていきます。

  そのアルゴリズムは、円周率の近似値を x 角形の領域を合計することで計算
  します。C のプログラムで素直に順次実行すると、アルゴリズムは下記のよう
  になります。

  ______________________________________________________________________
  #include <stdlib.h>;
  #include <stdio.h>;

  main(int argc, char **argv)
  {
    register double width, sum;
    register int intervals, i;

    /* get the number of intervals */
    intervals = atoi(argv[1]);
    width = 1.0 / intervals;

    /* do the computation */
    sum = 0;
    for (i=0; i<intervals; ++i) {
      register double x = (i + 0.5) * width;
      sum += 4.0 / (1.0 + x * x);
    }
    sum *= width;

    printf("Estimation of pi is %f\n", sum);

    return(0);
  }
  ______________________________________________________________________

  しかしこの順次実行のアルゴリズムは、「極めて並列性を上げて」実行できま
  す。領域は間隔単位に細分化され、プロセッサの数の分だけ間隔単位で割り当
  てた部分を独立に合計できます。ここではプロセッサ間の調整作業は必要あり
  ません。ローカルでの合計が計算されれば、それを合計して総合計をだせばい
  いわけです。この段階ではプロセッサ間で何らかの調整と計算が発生します。
  最終的に、この総合計が円周率の近似値としてプロセッサの 1 つが値を出力
  します。

  この HOWTO では、このアルゴリズムを表現した様々な並列処理の実装を提示
  することで、プログラミング手法の様々な相違を検討していきます。

  1.4.  このドキュメントの構成

  このドキュメントの残りの部分は 5 つに分かれています。セクション 2 と
  3、4、5 それぞれは、Linux を使って並列処理をサポートする 3 種の異なる
  ハードウェアの構成にふれています。

  o  セクション 2 では SMP Linux システムを論じる。このシステムは、共有
     メモリを使って MIMD をそのまま実行できるが、メッセージ通信も簡単に
     実装できる。Linux は最大で 16 個のプロセッサの設定をサポートしてい
     るが、SMP PC システムの大部分は 2 もしくは 4 個の独立したプロセッサ
     を搭載している

  o  セクション 3 ではネットワークに接続していて Linux が動作しているマ
     シンで構成するクラスタについて論じる。クラスタとは並列処理システム
     で用いられ、MIMD の実行とメッセージ通信、もちろん論理的な共有メモリ
     もサポートする。SIMD の実行をシミュレートしたり、集合演算通信もサ
     ポートしているが、これはネットワークの手法に依存している。クラスタ
     当たりのプロセッサの数は 2 から 数千にまで及ぶが、数を制限する要因
     の第 1 はネットワークの取り回しである。1 つのクラスタに様々なタイプ
     のマシンを組み込むことが可能である。例えば、DEC の Alpha と Pentium
     の Linux システムで構成されたネットワークを異機種クラスタと呼ぶ

     【訳註：DEC は Compaq に吸収されましたが、Alpha マシンは健在です】

  o  セクション 4 では、SWAR(SIMD Within A Register)を論じる。これは非常
     に制限のある並列実行モデルではあるが、一般的なプロセッサに組み込ま
     れた機能でもある。最近では MMX(やその他類似の)命令セット拡張がプロ
     セッサに加えられており、このアプローチがより有効になってきた

  o  セクション 5 では、Linux PC を単独の並列計算システムを動かすホスト
     として使用する方法を論じる。付加プロセッサは追加のカードもしくは外
     付け機器の形をとって、Linux に特定のタイプのアプリケーションの実行
     に対して圧倒的な処理パワーを提供する。例えば、安価な ISA カードを利
     用することで、そこに搭載している複数の DSP プロセッサを使って計算の
     比重が大きい問題に数 100 MFLOPS の能力を割くことができる。しかしこ
     れらの追加のボード類は、まさにプロセッサそのものである。OS が動いて
     いるわけでも、ディスクや端末の入出力などがあるわけではない。そのよ
     うなシステムを活用するには、Linux という「ホスト」が上記の機能を提
     供する必要がある

  このドキュメントの最後のセクションでは、Linux を使った並列処理一般の関
  連事項について、上記の特定のアプローチにかかわり無くその特徴を取り上げ
  ます。

  このドキュメントを読むに当たって気に止めておいて欲しいことは、すべてを
  検証してあるわけではない、ということです。またここであげた多くの事柄は
  「まだまだ研究中のものです」(「思うようには動かない」といった方が適切
  です ;-) しかし Linux を使った並列処理は現状でも役に立ちますし、並列処
  理をより良いものにするべく、グループとしての活動があちこちで増えつつあ
  ります。

  この HOWTO の著者は、博士号を持つ Hank Dietz で、現在インディアナ州の
  郵便番号 47907-1285、ウエストラファイエットにあるパーデュ大学におい
  て、 Electrical and Computer Engineering の助教授をしています。Dietz
  はこのドキュメントに対して、the Linux Documentation Project のガイドラ
  インに準拠した権利を有しています。このドキュメントの発表に当たって、正
  確で公正になるように努力しましたが、 Dietz 氏およびパーデュ大学はいか
  なる問題や誤りに対して責任を負えないだけでなく、パーデュ大学はここで議
  論された研究やその成果物に対して、何も保証しません。

  【訳註：著者の Hank Dietz 氏は、1999 年 9 月にケンタッキー大学に異動さ
  れ、それにともない研究活動の中心も同大学に移りました。このドキュメント
  でも述べられている彼自身の並列処理の研究は、 KAOS
  <http://aggregate.org/KAOS/> グループで継続されています。その成果物と
  して KLAT2 <http://aggregate.org/KLAT2/> 等が稼働しています。 KLAT2
  は、IEEE/ACM SC2000 <http://www.sc2000.org/> にて Gordon Bell Award を
  受けています。パーデュ大学での成果及び最近の研究については、The
  Aggregate <http://aggregate.org/> で見ることができます】

  2.  SMP Linux

  このドキュメントでは、 SMP Linux
  <http://www.linux.org.uk/SMP/title.html> システムを並列処理に使う方法
  について、要点をかいつまんで見ていきます。 SMP Linux の最新の情報
  は、SMP Linux プロジェクトのメーリングリストで得られま
  す。majordomo@vger.rutgers.edu に subscribe linux-smp と書いて電子メー
  ルを送ると参加できます。

  SMP Linux は本当に動作するのでしょうか？ 1996 年 6 月に新品の(実はノー
  ブランド ;-)100MHz 駆動の Pentium を 2 個搭載したシステムを購入しまし
  た。パーツから組み上げたシステムで、プロセッサと Asus のマザーボー
  ド、256 K のキャッシュ、32 MB の RAM、1.6 GB のディスク、6 倍速の
  CD-ROM、ビデオカードに Stealth 64、15 インチの Acer のモニターがつい
  て、合計 1,800 ドルでした。プロセッサが 1 つのシステムと比べて数 100
  ドル高くなりました。 SMP Linux を動かすのは難しいことではなく、単独プ
  ロセッサ用の「普通についてくる」Linux をインストールして、makefile に
  ある SMP=1 という行のコメントを外して、カーネルを再コンパイルし(SMP を
  1 に設定とは、ちょっと皮肉ですね ;-)、lilo に新しいカーネルを設定すれ
  ばいいだけです。このシステムのパフォーマンスは申し分なく、安定性も十分
  です。それ以来メインのワークステーションとして活躍しています。つまり
  SMP Linux は実際に動いているのです。

  次にわからないのは、はたして SMP Linux でどの程度のレベルの高さで共有
  メモリを使った並列プログラムをコーディングして実行できるかということで
  しょう。 1996 年前半では、まだ十分とはいえませんでした。しかし状況は変
  わりました。例えば、現在では極めて完成度が高い POSIX スレッドライブラ
  リがあります。

  本来の共有メモリ方式と比べてパフォーマンスは劣りますが、SMP Linux シス
  テムでは、もともとソケット通信を使ったワークステーション上で動くクラス
  タ用に開発した並列処理用のソフトウェアのほとんどを利用できます。ソケッ
  ト(セクション 3.3 を参照のこと)は、単独の SMP Linux 上で動作し、クラス
  タをネットワーク上で組んだ複数の SMP マシンでも動作します。しかし、ソ
  ケットは SMP にとっては必要ないオーバーヘッドがかなり多くあります。
  オーバーヘッドのかなりの部分はカーネルや割り込み制御にあります。これが
  問題をさらに悪化させています。というのも、通常 SMP Linux は、カーネル
  内で同時に動作できるプロセッサは 1 つだけで、割り込みコントローラとし
  て設定できるプロセッサもたった 1 つだけなのです。つまりブートしたプロ
  セッサしか割り込みをかけられません。それにもかかわらず、標準的な SMP
  の通信機器は、大部分のクラスタを組んだネットワークよりも優れています。
  クラスタ用に設計されたソフトウェアが、クラスタ上で動くよりも SMP 上の
  方がうまく動作することが多いようです。

  【訳註：Linux のカーネルが 2.4 になり SMP 対応が改良されました。 2.2
  系列以前では、システムコールが呼び出されるとそれが動いているプロセッサ
  がカーネルを離すまでカーネル全体にロックをかけていました。つまり他のプ
  ロセッサがカーネルを利用しようとしても(システムコールを発行しても)、
  カーネルのロックがはずれるまで(現在動いているシステムコールの処理が終
  わるまで)処理待ちになっていました。2.4 系列ではカーネル全体ではなく、
  カーネル内部でプロセッサ間で共有している資源単位でのロックが可能になり
  ました。ただしすべてが資源単位でのロックに変更されたわけではなく、依然
  としてカーネルをロックする部分もあります。割り込みについても、それぞれ
  のプロセッサからかけることが可能になりました】

  このセクションの残りの部分では、SMP のハードウェアについて論じ、Linux
  で並列プログラムのプロセスがメモリを共有し合う基本的なしくみを見て行く
  ことにします。アトミックな処理や変数の保存、資源のロック、キャッシュ・
  ラインについて 少し意見を述べて、最後に他の共有メモリ並列処理について
  の資料をいくつか紹介したいと思います。

  2.1.  SMP のハードウェア

  SMP システムはもう何年も動き続けていますが、つい最近まで基本的な機能の
  実装がそれぞれのマシンで異なる傾向にあったため、オペレーティングシステ
  ムのサポートは移植性があるとは言えませんでした。この状況が変わるきっか
  けになったのが、MPS と呼ばれる Intel の Multiprocessor Specification
  です。MPS 1.4 の仕様は、PDF ファイルで
  <http://www.intel.com/design/pro/datashts/242016.htm> から利用できま
  す。また MPS 1.1 についての概要が
  <http://support.intel.com/oem_developer/ial/support/9300.HTM> 【訳註：
  リンク切れ】にあります。ただ、intel の WWW サイトはよく構成を変えるの
  で注意してください。プロセッサを最大 4 つまで搭載する MPS 互換のシステ
  ムは、数多くのベンダー <http://www.uruk.org/~erich/mps-hw.html>が構築
  しています。しかし MPS は理論上もっと多数のプロセッサをサポートしてい
  ます。

  非 MPS で 非 IA-32 なシステムで SMP Linux がサポートしているものは、
  Sun4m を載せたマルチプロセッサの SPARC マシンです。SMP Linux は Intel
  の MPS バージョン 1.1 もしくは 1.4 互換のマシンをサポートしており、16
  個までの 486DX、Pentium、Pentium MMX、Pentium Pro、Pentium II をサポー
  トしています。サポートしていない IA-32 プロセッサには、Intel の 386 と
  Intel の 486SX/SLC です(SMP のしくみと浮動小数点演算装置をつなぐインタ
  フェースがありません)。 AMD と Cyrix プロセッサ(SMP をサポートするチッ
  プが異なるので、このドキュメントを書いている時点では利用できません)が
  上げられます。

  【訳註：先に述べた KLAT2 <http://aggregate.org/KLAT2/> は、AMD の
  Athlon を搭載していて、SWAR として intel の MMX に加えて、AMD のマルチ
  メディア拡張命令である 3DNow! も利用しています】

  大切なのは、MPS 互換のシステムのパフォーマンスが大きくばらつくことを
  知っておくことです。皆さんの予想通り、プロセッサの速度はパフォーマンス
  の差に影響を与える要因の 1 つです。クロック速度が速ければ速いほど、よ
  り高速なシステムになる傾向にありますし、Pentium Pro は Pentium よりも
  高速です。しかし MPS 自体は共有メモリの実装がどのようであるかについて
  仕様を決めているわけではなく、ただソフトウェアの点からどのように実装が
  機能すべきなのかを決めているに過ぎません。つまりパフォーマンスとは、共
  有メモリの実装がどのように SMP Linux やユーザのプログラムの特性と相互
  に連係しているかの結果でもあります。

  まず MPS に準拠したシステムそれぞれで見なければいけないのは、どのよう
  にシステムが物理的な共有メモリにアクセスできるのかということです。

  2.1.1.  それぞれのプロセッサは独自の L2 キャッシュを持っているか？

  MPS Pentium の一部やすべての MPS Pentium Pro、Pentium II システムは、
  独立した L2 キャッシュを持っています(Pentium Pro や Pentium II はモ
  ジュールに組み込まれています)。独立した L2 キャッシュは一般的にコン
  ピュータのパフォーマンスを最大限に引き出すと見なされていますが、Linux
  ではそれほど単純ではありません。複雑にしている第 1 の原因は、現状の
  SMP Linux のスケジューラがプロセスそれぞれを同じプロセッサに割り当て続
  けないようにしている点にあります。これはプロセッサ・アフィニティと呼ば
  れている考え方です。これはすぐにでも変更されるかもしれません。最近「プ
  ロセッサ・バインディング」というタイトルで、SMP Linux の開発コミュニ
  ティでこの考え方が何度か議論されました。プロセッサ・アフィニティがない
  と、独立した L2 キャッシュが存在した場合無視できないオーバーヘッドが発
  生してしまいます。それは、あるプロセスが最後に実行していたプロセッサで
  はなく、別のプロセッサでタイムスライスを割り当てられた場合に発生しま
  す。

  【訳註：カーネル2.2系列では、特定の CPU に特定のプロセスを割り当てるこ
  とは標準ではできませんが、パッチとして PSET - Processor Sets for the
  Linux kernel( <http://isunix.it.ilstu.edu/~thockin/pset/>) が用意され
  ています。 2.4 系列では、プロセスを同じプロセッサになるべく処理させる
  ようにスケジューリング方法が変更されています。またプロセッサ・バイン
  ディングは、割り込みについては実装されており、プロセスについては検討中
  です】

  比較的安価なシステムの多くは、1 つの L2 キャッシュを 2 つの Pentium プ
  ロセッサで共有しています。困ったことにキャッシュの競合が発生して、複数
  の独立した順次処理をするプログラムに対して深刻なパフォーマンスの低下を
  引き起こします。逆に良い点は、並列プログラムの多くが実際に共有した
  キャッシュの恩恵を受ける可能性があることです。というのは、両方のプロ
  セッサが共有メモリの同じラインにアクセスしようとする場合、キャッシュに
  データをフェッチしなければならないのは 1 つのプロセッサだけで、バスの
  競合が避けられます。プロセッサ・アフィニティがなければ、共有の L2
  キャッシュのデータ不整合をより低く押えることもできます。つまり並列処理
  にとって、予想していたほど、共有の L2 キャッシュに欠点があるとは断定で
  きません。

  私達が所有している 2 つの Pentium を搭載した 256 K のキャッシュを持つ
  システムを使用した経験からすると、パフォーマンスに非常にばらつきがあ
  り、その原因はカーネルが負う処理のレベルに依存しています。最悪で約 1.2
  倍の速度向上にしかなりません。しかし 2.1 倍まで高速化ができたことか
  ら、計算中心の SPMD スタイルのコードはまさに「フェッチの共有」効果がで
  ていることになります。

  2.1.2.  バスの構成？

  最近の大部分のシステムでは、プロセッサに 1 つ以上の PCI バスが接続して
  いて、その PCI バスに 1 つ以上の ISA や EISA バスが「ブリッジ」してい
  ます。これらのブリッジが遅延を引き起こし、さらに EISA と ISA は PCI に
  比べて帯域も狭くなります(ISA が最も遅い)。そのためディスクドライブやビ
  デオカード他の高いパフォーマンスを必要とするデバイスは、普通 PCI バス
  インタフェース経由で接続すべきです。

  MPS システムでは、PCI バスが 1 つしかなくても計算が多くを占める並列プ
  ログラムを高速に実行できます。しかし入出力処理は、プロセッサが 1 つの
  ものより良いとは言えません…恐らくプロセッサによるバスの競合で若干パ
  フォーマンスが落ちてしまいます。つまり入出力の速度向上に注目するとすれ
  ば、複数の独立した PCI バスと入出力コントローラがある MPS システムを手
  に入れる方が良いということになります(例えば複数の SCSI 接続)。ここで注
  意しなければいけないのは、SMP Linux があなたが所有する部品をサポートし
  ているか、ということです。また最新の SMP Linux が基本的にカーネル上で
  は常に 1 つのプロセッサしか動いていないということも忘れないでくださ
  い。そういう訳で、入出力コントローラには入出力処理それぞれに必要となる
  カーネルの処理時間が最小のものを選ぶように心がけてください。本当に高性
  能を目指すならば、システムコールを使わないで、ユーザ・プロセスから直接
  raw デバイスを使って入出力することを考慮してもいいのではないでしょう
  か。これは思ったほど難しいことではありませんし、セキュリティレベルを下
  げることにもなりません(セクション 3.3 で基本的なやり方を説明します)。

  バスの速さとプロセッサのクロックの速さの関係がここ数年でいびつになって
  いることをぜひ知っておいてください。現状ではシステムの大部分は同一の
  PCI のクロック速度を使用していますが、高速なプロセッサのクロックと低速
  なバスのクロックの組合せは、まれなケースとは言えません。Pentium 133 は
  Pentium 150 より速いバスを採用しており、様々なベンチマークで一見不思議
  な結果を出しているのが良い例です。これらの影響は SMP システムではさら
  に大きくなります。より速いバス・クロックがより重要なのです。

  【訳註：Pentium 133 は 66 MHz の 2 倍速、Pentium 150 は 60 MHz の 2.5
  倍速で駆動します】
  2.1.3.  メモリのインタリーブと DRAM の技術？

  本来、メモリをインタリーブすることは MPS とは何の関係もありませんが、
  MPS システムではよく触れられる話です。というのも、これらのシステムは往
  々にしてメモリの帯域幅をより必要とするからです。普通は、2 way もしくは
  4 way をインタリーブして RAM を構成します。そのためブロック・アクセス
  は、 1 つのバンクだけではなく、複数のバンクを使用します。これによっ
  て、より広い帯域幅でメモリにアクセスでき、とりわけキャッシュのロードや
  ストアに効果があります。

  【訳註：インタリーブとは、メモリ上のデータ読み書きの高速化をはかるため
  の方法の 1 つで、搭載されているメモリをグループ分けします。このグルー
  プをバンクと呼び、このバンク単位に並列にアクセスする方式をとります。 2
  way は 2 バンク、4 way は 4 バンクにメモリをグループに分けて並列にアク
  セスします】

  しかしこの効果については、状況が少々混沌としてきました。その理由は、
  EDO DRAM と他の様々なメモリ関連技術が同じような種類の操作に改良を加え
  てきているためです。DRAM 技術についてとてもよくまとめられた資料が、
  <http://www.pcguide.com/ref/ram/tech.htm> で見られます。

  それでは、2 way でインタリーブしている EDO DRAM とインタリーブしていな
  い SDRAM ではどちらがパフォーマンスが優れているでしょうか？ この質問は
  とてもいい質問なのですが、簡単には答えらえません。というのも、インタ
  リーブするのも、魅力ある DRAM 技術も、とても高価になりがちだからです。
  同じお金をかけて普通のメモリを購入すれば、より多くのメモリを確保できる
  のは明らかです。一番遅い DRAM でも、ディスクベースの仮想メモリより比べ
  ものにならないほど高速です…。

  【訳註：SDRAM と EDO RAM の大きな違いは、SDRAM は PC のベースクロック
  と同期して動作するのに対して、EDO RAM はベースクロックとは非同期に動作
  する点にあります。しかし SDRAM もはじめのアドレスにあるデータにアクセ
  スする時には EDO DRAM と同様に遅延(RAS、CASの)が発生します。次世代メモ
  リと言われる RDRAM もより広いメモリ帯域幅が実現できますが、DRAM を使用
  することにはかわりなく、やはり遅延が発生してしまいます。つまり、現在ま
  でのメモリの高速化はバーストモードの高速化が中心であって、 DRAM 自体が
  抱えている遅延の問題を根本的に見直しているわけではありません】

  2.2.  共有メモリを使ったプログラミングをはじめるにあたって

  そろそろ並列処理を SMP で動かすことが、いかに素晴らしいかを理解できた
  と思いますが…。どこからはじめたらいいでしょうか？ まずは共有メモリ通
  信が実際にどのように動くのか、少しばかり勉強してみましょう。

  あるプロセッサが値をメモリにストアして、あるプロセッサがそれをロードす
  る―こう言うと単純なことに思われるかもしれませんが、残念ながらそれほど
  単純ではありません。例えば、プロセスとプロセッサの関係はきっちり決って
  いるわけではありません。しかし、プロセッサの個数以上にプロセスが動いて
  いないなら、プロセスとプロセッサという言葉を入れ換えてしまっても、おお
  ざっぱに言って同じことです。このセクションの残りの部分では、大きな問題
  になると思われるキーポイントを概観して、ポイントを外したままにならない
  ようにしたいと思います。何を共有すべきかを決めるのに使われるモデルを 2
  つと、アトミックな操作、変数の保存のしかた、ハードウェアのロック操作、
  キャッシュ・ラインの効果、Linux におけるプロセスのスケジューリングを扱
  います。

  2.2.1.  すべてを共有するのか、一部を共有するのか

  共有メモリを使うプログラミングには、根本的に異なる 2 つのモデルがあり
  ます。それはすべてを共有する場合と一部を共有する場合です。この 2 つの
  モデルとも、プロセッサが共有メモリからもしくは共有メモリへロードやスト
  アをしてやりとりできます。違うところは、すべてを共有する場合がデータ構
  造すべてを共有メモリに置くのに対して、一部を共有する場合は、ユーザが
  はっきりとどのデータ構造が共有メモリを使用する可能性があり、どのデータ
  構造が単一プロセッサのローカルなメモリを使用するかを示す必要がありま
  す。

  どちらの共有メモリモデルを使うべきでしょうか？ たいていの場合は好みの
  問題と言ってもいいと思います。すべてを共有するモデルを好む人々が多いの
  は、データ構造のどれが宣言されたその時に共有されるべきか、ということを
  区別する必要がまったくないからです。ただ競合しそうなアクセスにロックを
  かけて、共有しているものが 1 つのプロセス(プロセッサ)からしか一時にア
  クセスしないようにします。繰り返しますが、これも上記のように単純なわけ
  ではありません。そこで、比較的安全である共有するものを一部にとどめる方
  法を選ぶ人々が多数いるのです。

  2.2.1.1.  すべてを共有する

  すべてを共有する利点は、既にある順次実行プログラムがたやすく手に入るこ
  と、そしてそのプログラムをすべてを共有することを前提にした並列プログラ
  ムに、それほど手間をかけず修正していける点にあります。どのデータが他の
  プロセッサによってアクセスされるかを最初から解決する必要はありません。

  簡単に言うと、すべてを共有する上で大きな問題となるのは、あるプロセッサ
  が処理したことすべてが他のプロセッサに影響を与えてしまうかもしれない点
  にあります。この問題が顕著になるケースは 2 つあります。

  o  ライブラリの多くは、データ構造が共有されることを前提にしていない。
     例えば、UNIX の慣習として関数の多くは errno と呼ばれる変数でエ
     ラー・コードを返す。すべてを共有している 2 つのプロセスが、様々なシ
     ステムコールを発行した場合、同じ errno を共有してしまい、お互いに干
     渉しあってしまうかもしれない。現在ではこの errno の問題を修正した
     バージョンのライブラリがあるが、同じような問題は依然として大部分の
     ライブラリに残っている。例えば、特別な処置をしないと複数のすべてを
     共有するプロセスから X ライブラリを呼んでも動作しないのが常である

  o  通常最悪のケースとなる動作は、プログラムがポインタや配列の予約を間
     違ってしまい、そのおかしなコードを含んだプロセスが死んでしまうこと
     である。そうすると、おそらく core ファイルができて、何が起こったか
     の手掛かりが得られる。すべてを共有する並列処理の場合は、不正なアク
     セスによって、おかしなプロセス以外のプロセスが死んでしまうかもしれ
     ず、エラーを特定したり、修正したりすることがほとんど不可能になる

  上記の類の問題は、一部を共有する方法をとった場合にはほとんど起こりませ
  ん。それは、はっきりと指定したデータ構造だけを共有するためです。また、
  すべてを共有する方法が動作するのは、プロセッサすべてが全く同じメモリ・
  イメージを実行している場合だけです。これはほとんど疑う余地がないことで
  す。すべてを共有して複数の異なるコード・イメージを扱うことはできませ
  ん(つまり、SPMD は利用できますが、MIMD はできません)。

  すべてを共有する場合にプログラムがサポートする典型的なタイプが、スレッ
  ド・ライブラリです。Threads
  <http://liinwww.ira.uka.de/bibliography/Os/threads.html> は、本来「軽
  量(light-weight)」なプロセスとして、通常の UNIX 上のプロセスとは違った
  スケジュールで動作していました。ここでとても大切なのは、1 つのメモリ・
  マップを共有してアクセスする点にあります。 POSIX Pthreads
  <http://www.gnu.ai.mit.edu/software/pth/> のパッケージは、移植に対して
  たいへんな労力を注いできました。ここで非常に疑問なのは、移植されたもの
  が本当に SMP Linux 上のプログラムのスレッドとして並列に動作するのか、
  ということです(理想的には、あるプロセッサでそれぞれのスレッドが動作す
  る)。POSIX API は、そこまでを求めていませんし、
  <http://www.aa.net/~mtp/PCthreads.html> 【訳註：リンク切れ】のような
  バージョンは、スレッドを並列に実行することをまったく考慮していません。
  プログラムから実行されるスレッドはすべて、Linux の 1 つのプロセス内で
  動き続けます。

  SMP Linux の並列処理を最初にサポートしたスレッド・ライブラリは、既に過
  去のものになりつつある bb_threads ライブラリで、
  <ftp://caliban.physics.utoronto.ca/pub/linux/> にあります。これはとて
  も小規模なライブラリで Linux の clone() システムコールを使い、新たに子
  プロセスを立ち上げた上で独自のスケジュールで動きます。ここでは Linux
  のプロセスすべては 1 つのアドレス空間を共有します。 SMP Linux マシンで
  は、これらの「スレッド」を複数並行に動作することができます。理由は各々
  の「スレッド」が Linux のプロセスに他ならないからです。これと引き換え
  に、他のオペレーティング・システムが提供しているいくつかのスレッド・ラ
  イブラリのような「軽量な」スケジュール制御はできません。このライブラリ
  は、C でラッパーしたアセンブリ・コードが少し含まれていて、メモリ上にそ
  れぞれのスレッドのスタック領域を確保し、アトミックなアクセス機能をロッ
  クの配列(ミューテック、mutex)で実現します。ドキュメントは、README と短
  いサンプル・プログラムがあります。

  最近になって、clone() を使った POSIX スレッドが開発されています。この
  ライブラリは LinuxThreads
  <http://pauillac.inria.fr/~xleroy/linuxthreads/> で、SMP Linux で使用
  することを前提にしてすべてを共有することを主目的にしたライブラリで
  す。POSIX スレッドはドキュメントが整備されており、LinuxThreads README
  <http://pauillac.inria.fr/~xleroy/linuxthreads/README> や LinuxThreads
  FAQ <http://pauillac.inria.fr/~xleroy/linuxthreads/faq.html> はとても
  良い資料です。現状での主な問題は、POSIX スレッドは内容が非常に濃いのた
  めに、きちんとその内容を理解する必要があること、LinuxThreads は開発途
  中であることです。また、POSIX スレッド規格は、標準化作業中という問題も
  あり、古くなった規格の初期バージョンで作成しないように少々注意が必要で
  す。

  【訳註：POSIX スレッドは IEEE 1003.1 として標準化されています。各種ス
  レッドライブラリについては Multithreading Libraries
  <http://www.gnu.org/software/pth/related.html>、マルチスレッドのプログ
  ラミングについてはマルチスレッドのプログラミング
  <http://docs.sun.com/ab2/coll.141.7/MTP/@Ab2TocView?Ab2Lang=ja&Ab2Enc=shift_jis>
  が役に立ちます】

  2.2.1.2.  一部を共有する

  実際のところ一部を共有するということは、「共有する必要があるものだけを
  共有する」ということです。このアプローチは、一般的には MIMD(SPMD では
  ない) に合致していて、それぞれのプロセッサのメモリ・マップ上で同じ位置
  にある共有するオブジェクトを扱う点に注意が必要となります。もっと大切な
  のは、一部を共有することでパフォーマンスの予測と改善やコードのデバック
  等が簡単になることです。ただ問題になるのは、

  o  何が本当に共有すべきなのかを前もって知ることが難しい

  o  実際、共有メモリ上に割り付けられたオブジェクトは扱いづらく、特にス
     タック上にあるオブジェクトはなおさらである。例えば、共有されるオブ
     ジェクトは、それぞれ独立したメモリセグメント上にきちんと配置する必
     要があるかもしれないし、それぞれのメモリセグメントに配置する作業や
     それぞれの参照情報の中にさらに余分に間接的なポインタを格納する必要
     があるかもしれない

  現状では、Linux のプロセスグループを独立したメモリ空間に置くのに非常に
  似かよった手法が 2 つあります。共有するものすべては、そのメモリ空間上
  ではほんのわずかなセグメントを使用するだけです。Linux システムを設定す
  る時に「System V IPC」をうっかり外してしまわなければ、 Linux は
  「System V Shared Memory(共有メモリ)」というとても移植性が高いしくみを
  利用できるようになります。他の選択としてメモリのマッピング機能も利用で
  き、これはいろいろな UNIX システム間でそれぞれ実装されています。それが
  mmap() システムコールです。マニュアルその他でこれらのシステムコールを
  学習することをお勧めします。簡単な概略はセクションの 2.5 と 2.6 で触れ
  ますので、勉強するきっかけとして利用してください。

  2.2.2.  アトミックな処理と処理の順序

  上記どちらのモデルを使うにしても、結果はそれほど違いません。要は並列プ
  ログラムの中から、すべてのプロセッサがアクセスできる読み書き可能なメモ
  リの一部へのポインタを得るわけです。これが意味するところは、共有メモリ
  上のオブジェクトをアクセスする並列プログラムが、そのオブジェクトがロー
  カルなメモリにあるかのごとく扱える、ということなのでしょうか？そうとも
  言えるのですが、ちょっと違います…

  アトミックな処理というのは、ある対象に対する操作を部分に分けたり、割り
  込みが入ったりすることなく、連続して処理をする考え方です。残念ながら共
  有メモリのアクセスでは、共有メモリ上にあるデータに対するすべての操作が
  アトミックに行われるわけではありません。何か手を打たない限り、単純な
  ロードやストア操作のようなバス上での処理が 1 回で済む操作 (つま
  り、8、16、32 ビットにアラインされた操作。アラインされていなかったり、
  64 ビットの操作は異なる)だけが、アトミックな処理になります。さらに都合
  が悪いことに、GCC のような「賢い」コンパイラでは最適化が働いてしまい、
  あるプロセッサの処理が完了したことを他のプロセッサが検知するのに必要と
  なるメモリ上の操作を取り除いてしまいがちです。幸いなことには、これらの
  問題 2 つとも改善することができます。気になるアクセスの効率とキャッ
  シュ・ラインの大きさの関係をそのままにして置くのです。

  しかしこれらの問題点を論じる前に、それぞれのプロセッサがメモリを参照す
  る時には、前提としてコーディングした順番で参照するということをはっきり
  しておくことは無駄ではありません。Pentium がそうですが、将来の Intel
  のプロセッサがそうであるとは限らない、ということも忘れないでください。
  そのようなわけで、気に止めておいて欲しいことがあります。それは将来のプ
  ロセッサが、保留しているメモリへのアクセスを完了させる命令を通じて共有
  メモリにアクセスする必要が出てくるかもしれない、ということです。つま
  り、メモリへのアクセスに順番を付ける機能を提供するわけです。CPUID 命令
  は、その命令によって生じる悪影響に対しての予防処置であることは明らかで
  す。

  2.2.3.  変数の保存のしかた

  レジスタにある共有メモリのバッファされているオブジェクトの値を GCC が
  最適化しないようにするには、共有メモリ上のすべての実体を volatile とい
  う属性をつけて宣言する必要があります。こう宣言すると、共有するオブジェ
  クトに対する 1 ワードだけのアクセスをともなう読み書きは、アトミックに
  行われます。例えば、p が整数へのポインタでポインタと整数両方が共有メモ
  リにあるとすると、ANSI C での宣言は次のようになります。

  ______________________________________________________________________
  volatile int * volatile p;
  ______________________________________________________________________

  このコードでは、最初の volatile は int を参照し、その int は p を指し
  ています。次の volatile は、ポインタそのものを参照しています。ややっこ
  しいのですが、このちょっとした指定で、GCC があちこちで強力に最適化する
  ことが可能になります。少なくとも理屈上では、GCC に対して -traditional
  オプションをつけることで、最適化をいくぶん犠牲にして正しいコードを間違
  いなく生成できると思います。というのは、ANSI が定められる前の K&R の C
  は、そもそも register と意図的に指定をしない限り、すべての変数を
  volatile 扱いにしていました。GCC でコンパイルする場合に cc -O6 ... と
  いう様にしているなら、本当に必要な部分にははっきりと volatile と指定し
  たくなるに違いありません。

  プロセッサのすべてのレジスタが更新されたことを知らせる役目であるアセン
  ブリ言語のロックを使うと、GCC が適切にすべての変数をフラッシュして、
  volatile と宣言したことに関連したコードが「非効率」にコンパイルされる
  ことを防ぐ、という噂があります。この裏技は、GCC のバージョン 2.7.0 を
  使って static で確保した外部変数に対して効果を発揮します…がこれは
  ANSI C 標準に沿った機能ではありませんし、なおまずいことに読み込みだけ
  を行う他のプロセッサは、レジスタにずっと値をバッファしてしまいます。つ
  まり、共有メモリ上の値が変更されていることを全く知りようがありません。
  まとめると、望みの動作を行うには volatile を使って変数にアクセスするし
  か正しく動作することが保証されない、ということです。

  通常の変数に対して volatile 属性を指定してタイプキャストをすること
  で、volatile なアクセスが行えるということを理解しておいてください。例
  えば、通常の int i; は *((volatile int *) &i) とすることで、volatile
  に参照できるようになります。つまり、重要な部分に対してだけ「オーバー
  ヘッド」をともなう volatile な操作を意図して呼び出すことができます。

  2.2.4.  ロック

  ++i; が、共有メモリにある変数 i に常に 1 を加えると思っているなら、
  「なんだ、これは」という事態になります。単一な命令を実行するようにコー
  ディングされていても、ロードやストアは結果的に独立してメモリに対する命
  令を発行し、他のプロセッサは i に対するこの 2 つの命令の間にアクセスで
  きてしまいます。例えば、2 つのプロセスが両者から ++i; を実行すると、i
  は 2 増えるのではなく、 1 増えることになるかもしれません。Intel による
  Pentium の「Architecture and Programming Manual」によると、LOCK という
  単語が頭についていると、その後にくる命令が何であっても、メモリ上のデー
  タへのアクセスに関連する操作はアトミックであることが保証されています。

  ______________________________________________________________________
  BTS, BTR, BTC                     mem, reg/imm
  XCHG                              reg, mem
  XCHG                              mem, reg
  ADD, OR, ADC, SBB, AND, SUB, XOR  mem, reg/imm
  NOT, NEG, INC, DEC                mem
  CMPXCHG, XADD
  ______________________________________________________________________

  しかしこれらの命令を全面的に採用することは、良い思い付きとはいえないで
  しょう。例えば、XADD は 386 プロセッサでは使用できませんので、移植の面
  で問題が生じてしまうでしょう。

  XCHG 命令は、LOCK という単語が頭についていなくても常にロックをかけます
  し、この方法は明らかにアトミックな操作として、より高級なアトミックのし
  くみであるセマフォやキューの共有よりも優れています。もちろん C でコー
  ディングしたものを GCC にかけても、この命令は生成できません…そのかわ
  りに、少しばかりインラインでアセンブリ・コードの記述が必要になりま
  す。1 ワードの volatile なオブジェクトである obj とやはり 1 ワードのレ
  ジスタ値である reg を定義すると、GCC のインラインでのアセンブリ・コー
  ドは下記のようになります。

  ______________________________________________________________________
  __asm__ __volatile__ ("xchgl %1,%0"
                        :"=r" (reg), "=m" (obj)
                        :"r" (reg), "m" (obj));
  ______________________________________________________________________

  GCC のインラインのアセンブリ・コードで、ビット操作を使ってロックをかけ
  る例は、bb_threads library
  <ftp://caliban.physics.utoronto.ca/pub/linux/> にあります。

  しかし、おぼえておいて欲しいことがあります。それはメモリ操作をアトミッ
  クに行うと、それなりにコストがかかるということです。ロックをかける操作
  はかなり大きなオーバーヘッドがかかり、通常の参照がローカルなキャッシュ
  を利用するのに比べ、他のプロセッサからのメモリ参照は遅延が生じてしまう
  ことになるでしょう。最適なパフォーマンスを得るには、できるだけロック操
  作を使用しないことです。また、これら IA32 でのアトミックな命令は、当然
  ですが他のシステムには移植できません。

  この他にも解決方法はいろいろあります。通常の命令に対して、同期をいろい
  ろととるための実装がいくつも用意されています。その中には 相互排他
  (mutual exclusion。略称 mutex))があり、少なくとも 1 つのプロセッサがい
  つでも共有しているオブジェクトを更新できることを保証しています。たいて
  いの OS のテキストでは、これらのテクニックの内のいくつかを論じていま
  す。Abraham Silberschatz 氏 と Peter B. Galvin 氏の共著である、
  Operating System Concepts、ISBN 0-201-50480-4 の第 4 版で非常に優れた
  議論がなされています。

  2.2.5.  キャッシュ・ラインの大きさ

  アトミックな操作に関して必須で、SMP のパフォーマンスに劇的な影響をあた
  えることがもう 1 つあります。それはキャッシュ・ラインの大きさです。
  MPS 規格ではキャッシュが使われていてもいなくても、同期するための参照が
  必要とされています。しかし 1 つのプロセッサがメモリのあるラインに書き
  込みを行うと、以前書き込まれたライン上にあるキャッシュしてあるものは、
  すべてを無効にするか更新する必要があります。2 つ以上のプロセッサどれも
  がデータを同じラインの違う部分に書くとすると、キャッシュやバスに多量の
  やり取りが発生してしまうかもしれません。これは事実上キャッシュ・ライン
  から受渡しが起こることを意味しています。この問題は、偽共有(false
  sharing) と言われています。解決するのはいたって単純で、並列にアクセス
  する場合は、それぞれのプロセスに対して、データをできるだけ異なるキャッ
  シュ・ラインから取ってくるようにデータを構成することです。

  偽共有は、CPU が共通して使用する L2 キャッシュを持っているシステムには
  関係ない問題と思われるかもしれませんが、独立して存在する L1 キャッシュ
  を忘れてはいけません。キャッシュの構成や独立したレベルの数は両者とも変
  動するもので、Pentium の L1 のキャッシュ・ラインの大きさは 32 バイトで
  外部キャッシュ・ラインの典型的な大きさは 256 バイト程度です。仮に 2 つ
  の要素のアドレス(物理、仮想どちらでも)が a と b として、プロセッサ毎の
  最大のキャッシュ・ラインの大きさが c としてそれが 2 の累乗の大きさとし
  ます。厳密に言うと、もし ((int) a) & ~(c - 1) が ((int) b) & ~(c - 1)
  と等しければ、どちらも同じキャッシュ・ラインを参照することになります。
  共有するオブジェクトが並列に参照される場合、少なくとも c バイト離れた
  位置にあるならば、異なるキャッシュ・ラインに置かれるべきだ、というのが
  原則です。

  2.2.6.  Linux のスケジューラの問題点

  並列処理で共有メモリを使用する際には、あらゆる部分で OS のオーバーヘッ
  ドを避けなければいけませんが、OS のオーバーヘッドは通信それ自体以外の
  ところから発生する可能性があります。既にこれまでに述べてきましたが、プ
  ロセスの数はマシンに付けてあるプロセッサ数以下にするように構成すべきで
  す。でもどうやって動かすプロセス数を正確に決められるのでしょうか？

  最高のパフォーマンスを得るには、並列プログラムで動かすプロセス数は、そ
  のプログラムで起動するプロセスが同時に異なるプロセッサで動かせると思わ
  れる数と同じにすべきです。例えば、4 つのプロセッサを持つ SMP システム
  では、通常 1 つのプロセッサは何か別の目的(例えば WWW サーバ)に使われて
  いるとすると、3 つのプロセッサだけで並列プログラムを動かすことになるは
  ずです。おおまかにどのくらいのプロセスがシステムでアクティブなのかは、
  uptime コマンドで見られる「load average(平均負荷)」を見ればわかりま
  す。

  これとはまた別の方法があり、並列プログラムで使っているプロセスの実行優
  先順位を引き上げることが可能です。例えば、renice コマンドや nice() シ
  ステムコールです。優先度を上げるには、特権が必要になります。このアイ
  ディアは単純で、他のプロセスをプロセッサから追い出して、自分のプロセス
  をすべてのプロセッサで同時に動かしてしまおうという考えです。この考えを
  プロトタイプ版の SMP Linux を使用してもう少しきちんと実現したものが
  <http://www.rtlinux.org/> で、リアルタイムなスケジューリングを提供して
  います。

  SMP システムを並列処理マシンをただ一人で扱うユーザでないなら、2 つ以上
  の並列プログラムを同時に実行しようとすると、プログラム間で衝突が起こる
  恐れがあります。普通これを解決する方法は、ギャングスケジューリング
  (gang scheduling)をとります。すなわち、スケジューリングの優先度を操作
  して、いつでもただ 1 つの並列プログラムに関するプロセスだけが動いてい
  るようにします。しかし思い出してみてください。並行処理を行えば行うほど
  結果を得られにくくなり、スケジューラにオーバーヘッドが加わります。つま
  り例えば、4 つのプロセッサを持つマシンが、強制スケジュールを使って 2
  つのプログラムをそれぞれ 4 つのプロセスで動かすよりも、2 つのプログラ
  ムをそれぞれ 2 つのプロセスで動かす方が効率良いのは確かです。

  さらにもう 1 つこの問題をややこしくしている事があります。あるマシンで
  プログラムを開発していて、そのマシンは終日とても忙しく動いているが、夜
  間は並列処理に丸々使えるとします。プロセスすべてを立ち上げて正しく動く
  ようにコードを書いて、それをテストしなければいけませんが、昼間のテスト
  は遅いことを思い知らされると思います。実のところとても遅くなります。も
  し現在動いていない(他のプロセッサ上の)他のプロセスが変更した共有メモリ
  上の値をビジーウエイトしているプロセスがあるならば。同様の問題は、プロ
  セッサが 1 つしかないシステムでコードを開発したりテストしたりする時に
  も起こります。

  解決方法は、コードに呼び出しを組み込むことです。どんな場合でも、他のプ
  ロセスからの動きをループを使って待つようにします。そうすると Linux
  は、他のプロセスが動く機会を与えることができます。私は C のマクロを
  使って IDLE_ME のように呼び出しています。テストで動かす時には、cc
  -DIDLE_ME=usleep(1); ... とコンパイルし、「製品」として動かす場合には
  cc -DIDLE_ME={} ... とコンパイルしています。usleep(1) を呼び出すと、1
  ミリ秒間スリープするようになり、Linux のスケジューラがそのプロセッサ上
  で別のプロセスを選べるようになります。プロセスが使用可能なプロセッサの
  2 倍以上の数になると、コードの中で usleep(1) を使うと、使わない場合の
  10 倍速くなることも珍しくありません。

  2.3.  bb_threads

  bb_threads (「Bare Bones」(必要最低限の) threads)ライブラリは、
  <ftp://caliban.physics.utoronto.ca/pub/linux/> にある非常に簡素なライ
  ブラリです。Linux の clone() システムコールを使って実装されていま
  す。gzip tar ファイルでたった 7 KB です！ このライブラリは、2.4 で論じ
  た LinuxThreads ライブラリが世に出たことで過去のものになってしまいまし
  たが、bb_threads はまだまだ役に立ちます。小さく、シンプルで、とりあえ
  ず最初に Linux のスレッドを使用するには十分です。このソース・コードを
  読んでも、LinuxThreads のソースを眺めた時のように威圧感を感じることは
  ないのは確かです。まとめてみると、 bb_threads ライブラリは最初に試して
  みるのには良いライブラリですが、正直なところ大きなプロジェクトのコー
  ディングで使用するのには適していません。

  bb_threads ライブラリを使った基本的なプログラムの構築方法は、下記の通
  りです。

  1. プログラムは、1 つのプロセスとしてスタートさせること

  2. それぞれのスレッドで必要になるスタック空間の最大値を見積ること。大
     きく見積っても害はない(仮想記憶がそうであるように ;-)。しかし忘れな
     いで欲しいことは、スタックすべては単独の仮想アドレス空間に由来して
     いるため、大きいことはいいことだ、というわけにはいかないことであ
     る。64 K がお試しの大きさである。 bb_threads_stacksize(b) とする
     と、 b バイトに設定できる

  3. 次のステップは、必要となるすべてのロックを初期化することである。
     ロックのしくみはこのライブラリに組み込まれていて、0 から
     MAX_MUTEXES までの番号が設定でき、i というロックを初期化するに
     は、bb_threads_mutexcreate (i) と設定する

  4. 新しいスレッドを起こすには、ライブラリのルーチンを呼び出して何の関
     数をスレッドとして実行し、その関数に渡される引数は何かを指定する。
     新しいスレッドを開始するには、void を返り値とする関数である f を単
     独の arg を引数として持つ必要があり、 bb_threads_newthread(f, &arg)
     という感じで指定することになる。ここで f は void f(void *arg,
     size_t dummy) というように宣言しておくべきである。引数が 1 つ以上に
     なる場合は、引数の値で初期化してある構造体へのポインタを指定してす
     る

  5. 並列コードを実行する。実行に当っては、 bb_threads_lock(n) と
     bb_threads_unlock(n) を慎重に使用すること。n は整数で、どのロックを
     使用するかを指定する。このライブラリでのロックをかけることとロック
     を解除する操作は、アトミックにバスをロックする命令を使って、ありふ
     れたスピン・ロックで実現している。この方法では必要以上にメモリ参照
     の輻輳が生じ、プロセスを公平に実行することが保証できない

     bb_threads についているデモ・プログラムは正しくロックを扱えず、
     printf() が fnn や mainなどの関数内で同時に実行されてしまう。そのよ
     うなわけでこのデモは常に動くとは限らない。デモを非難するつもりはな
     いが、ロックには細心の注意が必要であることを強調しておきたい。ま
     た、LinuxThreads を使えばわずかではあるが使いやすくなることも付け加
     えたい

  6. あるスレッドが返り値を戻す時点で、そのプロセスが実際に破棄される…
     しかし、ローカルなスタック・メモリは自動的に解放されない。正確に言
     うと Linux はメモリの解放をサポートしておらず、そのメモリ空間は
     malloc() のメモリの空きリストに自動的に戻されない。つまり、親プロセ
     スは bb_threads_cleanup(wait(NULL)) を呼び出して、死んでしまった子
     プロセスのスペースを再利用のために再宣言しなければいけない

  下記の C プログラムはセクション 1.3 で論じたアルゴリズムを元に、2 つの
  bb_threads を使って円周率の近似値を求めています。

  ______________________________________________________________________
  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <sys/types.h>
  #include <sys/wait.h>
  #include "bb_threads.h"

  volatile double pi = 0.0;
  volatile int intervals;
  volatile int pids[2];      /* Unix PIDs of threads */

  void
  do_pi(void *data, size_t len)
  {
    register double width, localsum;
    register int i;
    register int iproc = (getpid() != pids[0]);

    /* set width */
    width = 1.0 / intervals;

    /* do the local computations */
    localsum = 0;
    for (i=iproc; i<intervals; i+=2) {
      register double x = (i + 0.5) * width;
      localsum += 4.0 / (1.0 + x * x);
    }
    localsum *= width;

    /* get permission, update pi, and unlock */
    bb_threads_lock(0);
    pi += localsum;
    bb_threads_unlock(0);
  }

  int
  main(int argc, char **argv)
  {
    /* get the number of intervals */
    intervals = atoi(argv[1]);

    /* set stack size and create lock... */
    bb_threads_stacksize(65536);
    bb_threads_mutexcreate(0);

    /* make two threads... */
    pids[0] = bb_threads_newthread(do_pi, NULL);
    pids[1] = bb_threads_newthread(do_pi, NULL);

    /* cleanup after two threads (really a barrier sync) */
    bb_threads_cleanup(wait(NULL));
    bb_threads_cleanup(wait(NULL));

    /* print the result */
    printf("Estimation of pi is %f\n", pi);

    /* check-out */
    exit(0);
  }
  ______________________________________________________________________

  2.4.  LinuxThreads

  LinuxThreads( <http://pauillac.inria.fr/~xleroy/linuxthreads/>)
  は、POSIX 1003.1c のスレッド標準規格に基づいて「すべてを共有する」こと
  を申し分なくしっかりと実装しています。他の POSIX 準拠のスレッドの移植
  と違い、LinuxThreads はカーネルのスレッド機能(clone())を使っており、こ
  れは bb_threads と同様な手法をとっています。POSIX 互換だとスレッドを
  使ったアプリケーションを他のシステムへ移植することが他スレッドと比べて
  かなり容易になり、チュートリアル類もいろいろ利用できます。つま
  り、Linux で大規模にスレッド化されたプログラムを開発するなら、間違いな
  くこのパッケージが適切です。

  LinuxThreads ライブラリを使った基本的なプログラムの構築のしかたは、下
  記の通りです。

  1. プログラムは、1 つのプロセスとしてスタートさせること

  2. 次のステップは、必要となるあらゆるロックを初期化することである。
     bb_threads のロックが番号を指定するのとは異なり、 pthread_mutex_t
     lock という変数を宣言するのが POSIX 流であ
     る。pthread_mutex_init(&lock,val) を使って、必要なそれぞれのスレッ
     ドを初期化する

  3. bb_threads と同様に、新しいスレッドを起こすにはライブラリのルーチン
     を呼び出し、何の関数をスレッドとして実行し、その関数に渡される引数
     は何かを指定する。しかし、POSIX ではユーザが pthread_t という変数を
     宣言してスレッドを区別する。スレッドを起こすには、pthread_t thread
     で f() を動かすには、pthread_create(&thread,NULL,f,&arg) を呼び出す

  4. 並列コードを動かすには、 pthread_mutex_lock(&lock) と
     pthread_mutex_unlock(&lock) 適切なところで慎重に使用すること

  5. pthread_join(thread,&retval) を使って、スレッドの後始末をすること

  6. C のコードをコンパイルする時には、-D_REENTRANT を使うこと

  次にあげるのは、円周率の計算を LinuxThreads を使って並列に行う例です。
  セクション 1.3 で使ったアルゴリズムが使用されていて、bb_threads と同様
  に 2 つのスレッドを並列に実行します。

  ______________________________________________________________________
  #include <stdio.h>
  #include <stdlib.h>
  #include "pthread.h"

  volatile double pi = 0.0;  /* Approximation to pi (shared) */
  pthread_mutex_t pi_lock;   /* Lock for above */
  volatile double intervals; /* How many intervals? */

  void *
  process(void *arg)
  {
  register double width, localsum;
  register int i;
  register int iproc = (*((char *) arg) - '0');

  /* Set width */
  width = 1.0 / intervals;

  /* Do the local computations */
  localsum = 0;
  for (i=iproc; i<intervals; i+=2) {
  register double x = (i + 0.5) * width;
  localsum += 4.0 / (1.0 + x * x);
  }
  localsum *= width;

  /* Lock pi for update, update it, and unlock */
  pthread_mutex_lock(&pi_lock);
  pi += localsum;
  pthread_mutex_unlock(&pi_lock);

  return(NULL);
  }

  int
  main(int argc, char **argv)
  {
  pthread_t thread0, thread1;
  void * retval;

  /* Get the number of intervals */
  intervals = atoi(argv[1]);

  /* Initialize the lock on pi */
  pthread_mutex_init(&pi_lock, NULL);

  /* Make the two threads */
  if (pthread_create(&thread0, NULL, process, "0") ||
    pthread_create(&thread1, NULL, process, "1")) {
  fprintf(stderr, "%s: cannot make thread\n", argv[0]);
  exit(1);
  }

  /* Join (collapse) the two threads */
  if (pthread_join(thread0, &retval) ||
    pthread_join(thread1, &retval)) {
  fprintf(stderr, "%s: thread join failed\n", argv[0]);
  exit(1);
  }

  /* Print the result */
  printf("Estimation of pi is %f\n", pi);

  /* Check-out */
  exit(0);
  }
  ______________________________________________________________________

  2.5.  System V の共有メモリ

  System V の IPC(プロセス間通信(Inter-Process Communication))は、システ
  ムコールを数多く提供してます。メッセージ・キューやセマフォ、共有メモリ
  といったしくみです。もちろん本来このしくみは、複数のプロセスをプロセッ
  サが 1 つのシステムで動かすことを目的としていました。しかし、SMP Linux
  で複数のプロセス間での通信にも使えるはずです。たとえどんなプロセッサで
  あっても。

  これらのシステムコールの使い方の説明に入る前に、System V の IPC に含ま
  れているセマフォやメッセージ通信のようなシステムコールを使用すべきでは
  ないことを理解しておいてください。どうしてでしょうか？ SMP Linux では
  これらの機能は概して処理が遅く、順次実行されるからです。説明はこれで十
  分ですね。

  共有メモリのセグメントに一様にアクセスするために、プロセスのグループを
  作成する基本的な手続きは次の通りです。

  1. プログラムは、1 つのプロセスとしてスタートさせること

  2. 普通、それぞれの並列処理のプログラムを自分の共有メモリのセグメント
     を使って動かしたいかと思われるので、その場合は shmget() を呼んで必
     要な大きさのセグメントを新しく作成する。またこのシステムコールは、
     既存の共有メモリの ID を得るためにも使われる。どちらの場合も返り値
     は共有メモリの ID か、エラーを表す -1 である。例えば、b バイトの大
     きさの共有メモリを作るなら、shmid = shmget(IPC_PRIVATE, b,
     (IPC_CREAT | 0666)) と呼び出す

  3. 次のステップはプロセスにこの共有メモリをアタッチして、文字通りこの
     プロセスの仮想メモリマップに加える。プログラマーは shmat() を呼ぶこ
     とで、仮想アドレス上のどこに共有メモリのセグメントがあるのかがわか
     る。しかしそのアドレスはページ境界に合っていなければならない(すなわ
     ち getpagesize() が返す大きさはページの大きさの倍数になっていて、通
     常は 4096 バイトである)。また、以前そのアドレスをメモリにマップした
     ものは無効になる。つまり以前マップしていたもののかわりに、そのアド
     レスを使用するようにシステムに指示するのである。どちらの場合も返り
     値はマップされたセグメントの仮想アドレスへのポインタである。コード
     は、shmptr = shmat(shmid, 0, 0) となる

     注意すべき点は、共有メモリセグメントに置く共有する static 変数はす
     べて構造体のメンバーにして宣言することで確保できる、ということだ。
     そして shmptr をその構造体のポインタにする。このテクニックを使う
     と、x は shmptr->x と参照できる

  4. この共有メモリのセグメントを解放しなければならない時は、このセグメ
     ントにアクセスする最後のプロセスが終了するかデタッチする場合であ
     る。 shmctl() を呼び出して、初期状態に設定する必要がある。コード
     は、 shmctl(shmid, IPC_RMID, 0) のようになる

  5. Linux 標準の fork() システムコールを使って、必要なプロセスを立ち上
     げる…。それぞれのプロセスは共有メモリのセグメントを継承する

  6. プロセスが共有メモリのセグメントを使い終ったら、共有メモリのセグメ
     ントを必ずデタッチする。これは shmdt(shmptr) とすれば良い

  このようにいくつかのシステムコールが設定の際に必要となりますが、一度共
  有メモリのセグメントが確保されれば、どのプロセッサがメモリ上の値を変え
  ても自動的にすべてのプロセッサから見ることができます。最も大切なこと
  は、システムコールのオーバーヘッドなしに操作ができることです。

  System V の共有メモリのセグメントを使った C のプログラム例です。これは
  円周率を計算するもので、セクション 1.3 で使ったのと同じアルゴリズムを
  利用しています。

  ______________________________________________________________________
  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <fcntl.h>
  #include <sys/ipc.h>
  #include <sys/shm.h>

  volatile struct shared { double pi; int lock; } *shared;

  inline extern int xchg(register int reg,
  volatile int * volatile obj)
  {
    /* Atomic exchange instruction */
  __asm__ __volatile__ ("xchgl %1,%0"
                        :"=r" (reg), "=m" (*obj)
                        :"r" (reg), "m" (*obj));
    return(reg);
  }

  main(int argc, char **argv)
  {
    register double width, localsum;
    register int intervals, i;
    register int shmid;
    register int iproc = 0;;

    /* Allocate System V shared memory */
    shmid = shmget(IPC_PRIVATE,
                   sizeof(struct shared),
                   (IPC_CREAT | 0600));
    shared = ((volatile struct shared *) shmat(shmid, 0, 0));
    shmctl(shmid, IPC_RMID, 0);

    /* Initialize... */
    shared->pi = 0.0;
    shared->lock = 0;

    /* Fork a child */
    if (!fork()) ++iproc;

    /* get the number of intervals */
    intervals = atoi(argv[1]);
    width = 1.0 / intervals;

    /* do the local computations */
    localsum = 0;
    for (i=iproc; i<intervals; i+=2) {
      register double x = (i + 0.5) * width;
      localsum += 4.0 / (1.0 + x * x);
    }
    localsum *= width;

    /* Atomic spin lock, add, unlock... */
    while (xchg((iproc + 1), &(shared->lock))) ;
    shared->pi += localsum;
    shared->lock = 0;

    /* Terminate child (barrier sync) */
    if (iproc == 0) {
      wait(NULL);
      printf("Estimation of pi is %f\n", shared->pi);
    }

    /* Check out */
    return(0);
  }
  ______________________________________________________________________

  この例では、IA32 のアトミックな交換(exchange)命令を使ってロックを実現
  しています。パフォーマンスと移植性をさらに良くするには、バスをロックす
  る命令を回避するような同期のしくみに置き換えてください(セクション 2.2
  で論じたように)。

  コードをデバッグする場合は、現在使われている System V の IPC 機能の状
  態をレポートする ipcs コマンドを知っておくと便利です。

  2.6.  メモリマップ・システムコール

  システムコールはファイル入出力をともなうと重くなります。実際に、ユーザ
  レベルでのバッファを介したファイル入出力ライブラリ(getchar()、
  fwrite() 等)があるのはこのためです。しかし、ユーザレベルでのバッファ
  は、複数のプロセスが同じ書き込み可能なファイルにアクセスした場合には役
  立ちません。またこれを管理するオーバーヘッドもばかになりません。 BSD
  UNIX はこの問題を解決するため、システムコールを 1 つ追加しました。それ
  はファイルの一部をユーザのメモリ空間にマップして、仮想メモリのページン
  グ機構を利用して更新を行うものです。Sequent がこれと同様のしくみを何年
  も前から自社の共有メモリを使った並列処理の機能として提供してきました。
  (とても古い)man を見ると、とても否定的なコメントが散見されます
  が、Linux では少なくとも基本的な機能のいくつかは正しく動作します。この
  システムコールが複数のプロセスで利用できる共通のメモリ上のセグメントを
  マップするのに使用されるのはまれです。

  本質的に Linux の mmap() は、セクション 2.5 の基本的な手続きの 2、3、4
  で説明した System V の共有メモリの枠組みで置き換えることができます。

  ______________________________________________________________________
  shmptr =
      mmap(0,                        /* system assigns address */
           b,                        /* size of shared memory segment */
           (PROT_READ | PROT_WRITE), /* access rights, can be rwx */
           (MAP_ANON | MAP_SHARED),  /* anonymous, shared */
           0,                        /* file descriptor (not used) */
           0);                       /* file offset (not used) */
  ______________________________________________________________________

  munmap() は System V の共有メモリの shmdt() システムコールと同じ機能で
  す。

  ______________________________________________________________________
  munmap(shmptr, b);
  ______________________________________________________________________

  私見ですが、System V の共有メモリのかわりに mmap() を使っても、それほ
  どメリットはないと思います。

  3.  Linux システムでクラスタを組む

  このセクションでは、Linux を使ったクラスタによる並列処理をおおまかに見
  て行こうと思います。今クラスタは大流行しているだけでなく、アプローチの
  しかたも非常にバラエティに富みます。ごく普通にネットワークにワークス
  テーションをつないで構成したものから(これですね)、特注の並列マシンまで
  あります。そこで動いているプロセッサ・ノードがたまたま Linux PC を使っ
  ていたということもあります。またソフトウェアも相当な数が Linux マシン
  で組んだクラスタを使った並列処理用に用意されています。

  3.1.  なぜクラスタなのか？

  クラスタを組んで並列処理を行うと、大きなメリットがいくつか挙げられま
  す。

  o  クラスタを構成する個々のマシンは単独システムとして完結していて、広
     範囲に他の演算用途に利用できる。このことから、クラスタを組んで並列
     に演算するということは、まさに各人の机上で「無駄な時間」を費してい
     るワークステーションすべてに仕事をやらせる、ということに他ならな
     い。実際そのような無駄な時間を集めてくることはたやすいことでない
     が、同僚のスクリーンセーバーは遅くなるだろうが、演算は終らせること
     ができるだろう

  o  ここにきてネットワーク化したシステムが急増したことで、クラスタを構
     築するために必要なハードウェアの多くが多量に販売され、それにともな
     い結果として安価な「商品」が出まわっている。さらにコストを押える手
     段として、クラスタ個々に必要なビデオカード、モニター、キーボードは
     たった一揃えしかいらない、ということが挙げられる。(Linux を最初にイ
     ンストールする場合にそれらを交換して取り付ければ良い。動き出してし
     まえば、普通の Linux が載っている PC は、「コンソール」は不要)。こ
     れと比べると、SMP や付加プロセッサは非常に商品市場が狭く、ユニット
     単位のパフォーマンスは高価になりがちである

  o  クラスタ化した計算は、非常に大規模なシステムにまで拡張できる。現状
     では、プロセッサを 4 つ以上載せた Linux 互換の SMP を見つけるのは非
     常に困難だが、一般的に利用できるネットワーク機器を使って 16 台のマ
     シンまで 1 つのクラスタとして構築できる。もう少し手をかければ、何
     百、何千というマシンもネットワーク化できる。実際、インターネットす
     べてを 1 つの巨大なクラスタとみなすこともできる

  o  クラスタの中で「おかしなマシン」を交換することは、部分的に壊れた
     SMP を修理することに比べて、取るに足らないほど簡単ということは事実
     である。つまり慎重に設計、設定したクラスタは非常に高い可用性をもた
     らすことを示している。これは重要なことで、中断できない重要なサービ
     スを提供する特定のアプリケーションに対してだけではなく、普通に利用
     している十分なプロセッサを搭載したシステムにおいてマシンがよくおか
     しくなる場合にも当てはまる。(例えば、PC がおかしくなる平均時間が 2
     年だとしても、32 台でクラスタを組んでいるシステムでは、少なくとも 6
     ヵ月以内に 1 台がおかしくなる可能性がある。これは非常に高い確率であ
     る)

  それではクラスタがフリーかつ安価な上に、拡張性が高く、様々な用途に利用
  可能…なのに、なぜ皆さんはクラスタを使用しないのでしょうか？それは多分
  下記のような理由です。

  o  例外を除いて、ネットワーク機器は並列処理向けに設計されていない。通
     常レベルの遅延が非常に高く、SMP や付加プロセッサシステムに比べ帯域
     幅が相対的に狭い。例えば、SMP の遅延は通常数ミリ秒以上にはならない
     が、クラスタにおいては数 100 から数 1000 ミリ秒生じるのが普通であ
     る。SMP 通信帯域幅では 100 MB/秒以上の値が度々でる。最速のネット
     ワーク機器 (例えば「ギガビット・イーサネット」は速度面で SMP に匹敵
     するが、一般的に利用しているネットワークは、その 1/10 〜 1/1000 の
     速度しか得られない

     ネットワーク機器のパフォーマンスを稼ぐには、クラスタのために独立し
     たネットワークだけでは不十分である。そのネットワークが他のトラ
     フィックの影響を受けないとしても、「たまたまネットワークに接続して
     いるマシン」を使うと、クラスタ向け設計したシステムよりも実際にパ
     フォーマンスが悪くなるケースが多い

  o  クラスタを 1 つのシステムとして扱えるソフトウェアがほとんど皆無。例
     えば、ps コマンドは 1 台の Linux システム上のプロセスをレポートする
     だけで、Linux システムで構成されたクラスタ全体のすべてのプロセスを
     レポートできるわけではない

  つまり大筋ではクラスタは秘めたる潜在能力があるものの、アプリケーション
  の大部分でその能力を引き出すことが非常に困難なようです。ただ幸いなの
  は、クラスタ環境にマッチするようにプログラムのパフォーマンス向上を支援
  するソフトウェアがかなりたくさん存在していること、そしてより広範囲なプ
  ログラムがクラスタ環境で高いパフォーマンスを上げられるように特別に設計
  したネットワークが存在することです。

  3.2.  ネットワーク機器

  コンピュータのネットワーク化は、急成長の分野です…が、そんなことは既に
  ご存じでしょう。現在も広がりつつあるネットワーク技術とその製品は継続し
  て開発されており、それらの大部分はひとかたまりのマシン群(例えば、それ
  ぞれの PC で Linux が動いている)から構成されている並列処理クラスタでも
  利用できます。

  ただ残念なことに、どのネットワーク技術も問題すべてを完璧に解決するわけ
  ではありません。実際問題、アプローチのしかた、コスト、パフォーマンスの
  問題がすぐに片づくとは思えません。例えば、一般的に販売しているハード
  ウェアの場合、ネットワーク化するのには 5 ドル未満から 4,000 ドルまでの
  幅があります。提供できる帯域幅や遅延もまちまちで、大きく分けて 4 段階
  以上になります。

  特定のネットワークについて勉強をしようとする前に、ネットワークというも
  のは、常々変化しているものであることを知っておいてください(
  <http://www.linux.org.uk/NetNews.html> に Linux 関連のネットワークにつ
  いてのニュースがあります)。そしてネットワークの中には、正確なデータが
  得られにくいものがあるということも知っておいてください。

  確かな情報が得られなかった部分には、? と書いておきます。この話について
  はかなりの時間を割きましたが、間違いがないとも限りませんし、重要な事柄
  が抜けていないとも限りません。訂正する箇所や追加したいことがあれ
  ば、pplinux@ecn.purdue.edu まで電子メールをください。

  LAN 技術の性能評価をまとめたものが、
  <http://www.system75.com/documentation/dcom/lan-technology.html> にあ
  り、様々なタイプのネットワークや LAN 標準 の特徴がいくつか載っていま
  す。しかしこの HOWTO では、Linux クラスタの構築に密接に関係する属性に
  焦点を当ててまとめてみます。このセクションでは、まずそれぞれのネット
  ワークの特徴を簡単にまとめてから、その意味について定義していきます。

     Linux の対応:
        答えが no なら結果は言うまでもない。そうでなければ、プログラムか
        らネットワークにアクセスするために必要となる基本的なインタフェー
        スについて説明する。ネットワーク機器のほとんどは、カーネルのドラ
        イバ経由で接続していて、普通は TCP や UDP 通信に対応している。他
        のネットワークのいくつかは、よりダイレクトに(例えばライブラリを
        使って)接続していて、カーネルを経由しないことで遅延を抑えてい
        る。

        【訳註：現在の Linux の対応は、このドキュメントが書かれた時点よ
        りも広がっています。カーネル 2.2.18 でサポートしているものは、各
        項目で訳註で補足します。それ以外は…ごめんなさい】

        数年前であれば、OS のシステムコールによって浮動小数点演算装置に
        アクセスすることが当たり前のこととして行われていたが、現在では
        まったく意味のない方法になっている。私見だが、並列処理プログラム
        を実行するのに OS を呼び出してプロセッサ間の通信を行うことはス
        マートではないと思われる。問題なのは、コンピュータがいまだにこれ
        らを統合した通信手段を持ち合わせていない点にあり、そのためにカー
        ネルを経由しない解決手段が移植性の問題を抱えてしまいがちになる。
        近い将来もっと色々な話題を聞くようになると思われることがある。そ
        れは仮想インタフェース・アーキテクチャ(Virtual Interface (VI)
        Architecture)( <http://www.viarch.org/>)のような新しい体系であ
        る。これは、ネットワーク・インタフェースに対する操作を標準化した
        手法をとることで、通常の OS のシステムコールを使用しないようにす
        る。この VI には、Compaq と Intel、Microsoft が参加しており、数
        年後に登場する SAN (System Area Network) に大きな影響を与えるこ
        とは確実である

     最大帯域幅:
        皆が注目する値。理論的に達成できる最高値をあげておく。実際の成果
        は場合によって異なるだろう

     最低遅延:
        私見では、最大帯域幅よりも皆が注目すべき値である。上記と同じにな
        るが、ここでは現実的ではない最良値をあげておく。しかし、この値は
        少なくともハードウェアとソフトウェア両者による遅延の原因をすべて
        含んでいる。多くの場合ネットワークの遅延は数マイクロ秒程度で、こ
        れ以上大きな値になるとハードウェアとソフトウェアのインタフェース
        のレベルで効率が悪いことを示している

     利用できるのは:
        簡単に言えば、このタイプのネットワーク機器を手に入れられるかどう
        かを示す。市販の製品は多くのベンダーが提供しており、いろいろと利
        用できるが、価格が最も大きな要因となっている。複数のベンダーから
        出ている製品は 1 つ以上の競合するベンダーから利用可能となってい
        るが、相違点がかなりあり、相互接続の問題も含んでいる。単独ベン
        ダーのネットワークでは、業者のいいなりになってしまう(親切な業者
        もいるかもしれないが)。パブリック・ドメインな設計では、誰かが販
        売することはないが、その一部を購入したり、自分で作成してしまうこ
        ともできる。研究試作品がそれに当り、一般的には部外者がすぐにでも
        使える形になっていないし、手に入れることすらできない

     ポートもしくはバスのインタフェース:
        どのようにネットワークに接続するのか？ 最高のパフォーマンスで最
        も一般的に普及しているのは PCI バスのインタフェース・カードであ
        る。その他にも EISA や VESA Local バス(VL bus)、ISA バスカードが
        ある。ISA は初期に登場したもので、パフォーマンスが出ないカードで
        あるものの、いまだに使用されている。EISA は PCI マシンの多くでも
        う 1 つのバスとして使われてはいるが、ほとんどカードが存在しな
        い。今日では、VL バスはほとんど目にしない(
        <http://www.vesa.org/> ではそうではないと言っているが)

        もちろん、PC ケースを開けずに使えるインタフェースであれば、どん
        なものでも多少なりとも魅力がある。IrDA や USB インタフェースは登
        場する割合が多くなりつつある。標準パラレル・ポート(SPP)は、プリ
        ンターを取り付ける時に使用するが、最近では ISA バスの外部拡張と
        同じ様に見なされている。このインタフェースの新しい機能拡張は
        IEEE 1284 規格で、EPP と ECP を拡張している。また、いにしえの信
        頼性がある低速な RS232 シリアル・ポートもある。VGA ビデオコネク
        タやキーボード、マウス、ゲームポート…を使ってマシンを接続した例
        は聞いたこともないのでこれ以上触れない

     ネットワーク構成:
        バスは、1 本のケーブルであったり複数のケーブルの集まりやファイバ
        であったりする。ハブは小さなケースで、種類の異なるケーブルやファ
        イバーを差し込んで、ネットワークに接続する手段を提供する。スイッ
        チング・ハブは、複数接続しつつ同時にデータを転送できる。

     接続マシン当りのコスト:
        ここではこの値の利用方法を説明する。例えば、ネットワークへの接続
        を考慮に入れないと、クラスタで使用するノードの 1 つとして PC を
        購入するには 2,000 ドルのコストがかかる。ファースト・イーサネッ
        トを追加すると、ノード当り 2,400 ドルになる。そのかわりに
        Myrinet を追加すると、およそ 3,800 ドルになる。もし 20,000 ドル
        使えるなら、ファースト・イーサネットが付いた 8 台の マシンもしく
        は Myrinet が付いた 5 台のマシンが持てることになる。もちろん、複
        数のネットワークを利用するのも手頃な価格で実現できる。例えば
        20,000 ドルあれば、ファースト・イーサネットと TTL_PAPERS をつけ
        た PC が 8 台購入できる。使用するネットワークの種類を選択して、
        敷設すれば、アプリケーションを最速に動かすクラスタを実現できる

        これを読まれた時点では、これらの値が間違っているかもしれない…残
        念だが、確かに既に間違っている。大幅に値引きしているかもしれない
        し、読者が特別に契約を結んでいるかもしれない等で。それでも、ここ
        で挙げた価格は全体的に間違った選択をしてしまう程おかしなものでは
        ない。アプリケーションが特別な性質を持っていたり、クラスタ化した
        PC が比較的高価であっても、博士号を持った人間(私もその一人だが
        ;-)が高価なネットワークと見なすことはない。それを納得しさえすれ
        ば。

  では、上記の条件を飲んでもらったとして、見ていくことにしましょう…

  3.2.1.  ArcNet

  o  Linux のサポート: カーネル組み込みのドライバ

  o  最高帯域幅: 2.5 M ビット/秒

  o  最低遅延: 1,000 マイクロ秒 ?

  o  利用できるのは: 複数のベンダーのハードウェア

  o  ポートもしくはバスのインタフェース: ISA

  o  ネットワーク構成: ダムハブもしくはバス接続(論理的にはリング構成)

  o  接続マシン当りのコスト: 200 ドル

  ARCNET は LAN の一種で、そもそもは組込み用途のリアルタイム制御システム
  用です。イーサネットのように、物理的にはバス上にタップを設けたり、ハブ
  を 1 つ以上置いて構築します。イーサネットと違う点は、トークンベースの
  プロトコルを使っていて、論理的にはネットワークをリングとして構成しま
  す。パケットのヘッダが小さく(3 もしくは 4 バイト)、メッセージを 1 バイ
  トという小さなデータとして送ります。つまり ARCNET は、イーサネットより
  も遅延も一定値以下で押えられる等、確実にデータを伝送する機能を持ってい
  ます。ただ残念なのは、イーサネットよりも速度が遅い上、あまり利用されて
  おらず、そのせいで価格も高くなっています。もっと詳しい情報は、the
  ARCNET Trade Association  <http://www.arcnet.com/> で見られます。

  3.2.2.  ATM

  o  Linux のサポート: カーネル組込みのドライバと AAL* ライブラリ

  o  最高帯域幅: 155 M ビット/秒 (まもなく 1,200 M ビット/秒)

  o  最低遅延: 120 マイクロ秒

  o  利用できるのは: 複数ベンダーのハードウェア

  o  ポートもしくはバスのインタフェース: PCI

  o  ネットワーク構成: スイッチング・ハブ

  o  接続マシン当りのコスト: 3,000 ドル

  過去数年に渡りあなたが昏睡状態になかったとすれば、ATM(Asynchronous
  Transfer Mode)はどんなに明るい未来があり…といった話をいろいろ聞いてい
  るはずです。 ATM は HiPPI より安価で、ファースト・イーサネットよりも高
  速です。また非常に長距離間で使用できることから、電話会社が注目していま
  す。 ATM のネットワーク・プロトコルは、ソフトウェアへのインタフェース
  に対するオーバーヘッドが少なくなるように設計してあり、とても効率良く小
  さなメッセージやリアルタイムな通信(例えばデジタル・オーディオやビデオ)
  を扱えるようになっています。また現状 Linux で利用できる帯域幅が最も広
  いプロトコルの 1 つでもあります。ただ ATM は安価とはいえず、ベンダー間
  で互換性の問題がいくつか生じてしまっています。Linux における ATM 関連
  の開発の概要は、  <http://lrcwww.epfl.ch/linux-atm/> で見られます。

  3.2.3.  CAPERS

  o  Linux のサポート: AFAPI ライブラリ

  o  最高帯域幅: 1.2 Mビット/秒

  o  最低遅延: 3 マイクロ秒

  o  利用できるのは: 市販のハードウェア

  o  ポートもしくはバスのインタフェース: SPP

  o  ネットワーク構成: 2 台のマシン間をケーブルで接続

  o  接続マシン当りのコスト: 2 ドル

  CAPERS(Cable Adapter for Parallel Execution and Rapid Synchronization)
  は、パーデュ大学の Electrical and Computer Engineering で行われている
  PAPERS プロジェクト  <http://aggregate.org/AFAPI/> から派生したもので
  す。この方式のポイントは、一般的に使われている「LapLink」と呼ばれる標
  準プリンターポート間で使用するケーブルを使って、ソフトウェアで実現する
  プロトコルです。PAPERS ライブラリで実装し、2 台の Linux PC を接続しま
  す。このアイディアは拡張性はないものの、価格を気にする必要がありませ
  ん。 TTL_PAPERS を使えば、システムのセキュリティを向上できます。カーネ
  ルにパッチを当てた方がいいのですが、必須ではありません。
  <http://aggregate.org/AFAPI/> を参照してください。

  【訳註：PAPERS 関連の資料は、著者がケンタッキー大学へ異動されたことで
  <http://aggregate.org/AFAPI/> へ移っています】

  3.2.4.  イーサネット

  o  Linux のサポート: カーネル組込みのドライバ

  o  最大帯域幅: 10 M ビット/秒

  o  最低遅延: 100 マイクロ秒

  o  利用できるものは: 市販のハードウェア

  o  ポートもしくはバスのインタフェース: PCI

  o  ネットワーク構成: スイッチングもしくはダムハブか、ハブを使わないバ
     ス接続

  o  接続マシン当りのコスト: 100 ドル (ハブ無しだと 50 ドル)

  ここ数年来、ネットワーク技術の標準と言えば 10 M ビット/秒のイーサネッ
  トを指していました。イーサネットのインタフェースカードは良いものでも
  50 ドル以下で購入できます。またかなりの数の PC には、マザーボード上に
  もともとイーサネットコントローラがついています。それ程負荷の高くない
  ネットワークなら、イーサネットはハブを使わずに複数のタップを設けて構成
  しても十分です。最小限のコストで作った構成でも 200 台までサービスを提
  供できますが、並列処理には向かない環境です。ダムハブを設けてもパフォー
  マンスを上げることはまったくできません。しかしスイッチング・ハブを設置
  すれば、帯域幅を一杯まで使って同時に通信が可能になり、コストもポート当
  り 100 ドル程度ですみます。Linux はイーサネットのインタフェースをかな
  り広範囲にサポートしていますが、インタフェースのいろいろなハードウェア
  間でかなりパフォーマンスに差がでてしまうことを気に止めておいてくださ
  い。the Hardware Compatibility HOWTO を見れば、どれがサポートされてい
  て、どのくらいきちんと動くかについてのコメントが読めます。
  <http://www.scyld.com/network/index.html> も見てください。

  パフォーマンスを上げる興味深い方法があります。それは、NASA の CESDIS
  (the Center of Excellence in Space Data and Information Sciences)で行
  われている Beowulf プロジェクト( <http://www.beowulf.org/>)です。ここ
  では、16 台のマシンで Linux クラスタを実現しています。このプロジェクト
  には Donald Becker 氏というイーサネットカードのドライバを数多く作成し
  た方がいて、互いに見えない複数のイーサネットで構成しているネットワーク
  にまたがる負荷分散を実現するために開発を行っています (つまり、同じネッ
  トワークアドレスを共有するということ)。この負荷分散は、 Linux の標準的
  なディストリビューションに最初から含まれていて、ソケットレベルの操作よ
  りも下位の層には隠されています。ハブのコストはばかになりません。それぞ
  れのマシンを 2 つ以上のハブに接続しないで済ますか、ダムハブでイーサ
  ネットのネットワークに接続できれば、費用をかけずにパフォーマンスを上げ
  るとても良い方法となります。実際に、 1 台のマシンがネットワークの性能
  のボトルネックになっている状況では、隠れたネットワークを使った負荷分散
  は、1 つのスイッチング・ハブで構成したネットワークよりもパフォーマンス
  に優れています。

  3.2.5.  イーサネット(ファースト・イーサネット)

  o  Linux のサポート: カーネル組込みのドライバ

  o  最大帯域幅: 100 M ビット/秒

  o  最低遅延: 80 マイクロ秒

  o  利用できるものは: 市販のハードウェア

  o  ポートもしくはバスのインタフェース: PCI

  o  ネットワークの構成: スイッチングもしくはダムハブ

  o  接続マシン当りのコスト: 400? ドル

  「速いイーサネット」と名前が付けられていますが、かなり異なった技術を採
  用しています。しかし、ハブベースで 100 M ビット/秒を実現するイーサネッ
  トとして、それ以前の「10 BaseT」と呼ばれる 10 M ビット/秒で使われるデ
  バイスとケーブルと多少は互換性があるものとしてよく引き合いに出されま
  す。大方の予想通り、イーサネットと言われるものすべてはおよそ大きな市場
  向けに価格設定してあり、そのインタフェースは 155 M ビット/秒の ATM
  カードに比べると取るに足らない価格設定になっています。難点は 1 本の
  100 M ビット/秒の「バス」(ダムハブを使用)の帯域を分割して複数のマシン
  がまとまって利用すると、恐らくスイッチング・ハブを使ってそれぞれのマシ
  ンが 10 M ビット/秒をフルに利用している 10 M ビット/秒のイーサネットと
  平均して同じくらいのパフォーマンスが出せない点にあります。

  それぞれのマシンが同時に 100 M ビット/秒を利用できるスイッチング・ハブ
  はとても高価ですが、価格は日々下がっていてダムハブと比較してネットワー
  ク全体としての帯域はとても広くできます。ATM スイッチが高価になっている
  わけは、ATM のセル(イーサネットと比べて小さい)それぞれをスイッチしない
  といけないからです。ファースト・イーサネット用のスイッチの中には、ス
  イッチする頻度が低いと思われる場合に、効果を発揮するものがあります。そ
  れを実現している技術は、スイッチを通過する間の遅延は低いのですが、ス
  イッチのパスが変更されると、数ミリ秒の遅延が発生してしまいす…。経路の
  変更が頻繁に起こる場合は、そのようなスイッチは避けましょう。カードやド
  ライバについてのいろいろな情報は、 <http://www.scyld.com/network/> を
  見てください。

  またイーサネットについては、NASA の Beowulf プロジェクト
  <http://www.beowulf.org/>において複数のファースト・イーサネット間で負
  荷分散を行い、パフォーマンスを向上させる技術を開発していることも忘れな
  いでください。

  【訳註：Beowulf プロジェクトは、Don Becker 氏をはじめとする Beowulf プ
  ロジェクトのメンバーによって設立された Scyld Computing Corporation
  <http://www.scyld.com> にその活動の場が移行しています。

  3.2.6.  イーサネット(ギガビット・イーサネット)

  o  Linux のサポート: カーネル組込みのドライバ

  o  最大帯域幅: 1,000 M ビット/秒

  o  最低遅延: 300 マイクロ秒 ?

  o  利用できるものは: 複数ベンダーのハードウェア

  o  ポートもしくはバスのインタフェース: PCI

  o  ネットワークの構成: スイッチング・ハブもしくは FDR (全二重リピータ
     ハブ)

  o  接続マシン当りのコスト: 2,500 ドル ?

  ギガビット・イーサネットについてはよくわかりません。
  <http://www.cis.ohio-state.edu/~jain/refs/gbe_refs.htm> でなぜイーサ
  ネットと呼ぶかの技術的な説明がありますが…しかし正しくはギガビット・
  イーサネットが安価かつ一般市場向けであり、IP をもともとサポートしたコ
  ンピュータ向けのネットワークをターゲットにしているということに他なりま
  せん。しかし現在の価格を見ると、まだギガビット・イーサネットの機材は構
  築するのにはまだ心配なところがあるのも事実です。
  他のイーサネット技術とは異なり、ギガビット・イーサネットはフロー制御の
  機能を持っていて、より信頼性の高いネットワークが構築できるはずです。
  FDR もしくは全二重のリピータ、単純な多重回線などバッファリングや局所的
  なフロー制御を行うことで、パフォーマンスが向上します。スイッチング・ハ
  ブの大部分は、既存のギガビット・イーサネットを利用できる部品を新しいイ
  ンタフェース・モジュールとして組み込んでいます。スイッチや FDR 製品は
  少なくとも次のメーカーから出荷されていたり、製品化がアナウンスされてい
  ます。 <http://www.acacianet.com/>、 <http://www.baynetworks.com/>、
  <http://www.cabletron.com/>、 <http://www.networks.digital.com/>、
  <http://www.extremenetworks.com/>、 <http://www.foundrynet.com/>、
  <http://www.gigalabs.com/>、 <http://www.packetengines.com/>、
  <http://www.plaintree.com/>、 <http://www.prominet.com/>、
  <http://www.sun.com/>、 <http://www.xlnt.com/>。

  Linux のドライバは  <http://www.scyld.com/network/yellowfin.html> に
  Packet Engines  <http://www.packetengines.com/> 製の「Yellowfin」 G-
  NIC 用があります。 Linux での初期段階のテストでは、高性能な 100 M ビッ
  ト/秒のファースト・イーサネットが出せる帯域幅のおよそ 2.5 倍の性能がで
  ています。ギガビット・イーサネットのネットワークでは PCI バスを慎重に
  チューニングすることがとても大切になります。ドライバが改良され、他の
  NIC 用の Linux ドライバが後を追って出てくることは間違いありません。

  【訳註：上記ドライバに加えて、Alteon の AceNIC と 3Com の
  3C985/NetGear GA620 Gigabit、Packet Engines の GNIC-II
  (Hamachi)、SysKonnect の SK-98xx がサポートされています】

  3.2.7.  FC (ファイバー・チャネル)

  o  Linux のサポート: no

  o  最大帯域幅: 1,062 M ビット/秒

  o  最低遅延: ?

  o  利用できるのは: 複数ベンダーのハードウェア

  o  ポートもしくはバスのインタフェース: PCI?

  o  ネットワークの構成: ?

  o  接続マシン当りのコスト: ?

  FC(ファイバー・チャネル)の目指すところは、高性能なブロック型の入出力
  (FC のフレームは 一度に最大 2,048 バイトのデータを伝送できます)で、特
  にディスクや他の記憶装置をコンピュータ経由で接続せずに FC に直接接続し
  て共有できます。帯域幅に関していえば、FC は 比較的高速なものに分類で
  き、 133 〜 1,062 M ビット/秒の速さの範囲で動作します。FC がハイエンド
  の SCSI にとってかわるほど一般的になれば、急速に安価な技術になると思わ
  れます。現状は高価であり、Linux はサポートしていません。FC について
  は、 the Fibre Channel Association が維持管理している
  <http://www.fibrechannel.com/> が参考文献として良いでしょう。

  【訳註：上記サイトの日本版は、 <http://www.fcaj.org/> です。また、
  Compaq の Fibre Channel 64-bit/66Mhz HBA(SCSI low-level drivers とし
  て)や Interphase の 5526 Tachyon chipset based adaptor(Token がサポー
  トされています】

  3.2.8.  FireWire (IEEE 1394)

  o  Linux のサポート: no

  o  最大帯域幅: 196.608 M ビット/秒 (まもなく、393.216 M ビット/秒にな
     る)

  o  最低遅延: ?

  o  利用できるのは: 複数ベンダーのハードウェア

  o  ポートもしくはバスのインタフェース: PCI

  o  ネットワークの構成: ループしていないなら何でも(自動設定)

  o  接続マシン当りのコスト: 600 ドル

  FireWire( <http://www.apple.com/firewire/>)は、IEEE 1394-1995 で標準化
  されていて、安価で高速なデジタルネットワークを家庭用電化製品にもたらす
  ことを目指しています。現在の代表的な用途はデジタルビデオカムとコン
  ピュータの接続ですが、そもそもは SCSI の代替からホームシアターにある機
  材間の接続までをカバーすることを目指しています。65,536 個のデバイスが
  接続可能で、ループしていないバス型やブリッジ型であればどんな接続のしか
  たでもかまいません。また機器を抜き差しすると、自動的に構成を検知するこ
  とができます。小さく(4 バイト。「quadlet」と呼ばれている)、遅延の少な
  いメッセージがサポートされていて、これは ATM の 等時性伝送
  (isochronous transmission)(マルチメディア用メッセージの同期をとるため
  に使用)と同じようなものです。Adaptec は、FireWire 製品を扱っていて、
  63 個のデバイスを 1 枚の PCI インタフェースカードに接続できます。また
  FireWire 全般の情報も
  <http://www.adaptec.com/worldwide/product/prodtechindex.html?cat=%2fTechnology%2>
  で公開しています。

  FireWire では最高の帯域幅を持つネットワークを利用できるようにはならな
  いと思います。しかし一般消費者相手の市場(低価格路線をとる必要がある)を
  狙っていること、遅延が少ないことから、ここ数年内に Linux PC でのクラス
  タのメッセージ通信用ネットワーク技術の最適解の 1 つとなると思われま
  す。

  3.2.9.  HiPPI と Serial HiPPI

  o  Linux のサポート: なし

  o  最大帯域幅: 1,600 M ビット/秒 (serial は 1,200 M ビット/ 秒)

  o  最低遅延: ?

  o  利用できるものは: 複数ベンダーのハードウェア

  o  ポートもしくはバスのインタフェース: EISA、PCI

  o  ネットワークの構成: スイッチング・ハブ

  o  接続マシン当たりのコスト: 3,500 ドル (serial is 4,500 ドル)

  HiPPI(High Performance Parallel Interface)はもともと非常に大きなデータ
  の伝送をスーパーコンピュータやその他のマシン(スーパーコンピュータやフ
  レームバッファ、ディスクアレイ等)間で帯域幅を大きくとって行うことを目
  的として作られました。そしてスーパーコンピュータの分野では、事実上の標
  準となっています。Serial HiPPI も一般的になってきており、 32 ビット幅
  の標準(パラレル)の HiPPI ケーブルを使わずに、光ファイバのケーブルを普
  通は利用します。serial と言いながら光ケーブルを使うのはへんですが。数
  年前から HiPPI を使ったクロスバースイッチが広がっており、価格が急激に
  下がっています。PCI インタフェースカードをサポートしている serial
  HiPPI は依然として割高なのが残念です。さらに残念なことに、Linux は
  HiPPI をまだサポートしていません。CERN が作っている HiPPI の優れた概略
  は  <http://www.cern.ch/HSI/hippi/> にあります。CERN はこの他にも
  HiPPI を扱っている数多くのベンダー一覧を
  <http://www.cern.ch/HSI/hippi/procintf/manufact.htm> で公開していま
  す。

  【訳註：HiPPI は EXPERIMENTAL でサポートされています】

  3.2.10.  IrDA (Infrared Data Association)

  o  Linux のサポート: なし ?

  o  最大帯域幅: 1.15 M ビット/秒 と 4 M ビット/秒

  o  最低遅延: ?

  o  利用できるものは: 複数ベンダーのハードウェア

  o  ポートもしくはバスのインタフェース: IrDA

  o  ネットワークの構成: 見えません ;-)

  o  接続マシン当りのコスト: 0 ドル

  IrDA(Infrared Data Association  <http://www.irda.org/>)は小さな赤外線
  を使ったデバイスで、多くのラップトップタイプの PC の側面についていま
  す。このインタフェースを使って、2 台以上のマシンを接続するのはそもそも
  困難なので、クラスタで利用するのは適切ではありません。 Don Becker 氏が
  何とか IrDA を使えるようにしました。

  【訳註：正式にサポートしています】

  3.2.11.  Myrinet

  o  Linux のサポート: ライブラリ

  o  最大帯域幅: 1,280 M ビット/秒

  o  最小遅延: 9 マイクロ秒

  o  利用できるものは: 単独ベンダーのハードウェア

  o  ポートもしくはバスのインタフェース: PCI

  o  ネットワークの構成: スイッチング・ハブ

  o  接続マシン当りのコスト: 1,800 ドル

  Myrinet( <http://www.myri.com/>)はローカル・エリア・ネットワーク(LAN)
  の 1 つで、「システム・エリア・ネットワーク」(SAN)も実現するように設計
  してあります。つまり、そのネットワークは 1 つのキャビネット内にマシン
  を収納し接続することで、並列システムを構成できます。LAN と SAN では物
  理的な媒体が異なっていて、その特性も若干違います。通常 SAN では、1 つ
  のクラスタ内で使用するのが一般的です。

  Myrinet は構成上新しいところはほとんどありませんが、パフォーマンスがと
  ても良いと評判です。Linux 用のドライバは性能がとても良いと言われていま
  すが、ホストコンピュータに付ける PCI バスの実装毎にパフォーマンスの差
  がひどく大きいとも言われています。

  現状では、Myrinet はクラスタを構築する際に「予算獲得」にそれほどシビア
  になる必要がないグループにとっては、とても魅力的なネットワークです。
  Linux PC にハイエンドな Pentium Pro や Pentium II、少なくとも 256 MB
  の RAM を搭載して、SCSI RAID も入れるぐらいなら、Myrinet は手頃といっ
  てもいいでしょう。しかしもっと普通の PC で構成するなら、選択肢として N
  台のマシンを Myrinet で接続するか、2N 台を複数のファースト・イーサネッ
  トで TTL_PAPERS を使って接続するかのどちらかにするのではないでしょう
  か。これは予算がいくらで、計算する事に対して何に一番注意を向けるかによ
  るところが大きいです。

  3.2.12.  Parastation

  o  Linux のサポート: HAL もしくはソケット・ライブラリ

  o  最大帯域幅: 125 M ビット/秒

  o  最低遅延: 2 マイクロ秒

  o  利用できるものは: 単独ベンダーのハードウェア

  o  ポートもしくはバスのインタフェース: PCI

  o  ネットワークの構成: メッシュ状でハブを置かない

  o  接続マシン当りのコスト: 1,000 ドル以上

  ParaStation プロジェクト( <http://wwwipd.ira.uka.de/parastation>)
  は、University of Karlsruhe Department of Informatics で行われていて、
  PVM 互換の遅延が少ない特注のネットワークです。まずこのプロジェクトでは
  2 個のプロセッサを搭載した ParaPC のプロトタイプを作り上げ、それに独自
  の EISA カードのインタフェースを搭載し、BSD系の UNIX を走らせました。
  それから DEC の Alpha マシンを使って大規模なクラスタを構築しまし
  た。1997 年 1 月から、Linux でも ParaStation が利用できるようになりま
  した。PCI カードは Hitex( <http://www.hitex.com:80/parastation/> を参
  照してください)という企業と共同で開発しました。ParaStation のハード
  ウェアには、高速で、信頼性があり、メッセージ通信や簡潔なバリア同期も備
  わっています。

  【訳註：Hitex のハードウェアは現在使用していません。Myrinet
  (http://www.myri.com/)を採用しています】

  3.2.13.  PLIP

  o  Linux のサポート: カーネル組込みのドライバ

  o  最大帯域幅: 1.2 M ビット/秒

  o  最低遅延: 1,000 マイクロ秒 ?

  o  利用できるものは: 市販のハードウェア

  o  ポートもしくはバスのインタフェース: SPP

  o  ネットワークの構成: 2 台のマシン間をケーブルで結ぶ

  o  接続マシン当りのコスト: 2 ドル

  「ラップリンク」ケーブルのコストだけがかかります。PLIP(Parallel Line
  Interface Protocol)は、2 台の Linux マシンを標準パラレルポートで結ん
  で、普通のソケットベースのソフトウェアで通信を行います。帯域幅や遅延、
  拡張性の面からすると本格的なネットワーク技術とはいえません。しかし、コ
  ストがほとんどただ同然なこと、ソフトウェアの互換性がある点では便利で
  す。ドライバは普通の Linux カーネルのディストリビューションに含まれて
  います。

  3.2.14.  SCI

  o  Linux のサポート: no

  o  最大帯域幅: 4,000 M ビット/秒

  o  最小遅延: 2.7 マイクロ秒

  o  利用できるものは: 複数ベンダーのハードウェア

  o  ポートもしくはバスのインタフェース: PCI と ベンダー独自のもの

  o  ネットワークの構成: ?

  o  接続マシン当りのコスト: 1,000 ドル

  SCI(Scalable Coherent Interconnect ANSI/IEEE 1596-1992)は、大規模なマ
  シン群で同期をとりながら共有メモリにアクセスする機能(様々なタイプの
  メッセージのブロック転送など)を高性能に提供するしくみを目指していま
  す。SCI の設計は、帯域幅と遅延において他のほとんどのネットワーク技術と
  比較して「素晴らしい」と言って過言ではないでしょう。問題点は、SCI が安
  価な製品として広く利用できないことと Linux をサポートしていないことで
  す。

  主に SCI は、論理的には共有しているが物理的には分散しているメモリを搭
  載しているメーカー独自のマシンに採用されています。例えば、HP/Convex の
  Exemplar SPP や Sequent の NUMA-Q 2000 ( <http://www.sequent.com/>)な
  どです。しかし SCI は PCI インタフェース・カードや 4 ポートのスイッ
  チ(4 ポートのスイッチ 4 台をカスケードして、16 台までのマシンを接続可
  能)が Dolphin( <http://www.dolphinics.com/> からクラスタ製品のライン
  ナップとして販売されています。CERN(
  <http://www.cern.ch/HSI/sci/sci.html> には優れた SCI の概説のリンク集
  があります。

  【訳註：Sequent は 1998 年に IBM に吸収されました。NUMA シリーズは IBM
  @server x シリーズ及び IBM NUMA-Q として発売されています】

  3.2.15.  SCSI

  o  Linux のサポート: カーネル組込みのドライバ

  o  最大帯域幅: 5 M ビット/秒 から 20 M ビット/秒以上

  o  最小遅延: ?

  o  利用できるものは: 複数ベンダーのハードウェア

  o  ポートもしくはバスのインタフェース: PCI、EISA、ISA カード

  o  ネットワークの構成: マシン間でバスを使って SCSI デバイスを共有

  o  接続マシン当りのコスト: ?

  SCSI(Small Computer Systems Interconnect)は、元来ディスク・ドライブや
  CD-ROM、画像スキャナー等の入出力のためのバスです。SCSI-1 や SCSI-2、
  SCSI-3 という独立した規格があります。また速度の面で Fast や Ultra、
  データの転送幅として 8、16、32 ビット(FireWire との互換性は SCSI-3 に
  定義してあります)があります。かなり複雑なのですが、性能の良い SCSI は
  EIDE より若干速く、より多くのデバイスをさらに効率的に扱えるのは周知の
  事実です。

  2 台のコンピュータで 1 本の SCSI バスを共有できることに気づいていない
  方が予想外にたくさんいます。このタイプの構成は、ディスク・ドライブをマ
  シン間で共有するのにとても便利で、フェイル・オーバーを実現します。つま
  り、一方のマシンがおかしくなった時にそのマシンに対するデータベースへの
  要求を肩代わりするというように。現在のところ、Microsoft の PC クラスタ
  製品である WolfPack は、このしくみしか提供していません。しかし SCSI を
  共有するこのしくみが、より大規模なシステムへと拡張できないことで、並列
  処理一般にとっては色あせたしくみになってしまっています。

  3.2.16.  ServerNet

  o  Linux のサポート: no

  o  最大帯域幅: 400 M ビット/秒

  o  最低遅延: 3 マイクロ秒

  o  利用できるものは: 単独ベンダーのハードウェア

  o  ポートもしくはバスのインタフェース: PCI

  o  ネットワークの構成: 六面＋ツリー構造と四面＋格子構造を持つハブ

  o  接続マシン当たりのコスト: ?

  ServerNet は Tandem( <http://www.tandem.com>)から提供されている高性能
  なネットワーク機器です。特にオンライン・トランザクション処理 (OLTP)の
  分野では、Tandem は高い信頼性も持ったシステムを作ることではトップクラ
  スにあるのは有名です。ですから Tandem のネットワークがただパフォーマン
  スだけではなく、データの保全性と信頼性をも追求しているのは当然のことで
  す。もう 1 つ ServerNet が興味深い点は、あらゆるデバイス同士が直接デー
  タをやりとりできる点にあります。プロセッサ間だけではなく、ディスク・ド
  ライブ間等でも可能です。セクション 3.5 で説明する MPI が推奨している
  ローカルでないメモリに対しての片方向へのアクセスのしかたと似ています。
  最後に 1 つだけ ServerNet についてコメントすることがあります。単独ベン
  ダーしかサポートしていませんが、そのベンダーは ServerNet を事実上の標
  準にするだけの力があります… Tandem の親会社は Compaq ですから。

  【訳註：「六面＋ツリー構造と四面＋格子構造を持つハブ」の具体的な図は、
  ServerNet：大容量データの高速移動を可能にする新サーバー・ アーキテク
  チャー 超並列 I/O へのアプローチ
  <http://www.compaq.co.jp/tandem/technology/servernet/whitepaper/BRKTECWP.html>
  にある図(図 7 と 8)を参照してください。ここにはより詳細な ServerNet の
  解説もあります】

  3.2.17.  SHRIMP

  o  Linux のサポート: ユーザレベルのメモリマッピングインタフェース

  o  最大帯域幅: 180 M ビット/秒

  o  最少遅延: 5 マイクロ秒

  o  利用できるものは: 研究段階の試作品

  o  ポートもしくはバスのインタフェース: EISA

  o  ネットワークの構成: メッシュ状のバックプレイン(Intel の Paragon の
     ように)

  o  接続マシン当たりのコスト: ?

  SHRIMP プロジェクト( <http://www.CS.Princeton.EDU/shrimp/>) は、プリン
  ストン大学の Computer Science Department で行われています。 Linux が動
  いている PC を演算ノードと見なし、1 台の並列コンピュータとして構築して
  います。最初の  SHRIMP(Scalable, High-Performance, Really Inexpensive
  Multi-Processor)は、独自の EISA カードのインタフェース上にデュアルポー
  トの RAM を積んで、プロセッサを 2 つ搭載した試作機でした。現状の試作機
  は、より構成の規模が大きくなり、独自のインタフェース・カードを Intel
  の Paragon( <http://www.ssd.intel.com/paragon.html> を参照) 【訳註：リ
  ンク切れ】と同様なメッシュ状の経路を持つ「ハブ」に接続しています。オー
  バーヘッドを押さえた「仮想メモリにマップした通信」機器とそれをサポート
  するソフトウェアの開発に精力を注ぎ込んでいます。

  【訳註：Intel はスーパコンピュータの製造を中止しています。Paragon の姿
  は、 <http://www.cs.sandia.gov/HPCCIT/gif/paragon.gif> で見ることがで
  きます】

  3.2.18.  SLIP

  o  Linux のサポート: カーネル組み込みのドライバ

  o  最大帯域幅: 0.1 M ビット/秒

  o  最低遅延: 1,000 マイクロ秒 ?

  o  利用できるものは: 市販のハードウェア

  o  ポートもしくはバスのインタフェース: RS232C

  o  ネットワークの構成: 2 台のマシン間のケーブル

  o  接続マシン当たりのコスト: 2 ドル

  SLIP(Serial Line Interface Protocol)は、パフォーマンスの点ではローエン
  ドに位置づけられます。しかし SLIP(もしくは CSLIP や PPP)は 2 台のマシ
  ンを普通の RS232 シリアルポート経由でソケットベースの通信が可能で
  す。RS232 ポートはヌル・モデム RS232 シリアルケーブルを使って接続で
  き、またモデム経由でダイヤルアップすることで接続することもできます。ど
  の場合でも遅延が多く、帯域幅は狭いため、SLIP は他に選択肢がない場合に
  だけ使うべきです。どこにもいいところがありませんが、PC の大部分は
  RS232 を 2 ポート持っているので、マシン間を直列もしくはリング状に接続
  することでネットワークが組めます。負荷分散のために EQL というソフト
  ウェアさえ用意されています。

  3.2.19.  TTL_PAPERS

  o  Linux のサポート: AFAPI ライブラリ

  o  最大帯域幅: 1.6 M ビット/秒

  o  最低遅延: 3 マイクロ秒

  o  利用できるものは: パブリック・ドメインで設計した単独ベンダーのハー
     ドウェア
  o  ポートもしくはバスのインタフェース: SPP

  o  ネットワークの構成: ツリー状のハブ

  o  接続マシン当たりのコスト: 100 ドル

  PAPERS(Purdue's Adapter for Parallel Execution and Rapid
  Synchronization) プロジェクト( <http://aggregate.org/AFAPI/>)は、パー
  デュ大学の School of Electrical and Computer Engineering で行われてい
  ます。拡張性があり、遅延の少ない集合演算を実現する通信ハードウェアと普
  通の PC やワークステーションを並列スーパーコンピュータのノードとして構
  成できるソフトウェアを構築しています。

  数 10 タイプもの PAPERS のハードウェアがあり、SPP(Standard Parallel
  Port) 経由で PC やワークステーションに接続しています。おおまかに言って
  2 つの開発計画が進められています。「PAPERS」と言われるものは、パフォー
  マンスの向上を目的として、ふさわしいと思われるあらゆる技術を駆使してい
  ます。現状は FPGA(Field Programmable Gate Array)を使い、広い帯域幅を持
  つ PCI バスインタフェースの設計を行っていて、両者とも開発中です。
  「TTL_PAPERS」はこれとは対照的で、パーデュ大学以外でも簡単に構築できる
  ように設計してあります。とてもシンプルかつパブリック・ドメインな設計
  で、一般的な TTL のロジックを使って構築しています。この設計は商用でも
  利用されています( <http://chelsea.ios.com:80/~hgdietz/sbm4.html>) 【訳
  註：リンク切れ】

  他の大学が独自のハードウェアを設計しているのに対して、TTL_PAPERS クラ
  スタは米国から韓国に至るまで、幅広い大学で構築されています。帯域幅は
  SPP による接続なので制限がいくつかありますが、PAPERS はほんのわずかの
  遅延で集合演算通信を実現しています。メッセージ指向タイプのシステムの中
  で最速のものでも、集合演算のパフォーマンスでは PAPAERS にはおよびませ
  ん。つまり PAPERS は、ビデオ・ウォールのモニター間の同期(近々に出る
  Video Wall HOWTO でさらに論じます)や広い帯域幅を持つネットワークへのア
  クセス管理、遺伝研究における全体適応性の評価等に特に優れていま
  す。PAPERS クラスタは IBM の PowerPC AIX や旧 DEC(現 Compaq)の Alpha
  OSF/1、HP の PA-RISC HP-UX のマシンでも構築されていますが、Linux ベー
  スの PC のサポートが一番です。

  ユーザレベルのプログラム で TTL_PAPERS AFAPI(Aggregate Function API)を
  使うと、Linux 配下で SPP に接続しているハードウェアポートのレジスタに
  直接アクセスしますので、アクセス毎にシステムコールは発生しません。こう
  するには、まず AFAPI が iopl() や ioperm() を使ってポートにアクセスで
  きるようにします。ただこれらのシステムコール双方とも、ユーザのプログラ
  ムに特権を持たせる必要があり、これが潜在的にセキュリティ・ホールになる
  恐れがある点が問題となります。この問題を解決するには、カーネルにパッチ
  <http://aggregate.org/AFAPI/>)を追加して当てて、特権を持つプロセスがあ
  らゆるプロセスのポートに対するアクセス権を制御できるようにします。

  3.2.20.  USB (Universal Serial Bus)

  o  Linux のサポート: カーネル組み込みのドライバ

  o  最大帯域幅: 12 M ビット/秒

  o  最低遅延: ?

  o  利用できるものは: 市販のハードウェア

  o  ポートもしくはバスのインタフェース: USB

  o  Network の構成: バス

  o  接続マシン当たりのコスト: 5 ドル ?

  USB(Universal Serial Bus  <http://www.usb.org/>)は、電源を入れたままで
  抜き差しでき、従来のイーサネット並みの速度が出すことができます。キー
  ボードからビデオ会議用のカメラまで、周辺機器類を 127 個までバスに接続
  できます。複数のコンピュータそれぞれを USB を使って接続する方法はよく
  わかりません。いずれにしても USB ポートは RS232 や SPP と同様に PC の
  マザーボードにまもなく標準で付くことになりますので、次に PC を購入する
  時に USB ポートが 1、2 つ付いていても不思議ではありません。Linux のド
  ライバの開発については、 <http://www.linux-usb.org/> で議論していま
  す。

  ある意味で USB は、パフォーマンスが低く、コストがかからない、現在購入
  可能な FireWire の一種と言えないこともありません。

  3.2.21.  WAPERS

  o  Linux のサポート: AFAPI ライブラリ

  o  最大帯域幅: 0.4 M ビット/秒

  o  最低遅延: 3 マイクロ秒

  o  利用できるものは: パブリック・ドメインな設計

  o  ポートもしくはバスのインタフェース: SPP

  o  Network の構成: 2 から 64 台のマシン間の回路化

  o  接続マシン当たりのコスト: 5 ドル

  WAPERS(Wired-AND Adapter for Parallel Execution and Rapid
  Synchronization)は、パーデュ大学の School of Electrical and Computer
  Engineering で行われている PAPERS プロジェクト
  <http://garage.ecn.purdue.edu/~papers/>【訳註：リンク切れ】から派生し
  ました。きちんと実装してあれば、SPP は 4 ビットのオープン・コレクタ出
  力ができ、マシン間をすべて回路化して、4 ビット幅の論理回路を実現しま
  す。この論理回路は電気的に扱いにくく、この方法で接続できるマシンの最大
  数もポートのアナログ特性に極めて依存しています(最大のシンク電流やプル
  アップレジスタ値。通常 WAPERS では、ネットワークには 7、8 台のマシンが
  接続できます。コストや遅延は低いものの、帯域幅も狭くなります。 WAPERS
  はクラスタ構成の中で唯一のネットワークとして使うというよりも、集合演算
  の操作のための二次的なネットワークに適しています。 TTL_PAPERS と同様
  に、システムのセキュリティを上げるのにはカーネルへの小さなパッチを当て
  ることが推奨されていますが、必ずしも必要ではありません(
  <http://garage.ecn.purdue.edu/~papers/giveioperm.html>)。

  3.3.  ネットワークに対するソフトウェアのインタフェース

  並列アプリケーションのソフトウェアのサポートを論じる前に、まずネット
  ワーク機器に対する低レベルなソフトウェアのインタフェースの基礎につい
  て、その概略を見ていくことにしましょう。基本的に選択肢はたった 3 つし
  かありません。それはソケットとデバイス・ドライバとユーザレベルのライブ
  ラリです。

  3.3.1.  ソケット

  低レベルのネットワークに対するインタフェースで圧倒的に広く使われるは、
  ソケット・インタフェースです。ソケットはずっと以前から UNIX の一機能で
  あり、標準的なネットワーク機器の大部分は少なくとも ソケットの 2 つのタ
  イプのプロトコルをサポートするように設計されています。それは UDP と
  TCP です。両タイプのソケットとも、あるマシンから他のマシンへ任意の大き
  さのデータブロックを送ることが可能です。しかし、根本的に異なる点もいく
  つかあります。両者とも最低遅延はおおよそ 1,000 マイクロ秒程度ですが、
  その性能はネットワーク上の流量によってはさらに低下します。

  これらのタイプのソケットは、移植性やより高度なレベルを求める並列処理ソ
  フトとっては基本となるネットワークインタフェースです。例えば PVM は
  UDP と TCP を組み合わせて使用していますので、両者の相違を知ることでパ
  フォーマンスを向上できるようになるでしょう。さらにパフォーマンスを上げ
  るのに、プログラムの中から直接これらの機能を利用することもできます。下
  記に UDP と TCP についての簡単な概略を述べます。詳しいことは man や
  ネットワーク・プログラミングについての良書を読んでください。

  3.3.1.1.  UDP プロトコル(SOCK_DGRAM)

  UDP は User Datagram Protocol の略称ですが、その特性をもっと覚えやすく
  するために、Unreliable Datagram Processing(信頼性のないデータ処理)とし
  てもいいのではないでしょうか。つまり UDP は送信するブロックを独立した
  メッセージとしていて、そのメッセージは伝送中に無くなるかもしれないので
  す。事実、ネットワークの流量しだいでは UDP メッセージが無くなったり、
  何回もやってきたり、送った順番とは違う順番できたりする可能性がありま
  す。UDP メッセージの送り手は自動的にこの状況を知るすべはなく、ユーザ自
  身が書いたコードで検知して、その問題に対処する必要があります。幸いなこ
  とに、UDP はメッセージが到着していれば、その中身は壊れていないことを保
  証しています(言い替えれば、1 つの UDP メッセージの断片だけを受け取ると
  いうことはあり得ません)。

  UDP の良いところは、ソケットプロトコルの中では最速である場合が多いこと
  です。その上 UDP は「コネクションレス」で、これは個々のメッセージが他
  すべてのメッセージと独立していることを意味しています。例えると、個々の
  メッセージは郵便に出した手紙と同じです。同じ住所にいくつも手紙を出せま
  すが、個々の手紙は他のものとは何の関係もありませんし、どのくらいの人に
  出そうとも制限というものはありません。

  3.3.1.2.  TCP プロトコル(SOCK_STREAM)

  UDP とは違い、TCPは信頼性があるコネクションベースのプロトコルです。個
  々のブロック送信は、1 つのメッセージとは見なされず、送受信間のコネク
  ションを通して伝送される、一連のバイトストリームの中のデータブロックと
  して扱われます。これが UDP メッセージと大きく違う点で、個々のブロック
  は単にバイトストリームの一部に過ぎず、ユーザ自身が書いたコードによって
  そのバイトストリームからブロックを取り出す必要があります。しかし、メッ
  セージを分解するための印がない上に、コネクションはネットワークの障害に
  弱く、それぞれのプロセス当たりで同時にコネクションを張る数にも制限があ
  ります。信頼性があるゆえに、TCP は UDP と比べてかなりオーバーヘッドが
  大きくなる傾向があります。

  しかし、TCP には思わずうれしくなる機能がいくつかあります。1 つは、複数
  のメッセージを 1 つのコネクションで送ったとすると、TCP はそれらをひと
  まとめにしてバッファにおいた上で、ネットワーク機器のパケットの大きさに
  調整できることです。メッセージが小さかったり、中途半端な大きさの場合
  は、UDP よりもパフォーマンスが出るかもしれません。いいところは他にもあ
  り、マシン間を物理的に直接接続することで信頼性のあるネットワークを構築
  して、TCP コネクションをシミュレートしてしまう方法です。例え
  ば、ParaStation の「ソケット・ライブラリ」というインタフェースのソフト
  ウェアを使えば、実現できてしまいます。このソフトウェアは、ユーザレベル
  で呼び出すことで TCP を使いこなせます。OS 標準の呼び出しと違っている点
  は、PSS を呼び出すシステムコールそれぞれの前につける点だけです。

  3.3.2.  デバイス・ドライバ

  実際にデータをネットワークとやりとりする時に、標準的な UNIX のソフト
  ウェアのインタフェースとなるのは、UNIX のカーネルの一部であるデバイ
  ス・ドライバです。UDP や TCP はデータをただ伝送するのではなく、かなり
  のオーバーヘッドをともなうソケットの制御もしています。例えば、複数の
  TCP コネクションは物理的に 1 つのネットワーク・インタフェースを共有で
  きるということは、何かがそれを処理しているからできるのです。それにひき
  かえ、あるネットワーク・インタフェース専用のデバイス・ドライバは、いく
  つかの単純なデータ伝送機能だけを必要とします。これらのデバイス・ドライ
  バの機能は、ユーザのプログラムから実行することが可能で、open() を使っ
  て固有のデバイスを特定してから、オープンした「ファイル」に対して
  read() や write() のようなシステムコールを使います。つまりそのような操
  作では、データブロックを伝送するのにほとんどシステムコールによるオー
  バーヘッドが生じません。恐らく数十マイクロ秒程度で済みます。

  Linux で使えるデバイス・ドライバを書くのは大変なことではありません…。
  そのデバイスのハードウェアがどのように動作するかを正確に知っているなら
  ですが…。どのように動作するのか確かでないなら、推測するのは止めた方が
  いいでしょう。デバイス・ドライバのデバッグはつまらないだけでなく、失敗
  するとハードウェアを壊してしまいます。しかしそれも気にならないなら、デ
  バイス・ドライバを書くことは可能です。例えば、機能は劣っているものの通
  常のイーサネット・プロトコルのオーバヘッド無しにマシン間に直接コネク
  ションを張って高速に通信する専用のイーサネットカードを使うために。実
  際、初期の Intel の スーパーコンピュータのいくつかがそうでした…。詳し
  いことは、 Device Driver HOWTO を見てください。

  3.3.3.  ユーザレベルのライブラリ

  OS についての教育課程を受けたことがあるなら、ユーザレベルでハードウェ
  ア・デバイスのレジスタに間違ってもアクセスしてはいけない、と教わったと
  思います。デバイスへのアクセスを制御するのが OS の重要な役目の 1 つだ
  からです。しかし OS のシステムコールは少なくとも数 10 マイクロ秒のオー
  バーヘッドが生じます。TTL_PAPERS のような特殊なネットワーク機器のよう
  に、基本的なネットワーク処理がわずか 3 マイクロ秒しかかからない場合に
  は、そのような OS のシステムコールのオーバーヘッドは許容の範囲を越えて
  います。オーバーヘッドを避ける唯一の手段は、ユーザレベルのコードとライ
  ブラリを使って、ハードウェア・デバイスのレジスタに直接アクセスすること
  です。つまり課題となるのは、どうしたらユーザレベルのライブラリがハード
  ウェアに直接アクセスできるかということであって、OS がデバイスのアクセ
  ス権を制御することとどう調整しあうか、ということではありません。

  一般的なシステムで、ユーザレベルのライブラリで直接ハードウェアのレジス
  タをアクセスする唯一の方法は、

  1. ユーザ・プログラムを動かす時に、OS のシステムコールを使ってメモリの
     アドレス空間のページに存在しているデバイスのレジスタをユーザプロセ
     スの仮想メモリにマップすること。システムの中には、 mmap() システム
     コール(セクション 2.6 で説明済み)が、入出力デバイスの物理メモリ上の
     ページアドレスに相当するスペシャル・ファイルにマップする手段として
     良く利用されている。別の手段として、デバイス・ドライバを書いて、こ
     の機能を実現するのはそれ程難しくはない。さらにこのデバイス・ドライ
     バは必要としている特定のデバイスのレジスタのページだけをマップして
     アクセスを制御できるので、OS のアクセス制御はそのままにしておける

  2. マップしたアドレスに対して単純に読み書きすることで、OS のシステム
     コールを使わずにデバイスのレジスタにアクセスすること。例えば、
     *((char *) 0x1234) = 5; は 5 という値をメモリの 1234(16 進数) に書
     き込むことになる

  幸いなことに、Intel の 386(とその互換プロセッサ)では、さらにうまい解決
  方法があります。
  1. ioperm() という OS の システムコールを特権を持つプロセスから呼び出
     すことで、デバイスのレジスタに対応している入出力アドレスへのアクセ
     ス権を得られる。もう 1 つの方法として、独自の特権を持つユーザプロセ
     ス(すなわち「メタ OS」)で権限を管理する方法がある。Linux に
     giveioperm() システムコール
     <http://garage.ecn.purdue.edu/~papers/giveioperm.html> パッチを当て
     て実現できる

  2. OS のシステムコールを使わずに、386 の入出力ポート命令を使ってデバイ
     スのレジスタにアクセスすること

  2 番目が優れた解決方法です。複数の入出力デバイスは複数のレジスタを 1
  つのページ内に置いているのが普通で、そうなると最初のテクニックでは、他
  のデバイスのレジスタがたまたま目的のレジスタの同じページ内にあった時
  に、そのアクセスから目的のレジスタを保護する手段を提供できないためで
  す。もちろん 386 のポート入出力命令にもマイナス面があります。それは C
  でコーディングできないために、かわりにわずかですがアセンブラを使う必要
  があるかもしれないことです。下記に、あるポートに 1 バイトの値を入力す
  るために、GCC でラッパした(C プログラム内で利用可能)インラインのアセン
  ブラコードの関数をあげます。

  ______________________________________________________________________
  extern inline unsigned char
  inb(unsigned short port)
  {
      unsigned char _v;
  __asm__ __volatile__ ("inb %w1,%b0"
                        :"=a" (_v)
                        :"d" (port), "0" (0));
      return _v;
  }
  ______________________________________________________________________

  同様に、あるポートに 1 バイトの値を出力する GCC にくるんだコードを下記
  にあげます。

  ______________________________________________________________________
  extern inline void
  outb(unsigned char value,
  unsigned short port)
  {
  __asm__ __volatile__ ("outb %b0,%w1"
                        :/* no outputs */
                        :"a" (value), "d" (port));
  }
  ______________________________________________________________________

  3.4.  PVM (Parallel Virtual Machine)

  PVM(Parallel Virtual Machine)はフリーで利用ができ、移植性があるメッ
  セージ通信ライブラリで、大部分はソケットベースで実装してあります。メッ
  セージ通信タイプのクラスタ並列計算では、事実上の標準と言っても過言では
  ありません。

  PVM がサポートしているのは、プロセッサ が 1 つのマシンや SMP Linux マ
  シン、ソケットが利用可能なネットワーク(例えば SLIP、PLIP、イーサネッ
  ト、ATM)に接続している Linux マシンによるクラスタです。PVM は、プロ
  セッサやシステム構成、使用している物理的なネットワークが異なっている様
  々なマシン構成 (異機種クラスタと言います)であっても実際に動作すると思
  われます。またインターネット経由で接続し合ったマシンを 1 つのクラスタ
  として扱うほどの規模でも動作すると思われます。また、PVM はクラスタ全体
  に渡って並列に実行しているジョブを制御する機能も持っています。そして何
  よりも PVM は長い年月に渡り何の制限も受けずに利用されており(現状は
  <http://www.epm.ornl.gov/pvm/pvm_home.html> から)、結果として数多くの
  プログラミング言語やコンパイラ、アプリケーション・ライブラリ、そしてプ
  ログラミングやデバックのためのツール等ができました。そしてこれらの成果
  物を「移植性のあるメッセージ通信を開発するためのライブラリ」として使用
  しています。また news グループ(comp.parallel.pvm)も存在しています。

  しかし注意が必要なのは、PVM のメッセージ通信を呼び出すと標準的なソケッ
  ト処理の遅延の大きさに加えて、さらにかなりのオーバーヘッドが加わってし
  まうことです。その上、メッセージを扱う呼び出しそのものがとりわけ「親し
  みやすい」プログラミング・モデルではないことにも注意が必要です。

  最初にセクション 1.3 で登場した円周率の計算を例にして、PVM ライブラリ
  を呼び出す C を使ったバージョンは下記のようになります。

  ______________________________________________________________________
  #include <stdlib.h>
  #include <stdio.h>
  #include <pvm3.h>

  #define NPROC   4

  main(int argc, char **argv)
  {
    register double lsum, width;
    double sum;
    register int intervals, i;
    int mytid, iproc, msgtag = 4;
    int tids[NPROC];  /* array of task ids */

    /* enroll in pvm */
    mytid = pvm_mytid();

    /* Join a group and, if I am the first instance,
       iproc=0, spawn more copies of myself
    */
    iproc = pvm_joingroup("pi");

    if (iproc == 0) {
      tids[0] = pvm_mytid();
      pvm_spawn("pvm_pi", &argv[1], 0, NULL, NPROC-1, &tids[1]);
    }
    /* make sure all processes are here */
    pvm_barrier("pi", NPROC);

    /* get the number of intervals */
    intervals = atoi(argv[1]);
    width = 1.0 / intervals;

    lsum = 0.0;
    for (i = iproc; i<intervals; i+=NPROC) {
      register double x = (i + 0.5) * width;
      lsum += 4.0 / (1.0 + x * x);
    }

    /* sum across the local results & scale by width */
    sum = lsum * width;
    pvm_reduce(PvmSum, &sum, 1, PVM_DOUBLE, msgtag, "pi", 0);

    /* have only the console PE print the result */
    if (iproc == 0) {
      printf("Estimation of pi is %f\n", sum);
    }

    /* Check program finished, leave group, exit pvm */
    pvm_barrier("pi", NPROC);
    pvm_lvgroup("pi");
    pvm_exit();
    return(0);
  }
  ______________________________________________________________________

  3.5.  MPI (Message Passing Interface)

  PVM はメッセージ通信ライブラリの事実上の標準となっていますが、MPI
  (Message Passing Interface)は PVM と比べると新しい公式の規格です。 MPI
  規格のホーム・ページは  <http://www.mcs.anl.gov:80/mpi/> で、news グ
  ループは comp.parallel.mpi です。

  ここで MPI について議論する前に、過去数年に渡って続いた PVM 対 MPI の
  宗教戦争について触れざるを得ないと思います。私はどちらにも組みしていま
  せん。ここではその相違点を偏見にとらわれることなくまとめてみます。

     プロセス実行を制御する環境。
        一言でいうと PVM は持っているが、MPI は実装とそのやり方をきちん
        と仕様化していない。つまり、PVM のプログラムはどこでも同じ様に実
        行できるが、MPI は実装によってその動作が変わってしまう

     異機種クラスタのサポート。
        PVM はワークステーションが空いている時間を集めて処理するという観
        点で進化してきたので、いろいろな種類のマシンやオペレーティング・
        システムが混じった環境をじかに管理できる。これとは対照的に、MPI
        は主としてターゲットを MPP(Massively Parallel Processor) もしく
        は、クラスタ専用に使われる同じ種類のワークステーションに当ててい
        る

     何でもかんでも症候群。
        PVM は目的に対して一貫している。これが MPI 2.0 には欠けてい
        る。MPI 2.0 の新しい規格は、多くの機能を盛り込んでおり、基本的な
        メッセージ通信モデルをはるかに越えてしまっている。例えば
        RMA(Remote Memory Access)や並列ファイル入出力などがそれに当た
        る。本当に役に立つのだろうか？ もちろんそれらは…しかし MPI 2.0
        は、新しいプログラミング言語を完璧に習得するぐらい学習が必要にな
        る

     ユーザ・インタフェースの設計。
        MPI の設計は PVM より新しく、 PVM の設計を確かに参考にしてい
        る。MPI はシンプルかつ効率的なバッファ機能があり、その高度の抽象
        化によってユーザが定義したデータ構造をメッセージに入れて伝送でき
        る

     おきての影響力。
        私が見るところでは、MPI と比べるとまだ PVM の方がいろいろと考え
        抜いている点が多い。しかし PVM から MPI に移植することが簡単であ
        ること、MPI が公式な標準として広く支持されていることから、公共機
        関にとっては MPI を使用すること自体がポリシそのものになっている

  結論は？ という問いに対しては、フリーで使える MPI が 3 つ独立して開発
  されていて、Linux のクラスタが動きます、と答えておきます。 (その中の 1
  つは私が作りました)

  o  LAM(Local Area Multicomputer)は MPI 1.1 規格に完全に準拠してい
     る。MPI プログラムが、スタンドアローンの Linux システム や UDP や
     TCP ベースのソケット通信を使った Linux システムで構築したクラスタ上
     で動作する。システムには様々なプログラム開発やデバックを支援する
     ツールとともに、簡単な実行制御機能がある。
     <http://www.mpi.nd.edu/lam/> からフリーで利用可能

  o  MPICH(MPI CHameleon)は MPI 1.1 規格に完全に準拠していて、移植性を考
     慮して設計してある。LAM と同様に、MPI プログラムがスタンドアローン
     の Linux システム や UDP や TCP ベースのソケット通信を使った Linux
     システムで構築したクラスタ上で動作する。しかし MPI が効率的かつ目的
     に対して柔軟に対応することに重点をおいているのは間違いない。この
     MPI の実装を移植するには、「チャネル・インタフェース」の 5 つの関数
     とパフォーマンス向上のために MPICH ADI(Abstract Device Interface)を
     実装することになる。 MPICH についてのさらに詳しい情報とその移植方法
     については、 <http://www.mcs.anl.gov/mpi/mpich/> で知ることができる
  o  AFMPI(Aggregate Function MPI)は MPI 2.0 規格のサブセットで、私が作
     成した。AFAPI をベースに構築してあり、遅延の少ない集合通信と RMA の
     見本となるように設計した。したがって MPI のデータタイプや通信手段等
     は最低限のものしか提供していない。スタンドアロンの Linux システムと
     AFPI が利用できるネットワーク機器で接続しているクラスタにおい
     て、MPI を使った C プログラムが動作する。
     <http://garage.ecn.purdue.edu/~papers/> からフリーで利用可能

  これら MPI の実装(その他も)のどれを利用しても、通常使うタイプの通信は
  かなり簡単に実行できます。

  しかし MPI 2.0 には通信パラダイムが複数あり、それが根本的に違ったもの
  になっているため、プログラマーがあるパラダイムを使用しても MPI の他の
  コーディングでは利用でいない場合があります。したがって 1 つのプログラ
  ム例をあげるよりも、MPI がサポートしている根本的に異なっている通信パラ
  ダイムそれぞれについて例をあげる方が役立つでしょう。3 つのプログラムす
  べては、この HOWTO を通じて使用してきた円周率の計算をする基本的なアル
  ゴリズム (セクション 1.3 より)で実装してあります。

  最初の MPI のプログラムは、基本的な MPI のメッセージ通信呼び出しを使用
  して、それぞれのプロセッサが小計を 0 番のプロセッサに送って、そのプロ
  セッサが合計して結果を出力します。

  ______________________________________________________________________
  #include <stdlib.h>
  #include <stdio.h>
  #include <mpi.h>

  main(int argc, char **argv)
  {
    register double width;
    double sum, lsum;
    register int intervals, i;
    int nproc, iproc;
    MPI_Status status;

    if (MPI_Init(&argc, &argv) != MPI_SUCCESS) exit(1);
    MPI_Comm_size(MPI_COMM_WORLD, &nproc);
    MPI_Comm_rank(MPI_COMM_WORLD, &iproc);
    intervals = atoi(argv[1]);
    width = 1.0 / intervals;
    lsum = 0;
    for (i=iproc; i<intervals; i+=nproc) {
      register double x = (i + 0.5) * width;
      lsum += 4.0 / (1.0 + x * x);
    }
    lsum *= width;
    if (iproc != 0) {
      MPI_Send(&lbuf, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
    } else {
      sum = lsum;
      for (i=1; i<nproc; ++i) {
        MPI_Recv(&lbuf, 1, MPI_DOUBLE, MPI_ANY_SOURCE,
                 MPI_ANY_TAG, MPI_COMM_WORLD, &status);
        sum += lsum;
      }
      printf("Estimation of pi is %f\n", sum);
    }
    MPI_Finalize();
    return(0);
  }
  ______________________________________________________________________

  二番目の MPI のバージョンは、集合通信(この特定用途に対して最適解)を
  使っています。

  ______________________________________________________________________
  #include <stdlib.h>
  #include <stdio.h>
  #include <mpi.h>

  main(int argc, char **argv)
  {
    register double width;
    double sum, lsum;
    register int intervals, i;
    int nproc, iproc;

    if (MPI_Init(&argc, &argv) != MPI_SUCCESS) exit(1);
    MPI_Comm_size(MPI_COMM_WORLD, &nproc);
    MPI_Comm_rank(MPI_COMM_WORLD, &iproc);
    intervals = atoi(argv[1]);
    width = 1.0 / intervals;
    lsum = 0;
    for (i=iproc; i<intervals; i+=nproc) {
      register double x = (i + 0.5) * width;
      lsum += 4.0 / (1.0 + x * x);
    }
    lsum *= width;
    MPI_Reduce(&lsum, &sum, 1, MPI_DOUBLE,
               MPI_SUM, 0, MPI_COMM_WORLD);
    if (iproc == 0) {
      printf("Estimation of pi is %f\n", sum);
    }
    MPI_Finalize();
    return(0);
  }
  ______________________________________________________________________

  三番目の MPI のバージョンは、MPI 2.0 の RMA の機能を使って、それぞれの
  プロセッサがローカルの lsum を 0 番のプロセッサの sum に加算します。

  ______________________________________________________________________
  #include <stdlib.h>
  #include <stdio.h>
  #include <mpi.h>

  main(int argc, char **argv)
  {
    register double width;
    double sum = 0, lsum;
    register int intervals, i;
    int nproc, iproc;
    MPI_Win sum_win;

    if (MPI_Init(&argc, &argv) != MPI_SUCCESS) exit(1);
    MPI_Comm_size(MPI_COMM_WORLD, &nproc);
    MPI_Comm_rank(MPI_COMM_WORLD, &iproc);
    MPI_Win_create(&sum, sizeof(sum), sizeof(sum),
                   0, MPI_COMM_WORLD, &sum_win);
    MPI_Win_fence(0, sum_win);
    intervals = atoi(argv[1]);
    width = 1.0 / intervals;
    lsum = 0;
    for (i=iproc; i<intervals; i+=nproc) {
      register double x = (i + 0.5) * width;
      lsum += 4.0 / (1.0 + x * x);
    }
    lsum *= width;
    MPI_Accumulate(&lsum, 1, MPI_DOUBLE, 0, 0,
                   1, MPI_DOUBLE, MPI_SUM, sum_win);
    MPI_Win_fence(0, sum_win);
    if (iproc == 0) {
      printf("Estimation of pi is %f\n", sum);
    }
    MPI_Finalize();
    return(0);
  }
  ______________________________________________________________________

  MPI 2.0 に含まれる RMA のしくみが、様々なプロセッサ上の異なるメモリに
  存在する同期を必要とするデータ構造を扱う上で、問題となりそうなところを
  巧妙に屈伏している点は覚えておいても損はありません。これは、「window」
  を基準とすることで解決しています。window には、ベースアドレスや領域外
  へのアクセス防止に加えて、利用アドレス領域の変更を行う機能まで含んでい
  ます。RMA が効率的なのは、次の MPI_Win_fence を呼び出すまで実際には命
  令が実行されない点にあります。要約すると、RMA のしくみは、分散共有メモ
  リとメッセージ通信を巧妙に組み合わせたものですが、インタフェースがとて
  も明快で非常に効率的に通信ができる可能性を秘めています。

  3.6.  AFAPI (Aggregate Function API)

  AFAPI(Aggregate Function Application Program Interface)は PVM や MPI
  他とは違い、そもそも移植性のある抽象的なインタフェースを既存のネット
  ワーク機器やソフトウェア上に構築しようとして世に出したわけではありませ
  ん。AFAPI はむしろハードウェアにとても依存した低レベルなライブラリ
  で、PAPERS(Purdue's Adapter for Parallel Execution and Rapid
  Synchronization  <http://garage.ecn.purdue.edu/~papers/> 参照)用です。

  PAPERS についてはセクション 3.2 で簡単に述べましたが、パブリック・ドメ
  インで設計したネットワークで、独自の集合演算を実現し、遅延はわずか数ミ
  リ秒程度で済んでしまいます。しかし PAPERS で重要なのはこの点ではな
  く、1 つのスーパーコンピュータを作り上げる目的で開発が行われた点にあり
  ます。これは既存のスーパーコンピュータを対象にするというよりも、コンパ
  イラ技術を生かす相手としてスーパーコンピュータを対象としました。これは
  Linux クラスタの大部分がとった試みや、PVM、MPI のように標準的なネット
  ワークを使って比較的粒度が粗い並列アプリケーションを利用することに焦点
  を当てたものとは質的に異なっています。Linux PC を PAPERS システムの構
  成要素として採用したのは、最も費用対効果が高くとれる方法でプロトタイプ
  を実装した結果に過ぎません。

  様々に異なるプロトタイプの実装に対して、共通に使える低レベルなソフト
  ウェアが必要とされたことから、PAPERS ライブラリで作成したものが AFAPI
  の標準とされました。しかし AFAPI で使用しているモデルはそもそもシンプ
  ルで、並列化コンパイラでコンパイルしたものや SIMD アーキテクチャ用に作
  成した、粒度が細かい処理一般に適しています。モデルがシンプルなことで、
  PAPERS のハードウェアを容易に構築できるだけでなく、SMP 等の他の様々な
  ハードウェアへの AFAPI の移植も、思いのほか効率的に行えます。

  現状、AFAPI は TTL_PAPERS や CAPERS、WAPERS で接続している Linux クラ
  スタ上で動作しています。また SHMAPERS という System V の共有メモリライ
  ブラリを使って、SMP システムでも同様に動作しています(OS のシステムコー
  ルを使用しないばかりか、バスをロックする命令すら使わずに。セクション
  2.2 を参照)。 Linux クラスタ上で UDP のブロードキャストを使って普通の
  ネットワーク (例えばイーサネット)上で動くバージョンは開発中です。リ
  リースしたすべてのバージョンは、
  <http://garage.ecn.purdue.edu/~papers/> から取ってこられます。AFAPI の
  すべてのバージョンは、C や C++ から呼び出すように設計してあります。

  次のプログラム例は、セクション 1.3 であげた、円周率計算の AFAPI バー
  ジョンです。

  ______________________________________________________________________
  #include <stdlib.h>
  #include <stdio.h>
  #include "afapi.h"

  main(int argc, char **argv)
  {
    register double width, sum;
    register int intervals, i;

    if (p_init()) exit(1);

    intervals = atoi(argv[1]);
    width = 1.0 / intervals;

    sum = 0;
    for (i=IPROC; i<intervals; i+=NPROC) {
      register double x = (i + 0.5) * width;
      sum += 4.0 / (1.0 + x * x);
    }

    sum = p_reduceAdd64f(sum) * width;

    if (IPROC == CPROC) {
      printf("Estimation of pi is %f\n", sum);
    }

    p_exit();
    return(0);
  }
  ______________________________________________________________________

  3.7.  クラスタをサポートしているその他のライブラリ

  PVM や MPI、AFAPI に加えて、下記のライブラリが提供する機能が Linux シ
  ステムで構築したクラスタで並列処理を行うのに役立つかもしれません。この
  ドキュメントではこれらのシステムについては簡単に触れるだけに留めます。
  理由は簡単で、PVM や MPI、AFAPI と違い、私がほとんどまったくと言ってい
  いほどこれらのシステムを Linux クラスタ上でじかに扱ったことがないから
  です。これらのライブラリやその他ライブラリの中で、これは有効だ、と思わ
  れるものがあったなら、どうか私に電子メールを送ってください。何を発見し
  たかを書いていただき、pplinux@ecn.purdue.edu 宛までお願いします。検討
  した上、そのライブラリをこのセクションの補遺として追加するつもりです。

  3.7.1.  Condor(プロセス・マイグレーションをサポート)

  Condor は分散しているリソースの管理を行うシステムで、ワークステーショ
  ンで構成してある大規模な異機種クラスタを管理できます。計算を中心に長い
  時間を必要とするの処理を行いたいユーザが、クラスタの余っている能力を利
  用したい、という要求に答えるべく設計を行ってきました。Condor はプロセ
  スを実際に実行するマシン上に元々実行していたマシンの環境を広範囲に保持
  しています。たとえ元のマシンと実際に実行するマシンが共通のファイル・シ
  ステムやパスワードのしくみを共通に持っていなくてもです。最終的にジョブ
  の終了を保証する必要があるので、1 つのプロセスからなる Condor のジョブ
  は自動的にチェックポイントされ、ワークステーション間でマイグレーション
  します。

  【訳註：「プロセス・マイグレーション」とは、あるマシンで実行しているプ
  ロセスを他のマシンに引き継いで、引き続き実行することです。「チェックポ
  イント」とは、現在のプロセスやデータの状態を後で利用するために記録して
  おくことをです】

  Condor は  <http://www.cs.wisc.edu/condor/> から利用できます。 Linux
  への移植もあります。さらに詳しい情報は
  <http://www.cs.wisc.edu/condor/linux/linux.html> 【訳註：リンク切れ】
  にあります。詳細は、condor-admin@cs.wisc.edu に連絡を取ってください。

  3.7.2.  DFN-RPC (German Research Network - Remote Procedure Call)

  DFN-RPC(German Research Network Remote Procedure Call)は、科学技術アプ
  リケーション・プログラムで、ワークステーションと計算サーバもしくはクラ
  スタ間で分散、並列処理を行う目的で開発されました。インタフェース
  は、Fortran で書いたアプリケーションに最適化していますが、 DFN-RPC は
  C の開発環境でも利用できます。Linux にも移植されています。さらに詳細な
  情報は、 <ftp://ftp.uni-
  stuttgart.de/pub/rus/dfn_rpc/README_dfnrpc.html> にあります。

  3.7.3.  DQS (Distributed Queueing System)

  正確にはライブラリではないのですが、DQS 3.0(Distributed Queueing
  System) はジョブをキューイングするシステムです。Linux 上で開発、テスト
  されています。異機種クラスタを単一のシステムとして利用、管理できるよう
  に設計されています。 <http://www.scri.fsu.edu/~pasko/dqs.html> から利
  用できます。

  商用版もあり、CODINE 4.1.1(COmputing in DIstributed Network
  Environments) といいます。 <http://www.genias.de/genias_welcome.html>
  に情報があります。

  3.8.  利用可能なクラスタの一覧

  クラスタはいろいろと異なる方法で構築、運用できるので、クラスタの開発に
  貢献する興味深い活動を行っているグループがかなりあります。下記の一覧
  で、クラスタ関連のプロジェクトの中で皆さんが興味を持たれるであろうと思
  われるものをあげておきます。これには、Linux 固有のもの、クラスタ一般の
  ものが両方含まれています。リストはアルファベット順です。

  3.8.1.  Beowulf

  Beowulf プロジェクト( <http://www.beowulf.org/>) は、市販されている PC
  レベルのハードウェアと広い帯域幅を持つクラスタ組み込みのネットワーク、
  そして Linux オペーレーティング・システムをベースにしています。そのよ
  うな市販のワークステーションを使ってクラスタを組むためのソフトウェアを
  作成することにターゲットを当てています。

  Thomas Sterling 氏が Beowulf を推進していて、科学計算一般についての
  Linux クラスタリングについての、雄弁かつ率直な擁護者でもあります。実際
  に多くのグループが自分たちのクラスタを「Beowulf クラスの」システムと呼
  んでいます。本当はそのクラスタが公式の Beowulf の設計にそれほど似てい
  ないにもかかわらず。

  Don Becker 氏は Beowulf プロジェクトのサポートをしていて、一般的な
  Linux で利用可能なネットワーク・ドライバも数多く作成しています。そのド
  ライバの多くは BSD でも利用されています。彼はこれらのネットワーク・ド
  ライバの多くが複数の並列接続を負荷分散し、広い帯域幅を高価なスイッチン
  グ・ハブ無しに実現することに対しても責任を負っています。このタイプの負
  荷分散は、元々 Beowulf クラスタに独自のしくみでした。

  3.8.2.  Linux/AP+

  Linux/AP+ プロジェクト( <http://cap.anu.edu.au/cap/projects/linux/>)
  は、正しくは Linux のクラスタリングではありませんが、Linux を富士通の
  AP1000+ に移植して、マシンに見合った並列処理機能の拡張を目指していま
  す。AP1000+ は市販の SPARC ベースの並列マシンで、トーラス構造の特殊な
  ネットワークを持ち、帯域幅が 25 MB/秒、遅延が 10 ミリ秒…つまり SPARC
  ベースの Linux クラスタにそっくりです。

  【訳註：トーラス構造をしたスーパーコンピュータのイメージは IBM
  Bluegene <http://www.research.ibm.com/bluegene/original_pictures.html>
  を見てください】

  3.8.3.  Locust

  Locust プロジェクト( <http://www.ecsl.cs.sunysb.edu/~manish/locust/>)
  【訳註：リンク切れ】は、分散仮想共有メモリシステムを構築しています。コ
  ンパイル時に決定する情報を使用して、メッセージの遅延を顕在化させずに実
  行時のネットワークの流量を減らしています。Pupa が Locust の基本通信サ
  ブシステムで、イーサネットを使って FreeBSD が動作している 486 PC を接
  続しています。Linux はわかりません。

  3.8.4.  Midway DSM(分散共有メモリ)

  Midway(
  <http://www.cs.cmu.edu/afs/cs.cmu.edu/project/midway/WWW/HomePage.html>)
  は、TreadMarks(後述)とは違い、ソフトウェアベースの DSM(分散共有メモリ)
  システムです。ページ・フォルトのように遅いしくみではなく、コンパイル時
  の情報を利用していたり、フリーに利用できる点が良いのですが、残念ながら
  Linux クラスタでは動きません。

  3.8.5.  Mosix

  MOSIX は BSDI の BSD/OS に手を入れて、動的なロード・バランシングとネッ
  トワーク上でグループを組んでいる PC 間でプリエンプティブにプロセス・マ
  イグレーションができるようにしています。これは並列処理にうってつけとい
  うわけではありませんが、拡張性がある SMP のようなクラスタで利用するの
  に適しています。Linux バージョンは出るのか？ という問にたいする答は、
  <http://www.cs.huji.ac.il/mosix/> にあります。

  3.8.6.  NOW (Network Of Workstations)

  Berkeley の NOW(Network Of Workstations)プロジェクト(
  <http://now.cs.berkeley.edu/>)は、ネットワークに接続しているワークス
  テーションを使って、並列計算を行う流れを生み出しました。数多くの研究が
  継続しており、「100 個のプロセッサを載せた実用的なシステムをここ数年で
  実証する」ことに目標を定めています。悲しいことに Linux は使っていませ
  ん。

  3.8.7.  Linux を使った並列処理

  Linux を使った並列処理の WWW サイトは、
  <http://yara.ecn.purdue.edu/~pplinux/> にあります。ここは、この HOWTO
  のホームで、見るのに丸一日かかるようなチュートリアルのスライドを含むた
  くさんの関連ドキュメントがあります。PAPERS プロジェクトの活動だけでは
  なく、パーデュ大学の School of Electrical and Computer Engineering
  は、並列処理のリーダー的な存在です。このサイトを開設したのは、皆さんが
  Linux PC で並列処理をする手助けをするためです。

  パーデュ大学の最初の Linux PC のクラスタが 1994 年 2 月に組み立てられ
  て以来、Linux PC クラスタがパーデュ大学でたくさん組み立てられました。
  その中にはビデオ・ウォールがついたシステムもいくつかありました。それら
  のクラスタは、386 や 486、Pentium システム(Pentium Pro システムはあり
  ません)を使いましたが、最近になって Intel がパーデュ大学に寄付をしてく
  れました。その寄付で Pentium II システムを使った大規模なクラスタを複数
  構築できるようになりました(165 台ものマシンで 1 つのクラスタを構築する
  計画あり)。これらにクラスタはすべて PAPERS のネットワークに接続する予
  定で、その内の大部分はこれまでのネットワークにも接続しています。

  3.8.8.  Pentium Pro クラスタのセミナー

  1997 年 4 月 10、11 日にアイオワ州のデモインで AMES 研究所主催で、
  Pentium Pro クラスタのセミナーが開かれました。このセミナーからの報告
  は、 <http://www.scl.ameslab.gov/workshops/PPCworkshop.html> にありま
  す。ここは PC クラスタについて出席者すべてから集めた情報の宝庫になって
  います。

  3.8.9.  TreadMarks DSM (分散共有メモリ)

  DSM(分散共有メモリ)は、メッセージ通信システムが、SMP のごとくふるまえ
  る技術です。そのようなシステムはかなり多く、それらのほとんどは OS の
  ページ・フォールトのしくみを利用してメッセージ通信をはじめます。
  TreadMarks( <http://www.cs.rice.edu/~willy/TreadMarks/overview.html>)
  は、そのようなシステムの中でも効率的で、Linux クラスタ上でも動作しま
  す。「TreadMarks は大学や非営利組織に対してわずかな費用で配布される」
  という点が残念なところです。さらにこのソフトウェアについての知りたいな
  ら、 treadmarks@ece.rice.edu に連絡をとってください。

  3.8.10.  U-Net (User-level NETwork interface architecture)

  U-Net(User-level NETwork interface architecture)プロジェクトはコーネル
  大学の  <http://www2.cs.cornell.edu/U-Net/Default.html> で行われていま
  す。市販のネットワーク機器を使ってネットワーク・インタフェースを仮想化
  し、少ない遅延と広い帯域幅を提供しています。この仮想インタフェースに
  よって、アプリケーションはオペレーティング・システムを通すことなく、
  メッセージを送受信できます。U-Net は、DEC の DC21140 チップを載せてい
  るファースト・イーサネットカードと Fore Systems の PCA-200(PCA-200E で
  はない) ATM カードを使った Linux クラスタ上で動いています。

  【訳註：開発者がカルフォルニア大学のバークレイ校に異動したため Web サ
  イトも <http://www.cs.berkeley.edu/~mdw/projects/unet/> に移っていま
  す】

  3.8.11.  WWT (Wisconsin Wind Tunnel)

  ウィスコンシン大学では、クラスタ関連の研究がとても盛んに行われていま
  す。 WWT(Wisconsin Wind Tunnel)プロジェクト(
  <http://www.cs.wisc.edu/~wwt/>)は、コンパイラと並列処理の基盤となる機
  器間の「標準的」なインタフェース開発に関連するすべて分野で研究を行って
  います。Wisconsin COW(Cluster Of Workstations)は、Cooperative Shared
  Memory や Tempest、Paradyn Parallel Performance Tools 等を開発していま
  す。残念ながら、Linux についてはあまり触れられていません。

  【訳註：Paradyn と Wisconsin Wind Tunnel は、独立したプロジェクトの
  ニックネームです。Cooperative Shared Memory は、ソフトウェアでデータの
  変化を管理し、共有メモリのハードウェアを単純化します。Tempest は、プロ
  グラム、コンパイラ、ライブラリにメッセージ通信や透過的な共有メモリ、も
  しくはその組み合わせを提供するインタフェースです。Paradyn Parallel
  Performance Tools は、プログラマに対してプログラムの遅い部分を自動的に
  抽出し、その問題点についての情報をプログラマに与え、チューニングの手助
  けを行うツールです】

  4.  SIMD Within A Register(例えば MMX を利用)

  SIMD(Single Instruction stream, Multiple Data stream) Within A
  Register(SWAR)は、新しいアイディアではありません。k ビットのレジスタと
  データパス、演算装置を持つマシンでは、一般的なレジスタ処理は、 SIMD の
  並列処理で動くことが知られています。つまり k/n ビットの整数領域の値と
  して n 個実行できます。しかし最近になって SWAR 技術によってマルチメ
  ディア処理が 2 倍から 8 倍早くなったことで、この技術がコンピューティン
  グのメインストリームで関心が持たれるようになったのに過ぎません。1997
  年時点でのマイクロプロセッサのほとんどは、組み込みのハードウェアで
  SWAR をサポートしています。

  o  AMD K6 MMX (MultiMedia eXtensions)

  o  Cyrix M2 MMX (MultiMedia eXtensions)

     【訳註：VIA の Web サイトはオンラインドキュメントの提供を停止してい
     ます】

  o  Digital Alpha MAX (MultimediA eXtensions)

     【訳註：MAX は MVIという名称に変更されています】

  o  Hewlett-Packard PA-RISC MAX (Multimedia Acceleration eXtensions)
     【訳註：リンク切れ】

  o  Intel Pentium II & Pentium with MMX (MultiMedia eXtensions)

  o  Microunity Mediaprocessor SIGD (Single Instruction on Groups of
     Data)

  o  MIPS Digital Media eXtension (MDMX, pronounced Mad Max)【訳註：リン
     ク切れ】

  o  Sun SPARC V9 VIS (Visual Instruction Set)

  新しいマイクロプロセッサが提供しているハードウェア機能には、極秘になっ
  ていたり、あるフィールドの大きさのためだけに必要な操作のようなおかしな
  ものはほとんどありません。重要な点は、SWAR の操作を効率的行うのにいか
  なるハードウェアのサポートも必要がないということです。例えば、論理的に
  1 つのレジスタを分割しても、ビットごとの演算には影響がでません。

  4.1.  SWAR の何が優れているのか？

  最近のプロセッサであればどれでも多かれ少なかれ SWAR の並列処理機能を生
  かせます。ただ残念なことに、SWAR で大幅に機能強化された命令セットが、
  汎用的に並列処理に適用できるわけではありません。実際には、皆さんも
  Pentium と「MMX Pentium」の性能の違いは、MMX が発売されたと同時に搭載
  されたより大きな L1 キャッシュ等のおかげであることに気づかれていると思
  います。本当のところ、SWAR(もしくは MMX)は何が良いのでしょうか？

  o  データは整数のみ。小さければ小さい程良い。32 ビットの値 2 つが、 64
     ビットの MMX レジスタにはちょうどいいが、8 個の 1 バイトのキャラク
     タもしくは 1 ビットの値をチェス盤に値を散らすようにセットする

     注意：浮動小数点版の MMXもあるようだが、これを記述している時点で
     は、ほんの少ししか出まわっていないと言われている。Cyrix は以前発表
     したスライド( <ftp://ftp.cyrix.com/developr/mpf97rm.pdf>) 【訳註：
     リンク切れ】の中で、MMFP についてコメントしている。 MMFP は 2 つの
     32 ビット浮動小数値をパックして 64 ビットの MMX レジスタに納め、こ
     れをまとめて 2 つの MMFP パイプラインに流し、クロック当たり 4 つの
     単精度浮動小数点演算をサポートするとしている。

     【訳註：2001.3 現在、Cyrix(現 VIA 傘下)のプロセッサでこの機能を実装
     しているものはありませんし、予定も発表されていません】

  o  SIMD もしくはベクトル型の並列性。同じ操作がすべてのフィールドに対し
     て同時に発生する。特定フィールドの結果を無効にする(つまり、SIMD が
     マスキングをすることと同じ)手段があるが、コーディングが複雑になった
     りパフォーマンスが低下したりする

  o  ローカルで規則的な(パックしてあればなお良い)メモリ参照パターン。
     SWAR 全般、特に MMX はランダムな順序でアクセスするととんでもないこ
     とになる。x[y](y インデックスの配列)のようなベクトルを集めてくる処
     理はとても重くなる

  これらの制約は深刻ですが、このタイプの並列性はマルチメディアのアプリ
  ケーションだけでなく、並列アルゴリズムではよく生じることです。アルゴリ
  ズムのタイプが適切であれば、SWAR は、SMP や クラスタの並列性より有効で
  すし、使用するに当ってまったく費用がかかりません。
  4.2.  SWAR プログラミング入門

  SWAR(SIMD Within A Register)の基本的なコンセプトは、ワード長レジスタの
  操作を nk/n ビットのフィールド値を SIMD の並列演算をすることで、計算速
  度を向上させる点にあります。しかし、 SWAR 技術は扱いづらく、実際 SWAR
  の演算は同様な演算を行う順次演算を連続して行ったのと比べて重くなりま
  す。それは SWAR の演算には、フィールドを分割する命令がさらに加わるため
  です。

  この点を説明するのに、SWAR のしくみをかなり単純化して考えてみることに
  しましょう。4 つの 8 ビットフィールド長を持つ 32 ビットのレジスタがあ
  るとします。 2 つのレジスタの値は、下記のようになります。

  ______________________________________________________________________
           PE3     PE2     PE1     PE0
        +-------+-------+-------+-------+
  Reg0  | D 7:0 | C 7:0 | B 7:0 | A 7:0 |
        +-------+-------+-------+-------+
  Reg1  | H 7:0 | G 7:0 | F 7:0 | E 7:0 |
        +-------+-------+-------+-------+
  ______________________________________________________________________

  この簡単な図はそれぞれのレジスタに 4 つの独立した 8 ビットの値のベクト
  ルがあることを表しています。A と E が Reg0 と Reg1 のプロセッシングエ
  レメント 0(PE0)、B と F を PE1 のレジスタ値とも見なせます。その他の項
  目も同様です。

  この後ドキュメントでは SIMD 並列処理を分類し、ベクトルを扱う関数がどの
  ように実装されているかをこの整数ベクトルを元にして概観していきます。

  4.2.1.  ポリモフィックな操作

  SWAR の操作のいくつかは、通常の 32 ビット整数の操作そのままに処理しま
  す。しかし実際には、この操作が並列して独立に 8 ビットのフィールドを操
  作することとは無関係です。このような SWAR の操作のことをポリモフィック
  と呼んでいます。その操作がフィールドのタイプ(サイズ) に影響されないた
  めです。

  どのフィールドがゼロでないかをテストするのは、すべてに対してビット論理
  演算をする点でポリモフィックな操作と言えます。例えば、通常のビット論理
  積演算(C の & 演算子)はフィールドの大きさがいくつであっても、ビット演
  算となります。単純なビット論理積演算を上記のレジスタで行うと下記のよう
  になります。

  ______________________________________________________________________
            PE3       PE2       PE1       PE0
        +---------+---------+---------+---------+
  Reg2  | D&H 7:0 | C&G 7:0 | B&F 7:0 | A&E 7:0 |
        +---------+---------+---------+---------+
  ______________________________________________________________________

  ビット論理積演算は常にオペランド・ビットの k という値にだけ左右されて
  k というビット値になるので、フィールドの大きさはすべて同じ 1 つの命令
  を使ってカバーできます。

  4.2.2.  分割操作

  あいにく SWAR 操作で重要なものの中にも、ポリモフィックでない操作がたく
  さんあります。四則演算のような算術演算では、フィールド間で桁の上げ下げ
  をする必要があります。そのような操作を SWAR で行うことを「分割する」と
  呼んでいます。理由は、そのような操作が事実上オペランドや結果を分割し
  て、フィールド間のやりくりを防がなければならないからです。実際この効果
  を生むのには 3 つの異なる手法が取られます。

  4.2.2.1.  分割命令

  おそらく、分割操作の中で最も理解しやすい実装方法は、「分割並列命令
  (partitioned parallel instruction)」をハードウェアでサポートすることで
  しょう。これでフィールド間の桁の上げ下げを排除します。この方法を使えば
  パフォーマンスは文句なく上がりますが、プロセッサの命令セットが変更され
  たり、フィールドの大きさに対しての制限が増えてしまいます (例えば、8
  ビットのフィールドはサポートしても、12 ビットは駄目であるとか)。

  AMD や Cyrix、Intel の MMX や Digital の MAX、HP の MAX、 Sun の VIS
  すべては、分割命令の実装に制限があります。残念なことに、それぞれの命令
  セット拡張は制限事項についても著しく異なっているので、それらの間では若
  干ですがアルゴリズムに移植性がなくなっています。例えば、下記で分割命令
  の一部を見てみましょう。

  ______________________________________________________________________
    Instruction           AMD/Cyrix/Intel MMX   DEC MAX   HP MAX   Sun VIS
  +---------------------+---------------------+---------+--------+---------+
  | Absolute Difference |                     |       8 |        |       8 |
  +---------------------+---------------------+---------+--------+---------+
  | Merge Maximum       |                     |   8, 16 |        |         |
  +---------------------+---------------------+---------+--------+---------+
  | Compare             |           8, 16, 32 |         |        |  16, 32 |
  +---------------------+---------------------+---------+--------+---------+
  | Multiply            |                  16 |         |        |    8x16 |
  +---------------------+---------------------+---------+--------+---------+
  | Add                 |           8, 16, 32 |         |     16 |  16, 32 |
  +---------------------+---------------------+---------+--------+---------+
  ______________________________________________________________________

  この表では数字がフィールドの大きさをビットで表していて、この値でそれぞ
  れの操作が可能になっています。もっと興味深い命令など多くの命令が表には
  載っていませんが、この表だけでも違いの大きさは明らかです。結論として
  は、明らかに高級言語(HLL)はプログラミング・モデルとしてまったく役に立
  たず、移植性もほとんどないことです。

  4.2.2.2.  コードを修正して、分割命令を使わない

  分割命令を使った分割操作を実装すれば確かに効果が上がりますが、もし必要
  としている分割操作をハードウェアがサポートしていなかったらどうなるで
  しょう？ 答は、通常の命令群を使ってフィールド間で桁の上げ下げを行う操
  作を行い、フィールド間での余計なやりとりを正します。

  この手段はまさにソフトウェアによるもので、修正によるオーバーヘッドが加
  わります。しかし、これで汎用的にフィールドの分割が可能になります。この
  方法は非常に汎用的で、ハードウェア毎の分割命令の違いを埋めるだけでな
  く、まったくハードウェアのサポートのないマシンに対しても機能をフルに提
  供できます。実際には、C のようなプログラミング言語でコードを書くこと
  で、SWAR プログラムはとても移植しやすくなります。
  ここで疑問が沸いてきます。SWAR の分割操作を使わずにコードを修正してシ
  ミュレートした場合、正確にどのくらい効率が落ちるのでしょうか？いいとこ
  ろを突いた質問です…しかし、皆さんが思っているほど難しい操作が多いわけ
  ではありません。

  4 つの要素を持つ 8 ビットの整数ベクトル 2 つを 32 ビット操作を使って加
  算(x+y)を実現することを考えてみましょう。

  通常の 32 ビット加算は正確な値になると思いますが、それはどの 8 ビット
  フィールドも次のフィールドに影響を与えない場合です。したがって、最終的
  にはそのような桁上がりがまったく起こらないことを保証しなければいけませ
  ん。 k ビットのフィールドを加算すると、多くても k+1 ビット値になるの
  で、桁上がりは単にそれぞれのフィールドの最上位のビットを「マスクをかけ
  ること」で防げます。これはそれぞれのオペランドに対して 0x7f7f7f7f を
  ビット単位で論理積を取った後、通常の 32 ビット加算をすれば OK です。

  ______________________________________________________________________
  t = ((x & 0x7f7f7f7f) + (y & 0x7f7f7f7f));
  ______________________________________________________________________

  これで、それぞれのフィールドの最上位ビットを除いて正確な値がでました。
  それぞれのフィールドの値を正確に計算するには、x と y 2 つに分割された
  最上位の 1 ビットの値を 7 ビットで計算した t に加算するだけです。都合
  のいいことに、分割した 1 ビットの加算は通常の排他論理和演算として実装
  されています。つまり、結果は単純に下記のように求められます。

  ______________________________________________________________________
  (t ^ ((x ^ y) & 0x80808080))
  ______________________________________________________________________

  そんなにやさしくはないですね。結局ちょうど 4 つの加算に 6 回の操作が必
  要でした。しかし、操作の回数はフィールドの数がどのくらいあるか…という
  こととは関係ないことに注意してください。フィールドが増える程速度が上が
  ります。とにかく実際に速度が向上します。それはフィールドを 1 回の操作(
  整数ベクトル)でロードやストアすることで、レジスタを利用する割合が増
  え、動的にコードを実行するのに必要なスケジューリングの依存関係がほとん
  どなくなるからです(部分的なワードへの参照を避けられるため)。

  4.2.2.3.  フィールド値の制御

  上記 2 つの方法とも、分割操作でレジスタを最大限利用するように実行する
  のに対して、この方法ではフィールドの値を制御することで、より効率的に計
  算を行い、フィールド間の桁の上げ下げが決して起こらないようにします。例
  えば、加算したフィールドすべてにおいてオーバーフローが生じないことがわ
  かっている場合、分割操作の加算は通常の加算命令を使って実行できます。実
  際この制約下で通常の加算命令はポリモフィックとなり、コードを修正するこ
  となしにどんなフィールドの大きさでも利用できることになります。ここで問
  題なのは、どうしたらフィールドの値間で桁の上下げを起こさないかを確認す
  る方法です。

  この特性を確認する方法の 1 つは、フィールドの値の範囲を制約する分割命
  令を実装することです。Digital の MAX にあるベクトル最小最大命令は、
  ハードウェアでこれをサポートしているようで、フィールドの値をある範囲で
  カットして、フィールド間で桁の上げ下げを起こさないようにしています。

  しかし、フィールドの値の範囲を効率的に制限する分割命令がない場合はどう
  なるでしょうか…桁の上げ下げで隣のフィールドに影響を与えていないことを
  手軽に保証する十分条件はあるのでしょうか？ その答は演算の特性を分析す
  ることにあります。k ビットの 2 つの数を加算すると、結果は多くても k+1
  ビットです。したがって、k+1 ビットのフィールドなら、通常の命令を使って
  いても問題なく収まります。

  つまり先にあげた例を 8 ビットのフィールドを 7 ビットに 1 ビットの「桁
  の上げ下げ用の領域」を加えたものとしてみましょう。

  ______________________________________________________________________
                PE3          PE2          PE1          PE0
        +----+-------+----+-------+----+-------+----+-------+
  Reg0  | D' | D 6:0 | C' | C 6:0 | B' | B 6:0 | A' | A 6:0 |
        +----+-------+----+-------+----+-------+----+-------+
  ______________________________________________________________________

  7 ビットのベクトルの加算は次のようになります。まず分割操作をする前に、
  繰り上げ用領域に当るビット(A' や B'、C'、 D')すべてが 0 であると想定し
  ます。単純に通常の加算演算を行うとすべてのフィールドは正しい 7 ビット
  分の値になります。しかし、繰り上げ用領域のビットには、1 という値が入っ
  ているものが出てくるかもしれません。これを修正するには、もう 1 回だけ
  通常の操作を行います。それは繰り上げ用領域のビットにマスクをかけること
  です。7 ビット分の整数のベクトル加算 x+y はこのようになります。

  ______________________________________________________________________
  ((x + y) & 0x7f7f7f7f)
  ______________________________________________________________________

  これで、4 つの加算がちょうど 2 つの命令で済み、はっきりと速度が向上し
  ます。

  鋭い読者の方なら、繰り上げ用領域のビットを 0 にすると減算演算がうまく
  ないことに気づかれたと思います。しかしこれはとても簡単に修正できます。
  x-y を計算するには、初期状態で x の繰り上げ用領域のビットがすべて 1 で
  y がすべて 0 であることを保証すればいいのです。最悪の場合でもこのよう
  にすれば良いはずです。

  ______________________________________________________________________
  (((x | 0x80808080) - y) & 0x7f7f7f7f)
  ______________________________________________________________________

  しかし、追加したビット論理和演算は、最後の操作である & 0x7f7f7f7f の部
  分よりも x に | 0x80808080 で値を出す操作のところで最適化できます。

  どの方法を SWAR の分割処理として使うべきなのでしょうか？ 答は簡単で、
  「一番速度が速いものならどれでも」です。興味深いことに、同じプログラム
  を同じマシンで実行しても、フィールドのサイズが異なれば最適な方法も変わ
  るかもしれません。

  4.2.3.  値間のやりとりと型変換操作

  並列計算の中には画素に対する演算などのように、あるベクトル中の i 番目
  の値がオペランド側のベクトルの i 番目の位置にある値とだけ相関関係にあ
  る場合がありますが、必ずしもこのケースとはかぎりません。例えば、スムー
  ジングのような画素の操作は、隣の画素の値をオペランドとして必要とします
  し、FFT(fast Fourier transform 高速フーリエ変換)のような変形は、さらに
  複雑な(局所的ではない)やりとりをする傾向にあります。
  一次元の隣接する値のやりとりを分割操作ではないシフト操作で実現するのは
  難しいことではありません。例えば、PEi の値を PE(i+1) に移動するには、
  単純なシフト操作で十分です。フィールドが 8 ビットの大きさであれば、下
  記のようになります。

  ______________________________________________________________________
  (x << 8)
  ______________________________________________________________________

  しかしいつもそれほど単純とは限りません。例えば、PEi を PE(i-1) に移動
  するには単純なシフト操作で十分であるかもしれません…が、C ではシフト操
  作が符号ビットを正しく維持できているかどうかを確認できませんし、マシン
  の中には符号化シフトしか正しく行えないものもあります。つまり一般的に
  は、符号ビットのコピーを行って、それを 0 に設定しておく必要がありま
  す。

  ______________________________________________________________________
  ((x >> 8) & 0x00ffffff)
  ______________________________________________________________________

  「折り返してつなげる」方法をとると、シフト操作を分割せずにそこそこ効率
  的に行えます。例えばこの方法を使って PEi を PE(i+1)に移動するには、下
  記のようにします。

  ______________________________________________________________________
  ((x << 8) | ((x >> 24) & 0x000000ff))
  ______________________________________________________________________

  さらに一般的なパターンのやり取りを実装しなければならないとなると、問題
  が大きくなります。HP の MAX での命令セットに限って言うと、任意にフィー
  ルドを再配置する命令が単独で存在します。この命令を Permute と呼んでい
  ます。Permute 命令という名付け方は不適切で、任意に並べ替えを行うだけで
  なく、繰り返しもできてしまいます。つまり、自由に x[y] 演算が可能になっ
  ています。

  x[y] という操作は、そのような単独命令なしでは実現することが非常に困難
  です。処理するコードが長くなるだけでなく、効率も下がります。実際には順
  次処理になってしまいます。これではどうしようもありません。 x[y] 演算を
  比較的高速に行えるシステムが、MasPar の MP1/MP2 や Thinking Machines
  の CM1/CM2/CM200 という SIMD スーパーコンピュータであることがこれを裏
  付けています。しかし、そのようなスーパーコンピュータであっても、x[y]
  は隣り合ったフィールドとのやりとりと比べて常に遅く、できるだけ x[y] 演
  算を必要としないようにアルゴリズムを設計する場合がほとんどです。つまり
  ハードウェアのサポートがなければ、 x[y] は演算として不適切と言っても…
  まあ少なくとも安価に実現できるわけではありません。

  繰り返し操作(還元、走査など) 繰り返し処理は、計算している値間に逐次的
  な関係がはっきりと認められる計算のことを指します。しかし、繰り返し処理
  に結合操作がある場合は、木構造の並列アルゴリズムを使って、計算を記録で
  きる場合があります。

  最も一般的なタイプの並列化した繰り返し処理には、結合還元(associative
  reduction)と言われるものがあります。例えばベクトルの合計を計算するの
  に、 C のコードで単に逐次処理すると下記のようになります。

  ______________________________________________________________________
  t = 0;
  for (i=0; i<MAX; ++i) t += x[i];
  ______________________________________________________________________

  しかし、加算の順序が意味を持つことはまれです。浮動小数点や飽和演算で
  は、加算の順序が違うと結果が変わってしまいますが、通常のラップラウンド
  な整数の加算は順序によって結果に影響がでません。したがって、順次に実行
  する形から木構造で並列に合計を出すコードに書き換えられます。こうする
  と、まず最初に対となる値を加算し、それが合計値の一部となり…ということ
  を繰り返して、最終的に 1 つの合計値になるまで計算します。8 ビットの値
  を 4 つで構成したベクトルならば、ちょうど 2 回の加算が必要となります。
  最初に 2 つの 8 ビットの値を加算して、16 ビットのフィールド 2 つに結果
  が入ります (フィールドそれぞれに 9 ビットの結果が入っています)。

  【訳註：飽和演算とは、あらかじめ演算結果の範囲を決めておき、その範囲を
  越えた演算結果になった場合にあらかじめ用意した結果に置き換える処理で
  す】

  ______________________________________________________________________
  t = ((x & 0x00ff00ff) + ((x >> 8) & 0x00ff00ff));
  ______________________________________________________________________

  その次に、16 ビットのフィールドに入っている 9 ビットの値を 2 つ加算し
  て、 10 ビットの結果を 1 つ得ることになります。

  ______________________________________________________________________
  ((t + (t >> 16)) & 0x000003ff)
  ______________________________________________________________________

  実際ここでは、16 ビットのフィールドを 2 つ加算したことになります…が、
  先頭の 16 ビットの加算は意味がありません。というのは、結果を 1 つの 10
  ビットの値としてマスクしてしまうからです。

  走査は「並列化した単項演算」とも呼ばれていて、効率的に実装するのはいく
  ぶん困難です。還元とは異なり、走査の結果が分割されるからです。このよう
  な訳で、走査はきっと連続した分割操作として実装できます。

  4.3.  Linux における MMX を使った SWAR

  Linux にとって IA32 プロセッサは一番の関心事です。AMD や Cyrix、 intel
  すべてが同じ MMX 命令を実装しているのは素晴らしいことです。しかし、MMX
  のパフォーマンスにはばらつきがあります。例えば、K6 は MMX のパイプライ
  ンを 1 つしか持っていないのに対して、MMX Pentium は 2 つ持っています。
  ただ intel がいまだにへんてこりんなコマーシャルをやっているのにはうん
  ざりしていますが… ;-)

  【訳註：2001.3 現在 Intel および AMD の高性能デスクトップ PC 用プロ
  セッサである PentiumIII と Athlon は整数演算の高速化である MMX を拡張
  し、それぞれ SSE(internet streaming SIMD extensions)、Enhanced 3DNow!
  を実装し、浮動小数点演算の高速化をはかりました。浮動小数点演算に関して
  は Pentium III の方が Athlon より高速と言われていますが、浮動小数点の
  SIMD 命令については Athlon の方が高速と言われています。大きな理由
  は、SSE が 64 ビットの演算装置で 128 ビットのオペランドを処理するので
  2 サイクルかかるのに対し、Enhanced 3DNow! は 64 ビットの演算装置で 64
  ビットのオペランドを処理するので 1 サイクルで済むためです】
  SWAR に MMX を活用する方法は 3 つあります。

  1. MMX ライブラリのルーチンを使う。特に intel は「パフォーマンス・ライ
     ブラリ」 <http://developer.intel.com/software/products/perflib/> と
     いうものをいくつか開発していて、マルチメディア処理全般を対象に手作
     業で最適化した様々なルーチンを提供している。多少の努力をすれば、こ
     れらのライブラリのルーチンのいくつかを使って、非マルチメディア用の
     アルゴリズムを計算中心の部分を中心に書き直せる。これらのライブラリ
     は現状 Linux では利用できないが、移植可能である

  2. MMX 命令を直接使う。これは 2 つの理由から多少複雑になる。まず、MMX
     がプロセッサ上で利用できないかもしれないこと。そうなると他の手段を
     実装しなければならない。次の問題は、Linux で使っている IA32 のアセ
     ンブラが現状では MMX 命令を認識しないことである

  3. 高水準言語もしくは適切な MMX 命令を直接生成できるコンパイラのモ
     ジュールを使用する。現状そのようなツールは開発中であるが、まだ
     Linux 上ではすべての機能が動いていない。例えば我々はパーデュ大学 (
     <http://dynamo.ecn.purdue.edu/~hankd/SWAR/>)において現在コンパイラ
     を開発中である。それらは並列化を施してある少々癖のある C で書かれて
     おり、C の関数として呼び出せる SWAR モジュールを生成できる予定であ
     り、MMX をはじめとして SWAR の機能をサポートするものなら何でも利用
     できる。最初のプロトタイプであるモジュール・コンパイラは 1996 年秋
     に作成されたが、当初目指していた段階に到達するにはまだまだ時間がか
     かる

     【訳註：2001.3 現在、Linux 用の MMX や 3DNow!、SSE のライブラリ及び
     コンパイラ関連は、The SWAR Homepage at Purdue University
     <http://shay.ecn.purdue.edu/~swar/> にあります】

  要約すると、MMX を使った SWAR はまだ使い物になりません。しかし少し努力
  すれば上記 2 番目の方法は現在でも利用できます。主要なポイントをあげま
  す。

  1. プロセッサが MMX をサポートしていなければ MMX は使えない。下記の
     GCC のコードは MMX をプロセッサがサポートしているかを判定する。 0
     を返せば未サポート、0 以外ならサポートしている

     ___________________________________________________________________
     inline extern
     int mmx_init(void)
     {
             int mmx_available;

             __asm__ __volatile__ (
                     /* Get CPU version information */
                     "movl $1, %%eax\n\t"
                     "cpuid\n\t"
                     "andl $0x800000, %%edx\n\t"
                     "movl %%edx, %0"
                     : "=q" (mmx_available)
                     : /* no input */
             );
             return mmx_available;
     }
     ___________________________________________________________________

  2. MMX レジスタは基本的には GCC が unsigned long long を呼び出した場合
     にそれを保持する。つまりメモリベースのこの型の変数は、 MMX モジュー
     ルとそれを呼び出した C プログラム間のやりとりの手段となる。その一方
     で、MMX データを 64 ビットにアラインしたデータ構造として宣言でき
     る(unsigned long long の大きさを持つ union として型宣言することで、
     簡単に 64 ビットにアラインされていることを保証できる)

  3. MMX が利用可能なら、アセンブラ命令の .byte を使って MMX コードが書
     け、命令をエンコードできる。これは手作業では骨が折れるが、コンパイ
     ラでコードを生成するのは難しくない。例えば、MMX 命令の PADDB
     MM0,MM1 は GCC のインライン・アセンブラのコードで下記のようにエン
     コードできる

     ___________________________________________________________________
     __asm__ __volatile__ (".byte 0x0f, 0xfc, 0xc1\n\t");
     ___________________________________________________________________

  MMX が浮動小数点演算をするのと同じハードウェアのある部分を使用している
  ことを忘れないように。MMX が混じっているコードは、浮動小数点演算を呼び
  出してはいけない。また、浮動小数点のスタックは、MMX コードを実行する前
  には空にしておく必要がある。通常、浮動小数点のスタックは C の関数の先
  頭で浮動小数点を使っていないなら空になっている

  4. MMX コードから抜け出すには、EMMS 命令を実行する。エンコードすると下
     記のようになる

     ___________________________________________________________________
     __asm__ __volatile__ (".byte 0x0f, 0x77\n\t");
     ___________________________________________________________________

  上記があまりに扱いづらく、洗練されていないと感じるなら、それはその通り
  でしょう。しかし MMX はまだできたばかりで…このドキュメントの将来の版
  では、もっと優れた MMX による SWAR をプログラムを提示できると思いま
  す。

  5.  Linux に搭載されている付加プロセッサ

  この方法は最近注目を浴びなくなってきていますが、並列計算機構を付加した
  Linux システムを使うと、他の並列処理方式では到底不可能な低いコストと高
  いパフォーマンスを Linux で実現できます。問題は利用できるソフトウェア
  がほとんどないことで、自分でかなりのことを行う必要があります。

  5.1.  優れたホスト、それは Linux PC

  一般的に付加する形で搭載されているプロセッサは、ある種の機能を動作させ
  ることに特化しています。

  自分で多少なりとも作業するという事実にがっかりする前に、まず理解してお
  いて損しないことがあります。Linux PC に特定のシステムを採用するのは難
  しいことかもしれませんが、Linux PC はこの種の利用のしかたに向いている
  数少ないプラットフォームの 1 つである、ということです。

  PC がホストとして優れている理由は、主に 2 つあります。まず、安価で簡単
  に拡張できることです。メモリやディスク、ネットワーク等の資源は苦労なし
  に PC へ追加できます。それから、インタフェースが容易に取れる点もあげら
  れます。ISA や PCI カードを広く利用できるだけでなく、パラレルポートの
  ように手頃なパフォーマンスをインタフェースを拡張することなく利用できま
  す。IA32 は入出力空間を分割しているので、ハードウェアの入出力アドレス
  の保護を個々の入出力ポートのアドレスのレベルに合わせることで、インタ
  フェースを簡単に取ることができます。
  Linux は優れたホスト OS でもあります。フリーに利用できるソースコードや
  多数の「ハッキング」ガイドがきっと役に立ちます。Linux は、ほぼリアルタ
  イムに近いスケジューリングができますが、真にリアルタイムに動くバージョ
  ンとして  <http://www.rtlinux.org/> もあります。もちろんそれよりも重要
  なのは、完璧な UNIX 環境を提供するということです。Linux は Microsoft
  の DOS や Windows で動作するように書かれた開発用のツールもサポートして
  います。MSDOS のプログラムは dosemu を使って、Linux のプロセスとして、
  保護された仮想マシン上で文字通り MSDOS を実行します。 Windows 3.xx に
  対する Linuxのサポートは、より精巧で、 wine(
  <http://wine.dataparty.no/>)のようなフリーソフトでは、ほとんどのプログ
  ラムが UNIX の X 環境でかなり正しくかつ効率的に動きます。

  下記の 2 つのセクションでは、私が Linux でサポートした付加並列システム
  の例を見て行きます。

  5.2.  DSP してみましたか？

  高性能な DSP(Digital Signal Processing)プロセッサの市場は盛況です。普
  通これらのチップは特定のアプリケーションを対象とした組込みシステム用に
  設計してありますが、素晴らしい付加並列コンピュータでもあります。どうし
  てでしょうか？

  o  Texas Instruments( <http://www.ti.com/>)の TMS320 や Analog
     Devices( <http://www.analog.com/>)の SHARC DSP ファミリーのような多
     くのチップは、「グルー」ロジックが無いもしくはほんのわずかな数で並
     列マシンを構築できるように設計されている

     【訳註：グルー・ロジック(glue logic)とは、構成部品間のインタフェー
     スとなって両者間を仲立ちする論理回路やプロトコルを意味します】

  o  安価でありながら、1 つで 100 万命令、もしくは 100 万浮動小数点演算
     の性能がある。基本命令ロジックのコストを入れても、DSP プロセッサは
     1/10 のコストで PC 用のプロセッサに匹敵するパフォーマンスを出すこと
     が知られている

  o  電気をくわないかつ発熱も少ない。つまり、これらのチップをまとめ使っ
     てもこれまで使っている PC の電源で十分で、PC ケースの中に格納しても
     PC 内がオーブンの中のようにはならない

  o  DSP 命令セットのほとんどは、高水準言語用のコンパイラ(例えば、C 用)
     では解釈しにくく使いやすいとは言い難い。例えば、「ビット反転アド
     レッシング」がそれに当る。付加並列システムを使うと、大部分のホスト
     用コードを素直にコンパイルして実行し、数は少ないが最も時間を食うア
     ルゴリズムを慎重に人間がコードをチューンして DSP で実行する

  o  これらの DSP プロセッサは、実際は UNIX のような OS 向けに設計してあ
     るわけではないし、プロセッサとして単独に汎用的なコンピュータとは言
     い難い。例えば、メモリ管理のためのハードウェアを持たないものが多
     い。視点を変えると、DSP プロセッサはより汎用的なマシンに搭載されて
     こそ実力を発揮できる。Linux PC もその 1 つである

  オーディオカードやモデムの中には、Linux のドライバでアクセスできる DSP
  プロセッサを搭載しているものがいくつかありますが、4 つ以上の DSP プロ
  セッサ搭載する付加並列システムで効果が大きく見込まれます。

  Texas Instruments の TMS320 シリーズ (
  <http://dspvillage.ti.com/docs/dspproducthome.html>)は長い間に渡って人
  気があり、簡単に TMS320 ベースで並列システムを構築でき、かなり多くのシ
  ステムがすでに存在しています。 TMS320 には、整数演算専用のものと浮動小
  数点演算も可能なバージョンがあります。古い設計のものは、多少変わった単
  精度浮動小数点フォーマットを採用していますが、新しいモデルは IEEE
  フォーマットをサポートしています。旧 TMS320C4x(別名 'C4x)は、TI 独自の
  単精度浮動小数点フォーマットを使って、 80 MFLOPS を達成します。それに
  引き換え 'C67x は、単精度浮動小数点演算で 1 GFLOPS を達成し、IEEE の浮
  動小数点を使った倍精度計算では、420 MFLOPAS を達成します。これには
  VelociTI という VLIW ベースのチップ・アーキテクチャを使っています。こ
  れらのチップをマルチプロセッサとして構成するのは容易であるだけでなく、
  単独のチップである 'C8x マルチプロセッサは、 IEEE の浮動小数点で 100
  MFLOPS を実現します。このプロセッサは、2 もしくは 4 つの整数演算用の子
  DSP をともなう RISC プロセッサです。

  【訳註：VLIW(Very Long Instruction Word 超長命令)は、コンパイラによっ
  て依存関係のない複数の命令(整数演算や浮動小数点演算、メモリアクセス等)
  を 1 つの長い命令として、それを並列に実行する方法です。プロセッサの構
  造が単純になる反面、コンパイラが複雑になります。ただし VLIW な プロ
  セッサである Transmeta の Crusoe のように、コンパイラをいじることなく
  VLIW を効率よく動かそうとする方法も出てきています。詳しくはCrusoe
  Technology <http://www.transmeta.com/crusoe/technology.html> を参照し
  てください】

  この他の DSP プロセッサファミリーで、最近かなりの割合で付加並列システ
  ムに利用されてきているものに SHARC(別名、ADSP-2106x)という Analog
  Devices ( <http://www.analog.com/>)から出ているものがあります。これら
  のチップは、6 つのプロセッサで共有メモリを利用したマルチプロセッサで、
  グルー・ロジックを必要としません。これより大規模なシステムも構築でき、
  その場合は、6 つの 4 ビットのリンクとチップを利用します。大規模なシス
  テムの大部分は、軍事用のアプリケーションをターゲットにしているようで、
  少々割高になっています。しかし、Integrated Computing Engines, Inc.(
  <http://www.iced.com/>)は、GreenICE という小さな PCI カードを 2 枚セッ
  トにしたおもしろい製品を出しています。このユニットは、16 個の SHARC プ
  ロセッサをアレイ状に配置しており、IEEE フォーマットの単精度計算をピー
  ク性能で約 1.9 GFLOPS 出せます。 GreenICE は、5,000 ドル以下のコストで
  す。

  私見ですが、付加並列 DSP は、もっと Linux 並列処理のコミュニティから注
  目を浴びてもいいと思っています…。

  5.3.  FPGA と 再構成可能な論理計算回路

  並列処理がひたすら速さを求めるなら、カスタム・ハードウェアではなぜいけ
  ないのでしょうか？ そうですね、それに対する回答は、あまりにもコストが
  高いこと、開発するのに時間がかかり過ぎること、またと少しでもアルゴリズ
  ムが変更になると役に立たなくなってしまうこと等があげられます。しかし近
  年、電気的に再プログラミング可能な FPGA(Field Programmable Gate Array)
  の進歩によって、これらの欠点の大部分が無意味になってきています。現状で
  はゲート密度も高くなっていて、1 つの FPGA だけで単機能のプロセッサを十
  分に作成可能です。FPGA の再設定(再プログラミング)も敷居が低くなってき
  ており、アルゴリズムがある段階から次の段階へ移行するような場合でも、無
  理なく再設定できます。

  これは根気強い人向けの方法です。FPGA を設定するのには、VHDL のような
  ハードウェア記述言語を使わざるを得ません。それと同様に Linux ホストシ
  ステム上で、FPGA とのインタフェースのために低レベルなコードを書く必要
  があります。しかし FPGA のコストは低く、特に精度が低くてかまわない整数
  データの演算に適しています(実際、SWAR の小規模なサブセットとして具合が
  良い)。 FPGA は、複雑な演算をデータを入力するのと同じくらいの速度で実
  行できます。例えば、シンプルな FPGA ベースのシステムでもスーパーコン
  ピュータより高速に遺伝子データベースの検索を実現できています。

  FPGA ベースの専用ハードウェアを作成している企業はいろいろありますが、
  素晴らしいサンプルが、下記の 2 つの企業から出ています。

  Virtual Computer Company は Xilinx の SRAM ベースの FPGA を使って、動
  的に再設定可能な 製品をいろいろ出しています。8 ビットと 16 ビットの
  「Virtual ISA Proto Board」 ( <http://www.vcc.com/prod1.html>)
  は、2,000 ドル以下で購入できます。
  Altera の ARC-PCI(Altera Reconfigurable Computer, PCI bus) (
  <http://www.altera.com/cgi-bin/indexhtml.pl>) は、Virtual ISA Proto
  Board と同じタイプのカードですが、Altera の FPGA と ISA ではなく PCI
  バスを使っている点が異なります。

  設計ツールやハードウェア記述言語、コンパイラ、ルーター、マッパ等は、
  Windows や DOS 上のみで動作するオブジェクト・コードが付いてくる場合が
  ほとんどです。ホスト PC 上に DOS や Windows のディスク・パーティション
  を残しておいて、必要になったらリブートするという手も使えますが、Linux
  上で dosemu や wine のような Windows のエミュレータを使えば、これらの
  ソフトウェアの多くが動作します。

  【訳註：マッパ(mapper)は、回路を FPGA の論理ブロックに分割し、FPGA 上
  でその回路を実装する働きを持ちます】

  6.  一般的な興味として

  このセクションで扱う題材は、Linux において並列処理を行うための 4 つの
  モデルを扱います。

  6.1.  プログラミング言語とコンパイラ

  私はコンパイラ研究者として世間には名が通っていますので、Linux システム
  で自動的に効率的な並列コードを生成する素晴らしいコンパイラがいかに多い
  かということを話したいと思っていますが、現実はそうではありません。コー
  ドの作成者がわざわざ通信方法を指示したり、GCC でコンパイルした C の
  コードで書かれた他の並列処理方法を打ち負かすだけのパフォーマンスはなか
  なか実現できません。

  下記にあげる言語やコンパイラのプロジェクトは、高級言語から効率的なコー
  ドをそれなりに書けるようにする代表的なもののいくつかです。一般的に、そ
  れぞれがターゲットとするタイプのプログラミングに対しては効率的なのです
  が、何でもこなして GCC で C のプログラムをコンパイルする必要がまったく
  なくなるような言語やコンパイラは存在しません…あればいいのですが。これ
  らの言語やコンパイラを目的に応じてつかえば、開発が短期間で済み、デバッ
  グやメンテナンス等もより簡単になると思います。

  ここで(アルファベット順に)リストアップしたもの以外にも、言語やコンパイ
  ラはたくさんあります。フリーで使えるコンパイラ(Linux に関連ないものが
  ほとんどです)は、 <http://www.idiom.com/free-compilers/> に一覧があり
  ます。

  6.1.1.  Fortran 66/77/PCF/90/HPF/95

  少なくとも科学計算分野では Fortran が主流です。もちろん現在の Fortran
  は、 1996 年の ANSI 規格のものとは違います。Fortran 66 はかなり簡素で
  す。 Fortran 77 は機能をたくさん追加しており、その中で最も注目すべき機
  能として、キャラクタ・データのサポート、DO ループ構文の変更がありま
  す。 PCF(Parallel Computing Forum) Fortran は、77 に対して並列処理をサ
  ポートする機能をいろいろと追加しようとしました。Fortran 90 は今時の言
  語が備えている機能を完備していて、77 に対して C++ のようなオブジェクト
  指向プログラミング機能と配列の要素を並列に計算する機能が追加してありま
  す。 HPF(High-Performance Fortran、
  <http://www.crpc.rice.edu/HPFF/home.html>)は、2 つのバージョン(HPF-1
  と HPF-2)を経る過程で、基本的にこれを拡張したり、標準化を行ったりして
  いった結果、CM の Fortran や MasPar の Fortran そして Fortran D という
  広く使われているバージョンになりました。HPF はデータ配置関係に広く注目
  して様々な並列処理の強化をはかった Fortran 90 を拡張したものです。最終
  的には、Fortran 95 が Fortran 90 を多少強化、改良したものとして提供さ
  れています。
  C と連係していくには、f2c や g77(Linux に特化した概略が
  <http://linux.uni-regensburg.de/psi_linux/gcc/ html_g77/g77_91.html>
  にあります)もありますし、Fortran 90 と 95 の製品が
  <http://extweb.nag.co.uk/nagware/NCNJNKNM.html> 【訳註：リンク切れ】に
  あります。これらのコンパイラすべては結局、GCC をバックエンドに使って同
  じコード生成をしています。

  商用の Fortran で、SMP 用に並列化したコード生成に対応するものには、
  <http://www.kai.com/> や <http://www.psrv.com/vast_parallel.html> があ
  ります。これらのコンパイラが SMP Linux で動くかどうかははっきりしませ
  んが、 SMP Linux で動く POSIX 規格のスレッド(すなわち LinuxThreads)を
  生成できてもおかしくないと思います。

  Portland Group( <http://www.pgroup.com/>)は、商用で HPF Fortran (C や
  C++ も)を並列化するコンパイラがあり、SMP Linux 用のコードを生成しま
  す。別のバージョンとして、MPI や PVM を利用したクラスタをターゲットに
  した製品もあります。 < http://www.apri.com/> で出している FORGE や
  spf、xHPF は、SMP や クラスタで役に立ちそうです。

  下記にあげるものは、フリーに利用できる並列化 Fortran で、並列 Linux シ
  ステムで動作すると思われます。

  o  ADAPTOR(Automatic DAta Parallelism TranslaTOR、
     <http://www.gmd.de/SCAI/lab/adaptor/adaptor_home.html>) は、HPF を
     MPI や PVM の命令を使って、Fortran 77 もしくは 90 のコードに変換す
     る。しかし Linux については触れられていない

  o  カーネギ-・メロン大学の Fx( <http://www.cs.cmu.edu/~fx/>) は、ワー
     クステーションで組んだクラスタをターゲットにしているが、Linux につ
     いてはわからない

  o  HPFC(prototype HPF Compiler(
     <http://www.cri.ensmp.fr/~coelho/hpfc.html>))は、PVM を呼び出す形式
     で Fortran 77 のコードを生成する。これは Linux クラスタに使えるので
     はないか？

  o  Can PARADIGM(PARAllelizing compiler for DIstributed-memory General-
     purpose Multicomputers( <http://www.crhc.uiuc.edu/Paradigm/>))
     は、Linux で利用できるかわからない

  o  Polaris compiler(
     <http://ece.www.ecn.purdue.edu/~eigenman/polaris/>)は、共有メモリを
     使ったマルチプロセッサシステム用に Fortran のコードを生成する。恐ら
     くまもなく PAPERS の Linux クラスタが目をつけることになる

  o  PREPARE ( <http://www.irisa.fr/paris/pages-perso/Jean-Louis-
     Pazat/anglais/anterieures/Prepare.html>) は、MPI をターゲットにして
     いるが… IA32 プロセッサ用にコードを生成できるのかはっきりしない

  o  ADAPT と ADLIB、shpf(Subset High Performance Fortran compilation
     system、 <http://www.ccg.ecs.soton.ac.uk/Projects/shpf/shpf.html>)
     は、パブリックドメインで MPI を使った Fortran 90 を生成する。Linux
     で動く Fortran 90 のコンパイラを持っているなら…

  o  SUIF(Stanford University Intermediate Form(
     <http://suif.stanford.edu/>))は C と Fortran を並列化するコンパイラ
     である。これは National Compiler Infrastructure プロジェクトの中心
     であるので…並列 Linux にターゲットを当てている人がだれかいないだろ
     うか？

  きっと様々に存在する Fortran 系において、役に立つ可能性があるコンパイ
  ラの多くを省いてしまっていると思いますが、あまりに数が多くて追いかける
  のが困難です。その内に Linux で動くことがわかっているコンパイラだけを
  一覧にしたいと思っています。コメントや訂正がありました
  ら、pplinux@ecn.purdue.edu まで電子メールをください。
  6.1.2.  GLU (Granular Lucid)

  GLU(Granular Lucid)は、非常に高水準なプログラミングシステムで、データ
  フロー型(Lucid)と手続き型を兼ね備えたモデルです。PVM や TCP ソケットを
  サポートしています。Linux で動くのでしょうか？ さらに情報が欲しいなら
  <http://www.csl.sri.com/GLU.html>【訳註：リンク切れ】をどうぞ。

  6.1.3.  Jade と SAM

  Jade は並列プログラミング言語で、C を拡張して、粒度が粗い命令プログラ
  ムを並列に連続して実行します。分散共有メモリモデルを前提にしてい
  て、SAM という PVM を使ったワークステーションで組んだクラスタによって
  実現しています。さらに情報が欲しい場合は、
  <http://suif.stanford.edu/~scales/sam.html> にあります。

  6.1.4.  Mentat と Legion

  Mentat はオブジェクト指向の並列処理システムで、ワークステーションで組
  んだクラスタで動きます。Linux にも移植されています。 Mentat のプログラ
  ミング言語(MPL)は、オブジェクト指向プログラミング言語で、C++ をベース
  にしています。Mentat のランタイム・システムは、ノンブロッキングなリ
  モート・プロシージャ・コール(RPC)とそれとなく似たものを使っています。
  さらに詳しい情報は、 <http://www.cs.virginia.edu/~mentat/> にありま
  す。

  Legion( <http://www.cs.virginia.edu/~legion/>)は、Mentat 上に構築さ
  れ、WAN に接続しているマシン群が 1 台の仮想マシンとして振る舞います。

  6.1.5.  MPL(MasPar Programming Language)

  Mentat の MPL と混同しないようにしてください。もともとこの言語
  は、MasPar の SIMD スーパーコンピュータ用のネイティブな C 処理系のため
  に開発されたものです。ご存知の通り、MasPar は現在もうこの分野の業務を
  行っていません (現在は、NeoVista Solutions( <http://www.neovista.com>)
  という名で、データ・マイニング業務を行っています)。しかし MPL コンパイ
  ラは GCC を使って作成しているので、今でもフリーで利用できます。ハンツ
  ビルにあるアラバマ大学とパーデュ大学が共同で行った作業の中で、AFAPI(セ
  クション 3.6 を参照してください)を呼び出す C のコードを作成するの
  に、MasPar の MPL 見直されました。もちろん Linux の SMP、クラスタ両者
  とも動作します。しかしコンパイラには若干バグがあります…
  <http://www.math.luc.edu/~laufer/mspls/papers/cohen.ps> を見てくださ
  い。

  6.1.6.  PAMS(Parallel Application Management System)

  Myrias は PAMS(Parallel Application Management System)というソフトウェ
  ア製品を販売している企業です。PAMS は仮想共有メモリ並列処理に対して非
  常にシンプルな命令群を提供しています。ネットワーク上の Linux マシンは
  まだサポートしていません。詳しくは  <http://www.myrias.com/> を見てく
  ださい。

  6.1.7.  Parallaxis-III

  Parallaxis-III は、構造化プログラミング言語で、Modula-2 にデータの並列
  化(SIMD モデルの 1 種)を実現するため、「プロセッサと通信の仮想化」を加
  えました。Parallaxis のソフトウェアには、逐次、並列処理向けのコン
  ピュータシステム用のコンパイラとデバッガ(gdb と xgdb を拡張したもの)、
  画像処理をはじめとする、様々な分野から集めた膨大なサンプルアルゴリズム
  が付いています。 Linux システムでは逐次処理は動いています…古いバー
  ジョンでは、いろいろな並列化を対象にしていましたが、新しいバージョンも
  そうなる予定です(例えば、PVM クラスタをターゲットにするとか)。詳しい情
  報は、 <http://www.informatik.uni-stuttgart.de/ipvr/bv/p3/p3.html> を
  利用してください。

  6.1.8.  pC++/Sage++

  pC++/Sage++ は、C++ においてデータを並列化するスタイルの操作を「オブ
  ジェクトの収集」を使って、基本となる「基底」クラスのいくつかから利用で
  きるようにしたものです。プリプロセッサが PVM 配下で動作する C++ コード
  を生成します。Linux で動くのかどうかはわかりません。詳細は、
  <http://www.extreme.indiana.edu/sage/> を見てください。

  6.1.9.  SR (Synchronizing Resources)

  SR(Synchronizing Resources)は、コンカレント・プログラミング言語で、リ
  ソースが共有するプロセスや変数をカプセル化しています。操作として、プロ
  セス間の相互作用を扱う主なしくみを提供しています。SR は操作の実行とそ
  れを提供するメカニズムを新たに統合化した上で提供しています。その結果、
  次の機能をサポートします。それはローカルでの呼び出し、RPC、ランデ
  ブー、メッセージ通信、動的プロセス生成、マルチキャスト、セマフォです。

  【訳註：ランデブー(rendezvous)とは、異なるタスクの同期を取るための手法
  の 1 つで、Ada で使われています】

  Linux にも移植されましたが、どのような並列処理を行なえるのかはっきりし
  ません。詳細な情報は、 <http://www.cs.arizona.edu/sr/www/index.html>
  を見てください。

  6.1.10.  ZPL And IronMan

  ZPL は配列操作を基本としたプログラミング言語で、工学や科学分野のアプリ
  ケーションのサポート目的としています。IronMan という簡潔なメッセージ通
  信のインタフェースを呼び出すことで、このインタフェースを構成する機能の
  いくつかをほとんど他のメッセージ通信システムと同じような使い方で簡単に
  実現できます。しかしそもそもワークステーションで組んだクラスタ上での
  PVM や MPI をターゲットとしていて、Linux もサポートしています。詳細な
  情報は、
  <http://www.cs.washington.edu/research/projects/orca3/zpl/www/> を見て
  ください。

  6.2.  パフォーマンスの問題

  たくさんの方々がどのマザーボードやネットワークカード等がベストなのかを
  判断しようと、ベンチマークに入れ込んでいます。この解決方法では、ベンチ
  マークを計測できる状態になった時はすでにそれは利用できるベストなものと
  は言えなくなってしまっている、という問題があります。市場で見かけなく
  なっていたり、まったく違った特性を持つ改良製品に置き換わったりしている
  かもしれないのです。

  PC ハードウェアを購入するのはオレンジジュースを買うのと同じようなもの
  です。普通はラベルに企業名があろうがなかろうが、それなりにいいもので
  す。部品(もしくは濃縮オレンジ果汁)がどこのものかを知っていたり、気にし
  たりする人はほとんどいません。つまり、本当に注意を払わなければいけない
  のは、一部のハードウェアの違いなのです。アドバイスは簡単です。Linux で
  動かすハードウェアに何をやらせるのかを把握することです。把握できたら、
  できるだけ速やかに手に入れる方法や納得できる価格、相応な返品条件に注意
  を向けてください。

  PC プロセッサ間の違いを大枠で把握するのに格好の場所は、
  <http://www.pcguide.com/ref/cpu/fam/> です。ここは WWW サイト全体(
  <http://www.pcguide.com/>)が PC ハードウェアの技術を一望できる素晴らし
  いところでもあります。特定のハードウェアのパフォーマンスを上げるのにつ
  いて役に立つところもわずかですがあります。また、Linux Benchmarking
  HOWTO  <http://sunsite.unc.edu/LDP/HOWTO/Benchmarking-HOWTO.html> も手
  始めに読むのにいいと思います。

  Intel IA32 プロセッサには、事細かにシステムの動作性能を計測するのに使
  用する特別なレジスタがいくつもあります。Intel の VTune (
  <http://www.intel.co.jp/jp/developer/vtune/index.htm>)は、そのレジスタ
  を使って、完璧と言える程のコードチューニングをマシンにほどこします…が
  残念ながら Linux では使えません。Pentium のパフォーマンス用のレジスタ
  にアクセスするためのローダブル・モジュールのデバイス・ドライバやライブ
  ラリ・ルーチンは、 <http://www.cs.umd.edu/users/akinlar/driver.html>
  にあります。注意しなければいけないのは、IA32 プロセッサであっても、パ
  フォーマンス用のレジスタは、同じではないということです。このコードは
  Pentium でだけ動き、486 や Pentium、Pentium Pro、Pentium II、K6 等では
  動きません。

  【訳註：2001.3 現在、VTune は、Pentium、Pentium Pro、Pentium II、
  Pentium III、Pentium IV をサポートしています。MMX や SSE のチューニン
  グも可能です。日本での販売については、エクセルソフト
  <http://www.xlsoft.com/jp/products/vtune/perftool.htm> を参照してくだ
  さい】

  大規模なクラスタをわずかなスペースで構築しようとしている方に対しては、
  パフォーマンス上でさらに付け加えておきたいことがあります。少なくとも最
  近のプロセッサのいくつかは、温度センサーや回路が組み込まれていて、動作
  温度があまりに高くなると内部動作クロックを遅くします(熱の発生を押さえ
  て、信頼性を向上させる)。CPU を冷却するのにペルチェ・デバイス (ヒー
  ト・ポンプ)を購入するにはお勧めしませんが、高温での動作は部品の寿命を
  短くするだけでなく、システムとしてのパフォーマンスにじかに影響を与える
  ことに注意してください。コンピュータを設置する場合に、空気の流れをさえ
  ぎるような置き方や熱がこもってしまう場所には置かないようにしてくださ
  い。

  最後になりますが、パフォーマンスとはスピードだけではなく信頼性と可用性
  も含みます。高い信頼性があれば、たとえ部品がいかれても決してシステムは
  クラッシュしません。普通こうするには、冗長化電源やホットスワップ可能な
  マザーボードのように特別なしかけを必要とします。これは安いとは言えませ
  ん。高い可用性というのは、ほぼいついかなる時でも利用できるという考えで
  す。たとえ部品が壊れてシステムがクラッシュしても、システムはあっという
  間に修復されてリブートします。この件について基本的な要件の多くを論じて
  いる High-Availability HOWTO という資料があります。しかし特にクラスタ
  にとっては、高い可用性はわずかな予備だけで実現できなければいけません。
  私としては最低限 1 台の予備で、できればクラスタ当たり 16 台のマシンで
  予備 1 台というのがいいのでは、と思います。壊れたハードウェアを捨てて
  予備と交換すれば、保守契約に比べて可用性を高くしコストも低くできます。

  6.3.  結論―さあ、やりましょう

  Linux を使って並列処理は誰でもできますか？ もちろん！

  並列処理用スーパーコンピュータを扱う企業が消えて行ってしまったら並列処
  理も消えてしまうのか、ということをみんなで心配していたのは、そんな昔の
  ことではありません。私は消えてしまわなかったと思っています (
  <http://dynamo.ecn.purdue.edu/~hankd/Opinions/pardead.html> を見てくだ
  さい。本当に何が起こったのかをおもしろおかしくまとめてみました)。今や
  はっきりと並列処理は盛り返してきました。最近になって並列スーパーコン
  ピュータの製造を中止した Intel でさえ、MMX や次世代の IA64 EPIC
  (Explicitly Parallel Instruction Computer)で、自分たちの持っている並列
  処理を自慢しています。

  「Linux」と「parallel」をお気に入りのサーチ・エンジンで検索してみれ
  ば、かなりたくさんのところで Linux を使った並列処理を見つけられると思
  います。特に Linux PC クラスタはあちこちでやりはじめています。Linux が
  うってつけだったのは、PC のハードウェアが低いコストで高い性能を兼ね備
  えていて、それで Linux を使った並列処理が、小規模で予算の制約があるグ
  ループから大規模で資金潤沢な国立の研究機関まで広く使われることになった
  のです。

  このドキュメントのあちこちに記載してある様々なプロジェクトは、自分たち
  と似たような並列処理の構成をとっている「同種の」研究サイトの一覧を管理
  しています。しかし  <http://yara.ecn.purdue.edu/~pplinux/Sites/> で
  は、ハイパーテキスト形式で写真や解説、連絡先を記載したドキュメントを提
  供しています。Linux システムを並列処理に使っているすべてのサイトを対象
  としているのです。あなた方のサイトの情報をそこに掲載するには、

  o  「継続して運用できる」並列 Linux サイトを持っていること。SMP でもク
     ラスタを組んだマシンでも SWAR なシステムでも、はたまた付加プロセッ
     サがついているマシンでも、ユーザが Linux 配下で並列プログラムを実行
     できるのならかまわない。Linux ベースのソフトウェア環境(例えば PVM
     や MPI、AFAPI)のように、直接並列処理をサポートしている環境がシステ
     ムにインストールしてあること。しかしハードウェアが Linux で並列処理
     にかかりきりになっている必要はないし、並列プログラムが動いていない
     時には、まったく別の目的で使用されていてもかまわない

  o  あなたのサイトの掲載を申請すること。サイトの情報を
     pplinux@ecn.purdue.edu まで送ること。フォーマットにしたがって他の項
     目にも情報を記入すること。サイトを運営している方からのはっきりした
     要望がない限りは掲載されない

  現在 14 のクラスタが掲載してあります。しかし世界規模で見れば、少なくと
  も数十の Linux クラスタがあることがわかっています。もちろん掲載したか
  らといって、何の保証があるわけではありません。ただ Linux を使った並列
  処理に関連したことを共有し合って、研究、協力する機会が増えることを望ん
  でいるだけです。

  7.  日本語版謝辞

  翻訳に当たり下記の JF メンバーの方々にアドバイスを受けました。この場を
  借りてお礼申し上げます。

  o  武井さん

  o  早川さん

  o  山崎さん

