(57)【要約】【課題】リスト構造ループの処理を高速化することを課題とする。【解決手段】本発明のコンパイラ装置は、原始プログラム１０８を記憶する補助記憶装置１０４と、補助記憶装置１０４に記憶された原始プログラム１０８を読み出してリスト構造ループを認識するリスト構造ループ解析部１０６と、各ＣＰＵに、リスト構造ループ内で各要素を辿った回数に基づいてリスト構造ループの制御文の処理を実行する要素を振り分けることにより、リスト構造ループの制御文の並列処理を行わせるリスト構造ループ処理部１１０を、目的プログラム１０９中に生成して、当該目的プログラム１０９を補助記憶装置１０４に記憶させるリスト構造ループ変形部１０７と、を備える。【選択図】図１
2009140451.tif 000002
【特許請求の範囲】
【請求項１】
同じ型の複数の要素のそれぞれが次の要素へのポインタをメンバーに持つリスト構造データにおいてその要素ごとに処理を行うループであるリスト構造ループを有する原始プログラムから、複数のＣＰＵを有する共有メモリ型並列計算機上で動作する共有メモリ向けの目的プログラムを生成するコンパイラ装置であって、
前記原始プログラムを記憶する記憶装置と、
前記記憶装置に記憶された原始プログラムを読み出して前記リスト構造ループを認識するリスト構造ループ解析部と、
各前記ＣＰＵに、前記リスト構造ループ内で各前記要素を辿った回数に基づいて前記リスト構造ループの制御文の処理を実行する要素を振り分けることにより、前記リスト構造ループの制御文の並列処理を行わせるリスト構造ループ処理機能を、前記目的プログラム中に生成して、当該目的プログラムを前記記憶装置に記憶させるリスト構造ループ変形部と、を備える
ことを特徴とするコンパイラ装置。
【請求項２】
前記リスト構造ループ変形部は、
各前記ＣＰＵに、前記リスト構造ループ内で各前記要素を辿った回数に基づいて前記リスト構造ループの制御文の処理を実行する要素を振り分けるとき、各前記要素に与えられた連続番号を複数の前記ＣＰＵの数で割った剰余に基づいて振り分ける
ことを特徴とする請求項１に記載のコンパイラ装置。
【請求項３】
前記リスト構造ループ変形部は、
前記目的プログラムが実行される場合に単一の前記ＣＰＵによる逐次処理よりも複数の前記ＣＰＵによる並列処理のほうが処理時間が短くなる前記要素の数の閾値を決定するための並列化閾値決定機能と、その閾値と実際の前記要素の数を比較するための要素数判定機能と、実際の前記要素の数が前記閾値を下回るときは前記リスト構造ループを前記逐次処理するリスト構造ループ逐次処理機能とを、前記目的プログラム中に生成する
ことを特徴とする請求項１に記載のコンパイラ装置。
【請求項４】
前記並列化閾値決定機能は、
複数の前記ＣＰＵによる並列処理に起因するオーバーヘッドを踏まえて前記閾値を決定する
ことを特徴とする請求項３に記載のコンパイラ装置。
【請求項５】
同じ型の複数の要素のそれぞれが次の要素へのポインタをメンバーに持つリスト構造データにおいてその要素ごとに処理を行うループであるリスト構造ループを有する原始プログラムから、複数のＣＰＵを有する共有メモリ型並列計算機上で動作する共有メモリ向けの目的プログラムを生成するコンパイラ装置によるコンパイル方法であって、
前記コンパイラ装置は、前記原始プログラムを記憶する記憶装置と、リスト構造ループ解析部と、リスト構造ループ変形部と、を備え、
前記リスト構造ループ解析部は、前記記憶装置に記憶された原始プログラムを読み出して前記リスト構造ループを認識し、
前記リスト構造ループ変形部は、各前記ＣＰＵに、前記リスト構造ループ内で各前記要素を辿った回数に基づいて前記リスト構造ループの制御文の処理を実行する要素を振り分けることにより、前記リスト構造ループの制御文の並列処理を行わせるリスト構造ループ処理機能を、前記目的プログラム中に生成して、当該目的プログラムを前記記憶装置に記憶させる
ことを特徴とするコンパイル方法。
【請求項６】
前記リスト構造ループ変形部は、
各前記ＣＰＵに、前記リスト構造ループ内で各前記要素を辿った回数に基づいて前記リスト構造ループの制御文の処理を実行する要素を振り分けるとき、各前記要素に与えられた連続番号を複数の前記ＣＰＵの数で割った剰余に基づいて振り分ける
ことを特徴とする請求項５に記載のコンパイル方法。
【請求項７】
前記リスト構造ループ変形部は、さらに、
前記目的プログラムが実行される場合に単一の前記ＣＰＵによる逐次処理よりも複数の前記ＣＰＵによる並列処理のほうが処理時間が短くなる前記要素の数の閾値を決定するための並列化閾値決定機能と、その閾値と実際の前記要素の数を比較するための要素数判定機能と、実際の前記要素の数が前記閾値を下回るときは前記リスト構造ループを前記逐次処理するリスト構造ループ逐次処理機能とを、前記目的プログラム中に生成する
ことを特徴とする請求項５に記載のコンパイル方法。
【請求項８】
前記並列化閾値決定機能は、
複数の前記ＣＰＵによる並列処理に起因するオーバーヘッドを踏まえて前記閾値を決定する
ことを特徴とする請求項７に記載のコンパイル方法。
【請求項９】
請求項５から請求項８のいずれか１項に記載のコンパイル方法をコンピュータに実行させることを特徴とするプログラム。
【発明の詳細な説明】
【技術分野】
【０００１】
本発明は、プログラム中のループ（ｆｏｒ文など）の処理を高速化するためのコンパイラに関し、特に、リスト構造（同じ型の複数の要素のそれぞれが次の要素へのポインタをメンバーに持つ構造）を持つデータ（以下、「リスト構造データ」と呼ぶ。）を扱うコンパイラによるループ（以下、「リスト構造ループ」と呼ぶ。）の処理の高速化技術に関する。
【背景技術】
【０００２】
例えば、Ｃ言語では、リスト構造データを利用することができる。そして、従来のコンパイラでは、リスト構造ループを含む原始プログラムをコンパイルした場合、リスト構造ループに対して並列化が困難であるという問題点があった。その理由は、リスト構造データのある要素（データ）を取得するためには、前の要素が持つ次の要素へのポインタの値が必要であり、そのためリスト構造ループの繰り返し処理の間の依存関係を解決できなかったからである。
【０００３】
このため、従来は、プログラマ自身が原始プログラムを慎重にプログラミングし直して最適化を可能にする必要があった。
例えば、前記した依存関係の問題を解決し、リスト構造データの繰り返し処理を並列化できる既存の技術として、特許文献１に開示されている「コンパイラおよびリスト構造データを処理するループの高速化方法」では、リスト構造データの各要素のアドレスを作業配列に格納し、リスト構造ループを、作業配列を参照するループに置き換えることで、ループの並列化を実現している。
【特許文献１】特開２００３−３３７７０７号公報
【発明の開示】
【発明が解決しようとする課題】
【０００４】
しかしながら、特許文献１の技術では、実行時にリスト構造データの要素数に依存した作業配列を確保する必要があるので、事前に作業配列の確保することに伴うメモリ消費量の問題と、リスト構造データの各要素のアドレスを作業配列に格納することに伴う処理オーバーヘッドの問題がある。
本発明は、前記問題に鑑みてなされたものであり、リスト構造ループの処理を高速化することを課題とする。
【課題を解決するための手段】
【０００５】
本発明は、同じ型の複数の要素のそれぞれが次の要素へのポインタをメンバーに持つリスト構造データにおいてその要素ごとに処理を行うループであるリスト構造ループを有する原始プログラムから、複数のＣＰＵを有する共有メモリ型並列計算機上で動作する共有メモリ向けの目的プログラムを生成するコンパイラ装置である。
コンパイラ装置は、前記原始プログラムを記憶する記憶装置と、前記記憶装置に記憶された原始プログラムを読み出して前記リスト構造ループを認識するリスト構造ループ解析部と、各前記ＣＰＵに、前記リスト構造ループ内で各前記要素を辿った回数に基づいて前記リスト構造ループの制御文の処理を実行する要素を振り分けることにより、前記リスト構造ループの制御文の並列処理を行わせるリスト構造ループ処理機能を、前記目的プログラム中に生成して、当該目的プログラムを前記記憶装置に記憶させるリスト構造ループ変形部と、を備える。
その他の手段については後記する。
【発明の効果】
【０００６】
本発明によれば、リスト構造ループの処理を高速化することができる。
【発明を実施するための最良の形態】
【０００７】
以下、本発明によるリスト構造ループの処理の高速化方法を実施するための最良の形態（以下、「実施形態」と呼ぶ。）を、図面を参照（直接言及する図以外の図も適宜参照）して詳細に説明する。なお、本実施形態では、まず図１〜図９を参照して上位概念的内容を説明し、その後に図１０Ａ〜図１５を参照してプログラムの具体例を踏まえた下位概念的内容を説明する。
また、本実施形態によるリスト構造ループの高速化方法が適用されたコンパイラは任意の計算機上に実装され、生成された目的プログラムは共有メモリ型並列計算機上で実行される。
【０００８】
図１は、本実施形態によるリスト構造ループの処理の高速化方法が適用されたコンパイラを含む計算機の構成を表す図の一例である。
計算機１０１は、メモリ等で実現される主記憶装置１０２、ＣＰＵ(Central Processing Unit)１０３、ＨＤＤ（Hard Disk Drive）等の装置で実現される補助記憶装置１０４（記憶装置）を有する。
【０００９】
ＣＰＵ１０３が補助記憶装置１０４から主記憶装置１０２上にコンパイラ１０５を読み込み、ＣＰＵ１０３がコンパイラ１０５を実行することで補助記憶装置１０４に保存された原始プログラム１０８を翻訳（コンパイル）し、共有メモリ型の並列計算機上で実行可能な目的プログラム１０９を生成する。原始プログラム１０８がリスト構造ループを含んでいる場合は、目的プログラム１０９中にリスト構造ループ処理部１１０（リスト構造ループ処理機能）が生成される。なお、以下において、動作主体のＣＰＵ１０３の記載を省略することがある。
【００１０】
コンパイラ１０５は、リスト構造ループ解析部１０６とリスト構造ループ変形部１０７とを含む。
リスト構造ループ解析部１０６は、入力として原始プログラム１０８を受け取る。リスト構造ループ解析部１０６は、入力された原始プログラム１０８からリスト構造ループを解析し、解析結果をリスト構造ループ変形部１０７に渡す。
リスト構造ループ変形部１０７は、リスト構造ループ解析部１０６の解析結果を受け取り、解析結果にリスト構造ループが含まれている場合、目的プログラム１０９中にリスト構造ループ処理部１１０を生成する。
【００１１】
図２は、本実施形態のコンパイラ１０５から生成された目的プログラム１０９を実行する時の共有メモリ型並列計算機２０１の構成を表す図の一例である。
共有メモリ型並列計算機２０１は、Ｎ個のＣＰＵ（ＣＰＵ（０）〜（Ｎ−１））２０２と、いずれのＣＰＵ２０２からもアクセス可能な共有メモリである主記憶装置２０３を有し、ＣＰＵ２０２が主記憶装置２０３上に目的プログラム１０９を読み込む。
なお、目的プログラム１０９は、リスト構造ループ変形部１０７により生成されたリスト構造ループ処理部１１０を含む。
【００１２】
リスト構造ループ処理部１１０は、並列化閾値決定部２０４（並列化閾値決定機能）、要素数判定部２０５（要素数判定機能）、リスト構造ループ並列処理部２０６（リスト構造ループ処理機能）、リスト構造ループ逐次処理部２０７（リスト構造ループ逐次処理機能）、ループ内処理長情報２０８、並列化オーバーヘッド情報２０９、実行時ＣＰＵ情報テーブル２１０、並列化閾値情報２１１から構成される。
実行時ＣＰＵ情報テーブル２１０は、ＣＰＵ数２１２とＣＰＵ番号２１３から構成される。
【００１３】
並列化閾値決定部２０４は、目的プログラム１０９の実行時までに得られる情報をもとに、使用するＣＰＵ２０２の並列化によって性能向上を期待できるループ長の閾値を、リスト構造データの最小の要素数という形で求め、その値を並列化閾値情報２１１に設定する。なお、並列化閾値を求めるための計算式や必要となる情報の種類は、共有メモリ型並列計算機２０１が並列化を行う際のハードウェア特性に依存する。本実施形態では、並列化閾値を求めるために特定の計算式を使用しているが、これをハードウェア特性に合わせて変更してもよい。また、本実施形態では、入力としてループ内処理長情報２０８と並列化オーバーヘッド情報２０９と実行時ＣＰＵ情報テーブル２１０とを使用するが、目的プログラム１０９の実行時までに得られる情報であれば、これ以外の情報を使用してもよい。
【００１４】
要素数判定部２０５は、リスト構造ループを実行する時の実際の要素数と、並列化閾値情報２１１に格納された要素数とを比較し、並列化による性能向上（効果）を期待できるどうかを判断する。
並列化の効果が期待できる場合、リスト構造ループ並列処理部２０６は、リスト構造ループを並列実行（並列処理）する。
並列化の効果が期待できない場合、リスト構造ループ逐次処理部２０７は、リスト構造ループを逐次実行（逐次処理）する。
【００１５】
ループ内処理長情報２０８には、共有メモリ型並列計算機２０１がリスト構造ループ内の処理を１回実行する時に必要な時間の長さが格納される。
並列化オーバーヘッド情報２０９には、共有メモリ型並列計算機２０１が並列実行を開始するために必要な処理時間の長さが格納される。
並列化閾値情報２１１には、並列化によって性能向上を期待できるループ長の閾値（要素数）が格納される。リスト構造ループを実行する時の実際の要素数が、並列化閾値情報２１１の値以上であれば、並列化による性能向上を期待できるとみなす。
【００１６】
ＣＰＵ数２１２は、並列実行時に使用可能なＣＰＵ２０２の数を示す。
ＣＰＵ番号２１３は、並列実行時にそれぞれのＣＰＵ２０２に割り当てられるＩＤ番号（連続番号）を示す。このテーブルの値は各ＣＰＵ２０２がそれぞれに持ち、値は「０」〜「ＣＰＵ数−１」の範囲であり、ＣＰＵ２０２間で重複しない。
【００１７】
図３は、リスト構造ループ処理部１１０内の参照や設定の関係を表す図の一例である。
ループ内処理長情報２０８の値は、原始プログラム１０８のコンパイル時にリスト構造ループ変形部１０７によって設定され、並列化閾値決定部２０４から参照される。
並列化オーバーヘッド情報２０９の値は、原始プログラム１０８のコンパイル時にリスト構造ループ変形部１０７によって設定され、並列化閾値決定部２０４から参照される。
【００１８】
実行時ＣＰＵ情報テーブル２１０の値は、目的プログラム１０９の実行時に共有メモリ型並列計算機２０１の並列プログラム実行部（不図示）によって設定され、内部のＣＰＵ数２１２が並列化閾値決定部２０４から参照され、また、内部のＣＰＵ数２１２およびＣＰＵ番号２１３がリスト構造ループ並列処理部２０６から参照される。
並列化閾値情報２１１の値は、並列化閾値決定部２０４によって設定され、要素数判定部２０５から参照される。
【００１９】
図４は、本実施形態のコンパイラ１０５の処理の流れを表す図の一例である。
処理４０２において、コンパイラ１０５は、リスト構造ループ解析部１０６を実行する。つまり、入力となる原始プログラム１０８を読み込み、リスト構造ループを解析して認識する。処理４０２の詳細は図５に記載する。
処理４０３において、コンパイラ１０５は、リスト構造ループ変形部１０７を実行する。つまり、リスト構造ループ解析部１０６の解析結果を受け取り、目的プログラム１０９中にリスト構造ループ処理部１１０を出力する。処理４０３の詳細は図６に記載する。
【００２０】
図５は、リスト構造ループ解析部１０６の処理の流れを表す図の一例である。
処理５０２において、リスト構造ループ解析部１０６は、原始プログラム１０８中のデータ宣言文を解析し、リスト構造データ型を認識する。具体的には、まず、処理５０２中の手順１にあるように、原始プログラム１０８中の全てのデータ宣言文を解析する。次に、処理５０２中の手順２にあるように、自分自身の型へのポインタを持つ型を見つけ出す。このようにして見つかったデータ型を、リスト構造データ型として認識する。
【００２１】
処理５０３において、リスト構造ループ解析部１０６は、原始プログラム１０８中のリスト構造データを利用したループ制御文を認識する。具体的には、まず、処理５０３中の手順１にあるように、原始プログラム１０８中の全てのループ制御文を解析する。次に、処理５０３中の手順２にあるように、「（１）ループの終了条件を、リスト構造データ型の変数の値で判定している」および「（２）ループの増分式で、リスト構造データ型を更新している」という条件を満たすループ制御文を見つけ出す。
【００２２】
処理５０４において、リスト構造ループ解析部１０６は、処理５０３で認識されたループ内の処理の依存関係を解析し、並列化可否を判断する。具体的には、処理５０４中の手順１にあるように、「ループ内の処理に、ループ間での依存関係が無い」および「ループ内で、リスト構造データの個数や並び順が変化しない」という条件を満たすとき、並列化可能と判断する。
リスト構造ループ解析部１０６においてこのようなループを解析できた場合、そのループが並列化できることをリスト構造ループ変形部１０７に通知する。
【００２３】
図６は、リスト構造ループ変形部１０７の処理の流れを表す図の一例である。
処理６０２において、リスト構造ループ変形部１０７は、並列処理のための各種情報を生成する。具体的には、まず、処理６０２中の手順１にあるように、ループ内処理長を計測し、ループ内処理長情報２０８を生成する。次に、処理６０２中の手順２にあるように、並列化オーバーヘッドを計測し、並列化オーバーヘッド情報２０９を生成する。最後に、処理６０２中の手順３にあるように、並列プログラム実行部が設定する実行時ＣＰＵ情報テーブル２１０のための領域を生成する。
【００２４】
処理６０３において、リスト構造ループ変形部１０７は、並列化閾値決定部２０４を生成する。具体的には、処理６０３中の手順１にあるように、並列化閾値決定部２０４を呼び出し、結果を並列化閾値情報２１１に格納する処理を生成する。
処理６０４において、リスト構造ループ変形部１０７は、要素数判定部２０５を生成する。具体的には、まず、処理６０４中の手順１にあるように、要素数判定部２０５の内部で使用する要素数カウンタを生成する。次に、処理６０４中の手順２にあるように、原始プログラム１０８中のループ制御文の部分を複製する。さらに、処理６０４中の手順３にあるように、手順２で生成されたループ制御文のループ内処理として、リスト構造データの要素数を並列化閾値情報２１１の値までカウントする処理を生成する。最後に、処理６０４中の手順４にあるように、並列化閾値情報２１１の値と要素数カウンタの値を比較して分岐する処理を挿入する。
【００２５】
処理６０５において、リスト構造ループ変形部１０７は、リスト構造ループ並列処理部２０６を生成する。具体的には、まず、処理６０５中の手順１にあるように、リスト構造ループ並列処理部２０６の内部で使用する要素番号カウンタを生成する。次に、処理６０５中の手順２にあるように、複数のＣＰＵ２０２でこれ以降の処理を実行するために、主記憶装置２０３上の目的プログラム１０９をＣＰＵ数２１２分だけ複製する処理を生成する。さらに、処理６０５中の手順３にあるように、原始プログラム１０８中のループ制御文の部分を複製する。さらに、処理６０５中の手順４にあるように、手順３で生成されたループ制御文のループ内処理として、要素数をカウントする処理と、要素番号カウンタの値をＣＰＵ数２１２で割った時の剰余がＣＰＵ番号２１３に等しいかどうかの条件判定文を生成する。最後に、処理６０５中の手順５にあるように、手順４で生成された条件判定を満たす時だけ、原始プログラム１０８中のループ内処理が実行されるよう、処理を複製する。
【００２６】
処理６０６において、リスト構造ループ変形部１０７は、リスト構造ループ逐次処理部２０７を生成する。具体的には、処理６０６中の手順１にあるように、原始プログラム１０８中のリスト構造ループの制御文とループ内処理を複製する。
【００２７】
図７は、リスト構造ループ処理部１１０の処理の流れを表す図の一例である。
処理７０２において、並列化閾値決定部２０４を実行する。具体的には、入力として与えられたループ内処理長情報２０８と並列化オーバーヘッド情報２０９および実行時ＣＰＵ情報テーブル２１０をもとに、並列化の効果が期待できる要素数を実行時に計算し、その値を並列化閾値情報２１１に出力する。なお、並列化閾値決定部２０４の内部実装は共有メモリ型並列計算機２０１のハードウェア特性に依存するものであるが、その内部実装の一例については図１２の破線部１２０３の領域（以下、「の領域」は省略）に示している（詳細は後記）。
【００２８】
処理７０３において、要素数判定部２０５を実行する。入力として与えられた並列化閾値情報２１１の値をもとに、実際に要素数のカウントを行う。処理７０３の詳細は図８に記載する。
処理７０４において、実際の要素数と並列化閾値情報２１１の値を比較する、つまり、要素数カウンタの値が並列化閾値以上であるかどうかを判断する。
【００２９】
処理７０４での比較の結果、要素数が並列化閾値以上であり（Ｙｅｓ）並列化による性能向上が期待できる場合は、処理７０５において、リスト構造ループ並列処理部２０６を実行する。処理７０５の詳細は図９に記載する。
処理７０４での比較の結果、要素数が並列化閾値未満であり（Ｎｏ）並列化による性能向上が期待できない場合は、処理７０６において、リスト構造ループ逐次処理部２０７を実行する。
【００３０】
図８は、処理７０３（図７）における要素数判定部２０５の処理の流れを表す図の一例である。
処理８０２において、要素数カウンタを「０」に初期化する。
処理８０３において、リスト構造ループの初期化式および条件式（判定式）を実行する。
処理８０４において、リスト構造ループの終了条件を満たすなら（Ｙｅｓ）、要素数判定部２０５を終了し、そうでなければ（Ｎｏ）、処理８０５に進む。
【００３１】
処理８０５において、要素数カウンタの値が並列化閾値に達したなら（Ｙｅｓ）、要素数判定部２０５を終了し、そうでなければ（Ｎｏ）、処理８０６に進む。
処理８０６において、要素数カウンタの値を「１」増やす。
処理８０７において、リスト構造ループの増分式および条件式を実行し、処理８０４に戻る。
【００３２】
なお、リスト構造データの終端に辿りつく前に、処理８０３以降のループ処理をＭ回実行したとすると、リスト構造データの要素は最低でもＭ個あることになる。また、処理８０３以降のループ処理の実行中に、要素数カウンタの値が並列化閾値情報２１１の値に到達した場合は、処理８０５によってループから抜け出し、要素数のカウントを終了する。要素数判定部２０５の処理は、リスト構造データが並列化による性能向上を期待できるだけの要素数を持っているかどうかを確認することが目的である。そのため並列化閾値情報２１１の値に到達したことが分かれば、要素数のカウントに必要な処理時間を削減する目的で、要素数のカウント処理を中断する。
【００３３】
図９は、処理７０５（図７）におけるリスト構造ループ並列処理部２０６の処理の流れを表す図の一例である。
処理９０２において、要素番号カウンタを「０」に初期化する。
処理９０３において、これ以降の処理を全ＣＰＵ２０２で同時実行するように並列化する。
処理９０４において、リスト構造ループの初期化式および条件式を実行する。
処理９０５において、リスト構造ループの終了条件を満たすなら（Ｙｅｓ）、リスト構造ループ並列処理部２０６を終了し、そうでなければ（Ｎｏ）、処理９０６に進む。
【００３４】
処理９０６において、要素番号カウンタの値をＣＰＵ数２１２で割った時の剰余がＣＰＵ番号２１３に等しいかどうかの条件判定を行う。
処理９０６の条件判定を満たすなら（Ｙｅｓ）、処理９０７においてループ内の処理を実行する。
処理９０６の条件判定を満たさないなら（Ｎｏ）、処理９０７を実行せず、処理９０８に進む。
処理９０８において要素番号カウンタの値を「１」増やす。
処理９０９において、リスト構造ループの増分式および条件式を実行し、処理９０５に戻る。
【００３５】
なお、同時実行に使用するＣＰＵ数がＮ個の時、ＣＰＵ数２１２はＮであり、それぞれのＣＰＵ２０２でＣＰＵ番号２１３は「０」から「Ｎ−１」の値を重複せずに持つ。このとき、要素番号カウンタがどのような値であっても、必ずいずれか一つのＣＰＵ２０２で結果が真になる。また、リスト構造データの要素数がＭの時、要素番号カウンタの値は「１」ずつ増えながら「０」から「Ｍ−１」の値をとるので、処理９０６における条件判定をＭ回繰り返す時、ＣＰＵ２０２ごとに結果が真になる回数は均一（差が「０」か「１」）になる。つまり、処理９０４以降の処理を全ＣＰＵ２０２で同時実行すれば、ループ内の処理９０７が実行される回数がＣＰＵ２０２ごとに均一になり、並列化と同等の結果が得られる。
【００３６】
図１０Ａは、コンパイラ１０５がＣ言語で作成された原始プログラム１０００（１０８）を入力にしてコンパイルする時の、リスト構造ループ解析部１０６の動作例（動作例１）を表す図（図５に対応）である。
まず、原始プログラム１０００の内容について説明し、その後、原始プログラム１０００とフローチャートとの対応について説明する。なお、原始プログラム１０００において、プログラムの実行処理と関係のないコメント（行番号１００８，１０１３）以外の箇所における「＊」はポインタを意味する。
【００３７】
行番号１００１では、ｍａｌｌｏｃ関数を使用するために必要なヘッダファイルを記載した。
行番号１００２では、ｃｈａｒ＊型（文字データの先頭アドレスを指し示す型）のｎａｍｅとｉｎｔ型（整数型）のｉｄとを持つＤＡＴＡ型（構造体）を定義した。
行番号１００３では、先に定義したＤＡＴＡ型のｄとＬＩＳＴ＊型（ＬＩＳＴ型の変数のメモリ上のアドレスを持つことができる型）のｌを持つＬＩＳＴ型（構造体）を定義した。
【００３８】
行番号１００４では、ＤＡＴＡ型のｉｎｉｔｉａｌ＿ｄａｔａという変数を定義した。この変数の初期値は、ｉｎｉｔｉａｌ＿ｄａｔａ．ｄ＝“ｎ／ａ”で、ｉｎｉｔｉａｌ＿ｄａｔａ．ｌ＝０である。これは大域変数で、並列実行時にスレッドごとに共有される。
行番号１００５では、ＬＩＳＴ＊型のｌｓｔａｒｔを定義した。これは大域変数で、並列実行時にスレッドごとに共有される。
【００３９】
行番号１００６では、ｉｎｔ型の値を返すｍａｉｎ関数（最初に実行される関数）を定義した。
行番号１００７では、ｉｎｔ型のｉという変数と、ＬＩＳＴ＊型のｌｐという変数を定義した。
行番号１００８では、コメントを記載した。
【００４０】
行番号１００９では、ＬＩＳＴ型のデータの領域を確保して、その領域のアドレスをｌｓｔａｒｔに代入した後、ｌｐに代入する処理を記載した。なお、ｓｉｚｅｏｆ（ｓｔｒｕｃｔ  ＬＩＳＴ）は、ＬＩＳＴ型の変数を作るのに必要なメモリのサイズを返すことを意味する。また、ｍａｌｌｏｃ関数は、そのメモリのサイズの領域を確保して、その領域を指す先頭アドレスを返す関数である。
【００４１】
行番号１０１０と１０１１では、いわゆるｆｏｒ文（行番号１０１０の()内の左から、初期化式：判定式：増分式）を記載した。ここでのこのｆｏｒ文は、ＬＩＳＴ型の変数を作るのに必要なサイズのメモリの領域を１０個確保し、それぞれの要素のポインタ部分に次の要素の先頭アドレスを格納する処理を示す。
行番号１０１２では、前記ｆｏｒ文によって生成したリスト構造データの最後の要素を指すポインタにＮＵＬＬを代入する処理を記載した。
行番号１０１３では、コメントを記載した。
【００４２】
行番号１０１４と１０１５では、ｆｏｒ文を記載した。このｆｏｒ文は、先に生成した合計１１個のＬＩＳＴ型のｄ（ＤＡＴＡ型）の部分に次々とｉｎｉｔｉａｌ＿ｄａｔａを代入する処理を示す。なお、「ｌｐ＝ｌｐ−＞ｌ」は、ｌｐの示すＬＩＳＴ型のデータのｌの先頭アドレスに対応するＬＩＳＴ型のデータに移動すること、つまり、次の要素へ移動することを示す。
行番号１０１６では、ｍａｉｎ関数が「０」を返す。通常は、正常終了を意味する。
行番号１０１７では、ｍａｉｎ関数の終了が示されている。
【００４３】
図１０Ｂは、図１０Ａに示す原始プログラムを実行した場合にメモリ上に生成されるデータ（変数）の概要図である。
メモリ上には、図１０Ｂに示すように、データ（１）〜（１１）の１１個のＬＩＳＴ型のデータが生成される。データ（１）は、先頭アドレスが「１０００」で、ＤＡＴＡ型のｄには「ｎ／ａ」と「０」が格納され、ＬＩＳＴ＊型のｌにはデータ（２）の先頭アドレスである「２０００」が格納される。同様に、データ（２）〜（１０）が生成される。データ（１１）は、ＬＩＳＴ＊型のｌ（ポインタ）に「ＮＵＬＬ」が格納される点で他のデータと異なっている。
【００４４】
このように生成されたリスト構造データであるデータ（１）〜（１１）は、データ（１）から順にポインタによって次のデータの先頭アドレスを特定することで連鎖的にデータを辿ることができ、データ（１１）のポインタが「ＮＵＬＬ」であることでそのデータが最後のデータであることがわかる。
【００４５】
これらを踏まえ、図１０Ａに戻って説明を続ける。
処理５０２において、リスト構造ループ解析部１０６は、原始プログラム１０００中のデータ宣言文を解析し、リスト構造データの型を認識する。具体的には、行番号１００３を解析し、原始プログラム１０００中で構造体であるＬＩＳＴが定義され、その内部にＬＩＳＴ型へのポインタ“ｌ”を持っていることから、この構造体はリスト構造データとして利用可能であると認識する。
【００４６】
処理５０３において、リスト構造ループ解析部１０６は、原始プログラム１０００中のリスト構造データを利用したループ制御文を認識する。具体的には、行番号１０１４を解析し、この行は、原始プログラム１０００のループの初期化式・判定式・増分式であり、ループの終了条件をＬＩＳＴ型の変数“ｌｐ”の値で判定していて、ループの増分式では“ｌｐ”が“ｌｐ−＞ｌ”で更新されていることから、このループがリスト構造ループであると認識する。
【００４７】
処理５０４において、リスト構造ループ解析部１０６は、処理５０３で認識されたループ内の処理の依存関係を解析し、並列化可否を判断する。具体的には、行番号１０１５を解析し、この行は、原始プログラム１０００のループ内の処理であると判断する。また、この例では、ループ内の処理に依存関係がなく（つまり、ＬＩＳＴ型の各データにおけるｄ（ＤＡＴＡ型）に関する処理が独立しており）、また、ループ内で“ｌｐ−＞ｌ”を更新せずリスト構造データの個数や並び順がループ内で変化しないことから、このリスト構造ループが並列化可能であると判断する。
リスト構造ループ解析部１０６においてこのようなループを解析できた場合、そのループが並列化できることをリスト構造ループ変形部１０７に通知する。
【００４８】
図１１は、コンパイラ１０５が図１０Ａ中の原始プログラム１０００をコンパイルして目的プログラム１１００を生成する時の、リスト構造ループ変形部１０７の動作例（動作例２）を表す図（図６に対応）である。
処理６０２において、リスト構造ループ変形部１０７は、破線部１１０２および破線部１１０４のように、並列処理のための各種情報を生成する。具体的には、まず、破線部１１０２に示すように、リスト構造ループ処理部１１０を構成する関数を持った“ｐａｒａｌｌｅｌｉｚｅ．ｈ”を目的プログラムに取り込む。
【００４９】
次に、破線部１１０４に示すように、行番号１０１４および１０１５（図１０Ａ参照）のループ内処理長を計測し、計測結果が「５０」であった場合、ループ内処理長情報２０８を“ｉｎｔｅｒａｃｔｉｏｎ＿ｌｅｎｇｔｈ”という名称で生成し「５０」を設定する。
さらに、並列化オーバーヘッドを計測し、計測結果が「１００」であった場合、並列化オーバーヘッド情報２０９を“ｐａｒａｌｌｅｌｉｚｅ＿ｏｖｅｒｈｅａｄ”という名称で生成し「１００」を設定する。さらに、並列化閾値情報２１１を“ｔｈｒｅｓｈｏｌｄ”という名称で生成し、要素数判定部２０５の内部で使用する要素数カウンタを“ｅｌｅｍｎｕｍ”という名称で生成する。
最後に、実行時ＣＰＵ情報テーブル２１０内のＣＰＵ数２１２のための領域を“ｃｐｕｎｕｍ”という名称で生成し、共有メモリ型並列計算機２０１から並列実行に最適なＣＰＵ数を取得するための“ｇｅｔ＿ｃｐｕｎｕｍ”関数を呼び出す処理を実行するための処理を生成する。なお、“ｇｅｔ＿ｃｐｕｎｕｍ”関数は、“ｐａｒａｌｌｅｌｉｚｅ．ｈ”から提供される。“ｇｅｔ＿ｃｐｕｎｕｍ”関数の実装例は図１２に示す。
【００５０】
処理６０３において、リスト構造ループ変形部１０７は、破線部１１０５のように並列化閾値決定部２０４を生成する。具体的には、“ｇｅｔ＿ｔｈｒｅｓｈｏｌｄ”という名称で定義された並列化閾値決定部２０４を呼び出し、その結果を“ｔｈｒｅｓｈｏｌｄ”に格納する処理を生成する。なお、“ｇｅｔ＿ｔｈｒｅｓｈｏｌｄ”関数は、“ｐａｒａｌｌｅｌｉｚｅ．ｈ”から提供される。“ｇｅｔ＿ｔｈｒｅｓｈｏｌｄ”関数の実装例は図１２に示す。
【００５１】
処理６０４において、リスト構造ループ変形部１０７は、破線部１１０６のように要素数判定部２０５を生成する。具体的には、まず、原始プログラム１０００中の行番号１０１４（図１０Ａ参照）に示されるループ制御文の部分を複製する。さらに、このループ制御文のループ内処理として、“ｅｌｅｍｎｕｍ”を用いてリスト構造データの要素数を並列化閾値情報２１１の値までカウントする処理を生成する。最後に、並列化閾値情報２１１の値と要素数カウンタの値を比較して分岐する処理を挿入する。
【００５２】
処理６０５において、リスト構造ループ変形部１０７は、破線部１１０３のようにリスト構造ループ並列処理部２０６を生成する。具体的には、複数のＣＰＵ２０２で並列に実行されるリスト構造ループの処理を“ｐａｒａｌｌｅｌｉｚｅｄ＿ｌｉｓｔｌｏｏｐ”関数という関数として生成する。次に、ＣＰＵ２０２ごとに持つＣＰＵ番号２１３を“ｃｐｕｉｄ”という名称の変数として生成し、それを“ｐａｒａｌｌｅｌｉｚｅｄ＿ｌｉｓｔｌｏｏｐ”の引数で初期化する。さらに、その関数内にリスト構造ループ並列処理部２０６の内部で使用する要素番号カウンタを“ｅｌｅｍｉｄ”という名称で生成する。
【００５３】
さらに、原始プログラム１０００中の行番号１０１４（図１０Ａ参照）に示されるループ制御文の部分を複製する。さらに、生成されたループ制御文のループ内処理として、要素数をカウントする処理と、要素番号カウンタの値をＣＰＵ数２１２で割った時の剰余がＣＰＵ番号２１３に等しいかどうかの条件判定文を生成する。最後に、この条件判定を満たす時だけ、原始プログラム１０００中の行番号１０１５（図１０Ａ参照）に示されるループ内処理が実行されるよう、処理を複製する。
【００５４】
また、破線部１１０７のように、関数“ｐａｒａｌｌｅｌｉｚｅｄ＿ｌｉｓｔｌｏｏｐ”を複数のＣＰＵ２０２で並列に実行するための“ｐａｒａｌｌｅｌｉｚｅ”関数を呼び出す処理を挿入する。“ｐａｒａｌｌｅｌｉｚｅ”関数は、“ｐａｒａｌｌｅｌｉｚｅ．ｈ”から提供される。なお、“ｐａｒａｌｌｅｌｉｚｅ”関数の実装例は図１２に示す。
【００５５】
処理６０６において、リスト構造ループ変形部１０７は、破線部１１０８のようにリスト構造ループ逐次処理部２０７を生成する。具体的には、原始プログラム１０８中の行番号１０１４および１０１５（図１０Ａ参照）に示されるリスト構造ループの制御文とループ内処理を複製する。
【００５６】
図１２は、リスト構造ループ処理部１１０を構成する関数を持つ“ｐａｒａｌｌｅｌｉｚｅ．ｈ”の実装例を示す図である。
破線部１２０２に示すように、ＣＰＵ数２１２を表す大域変数“ｃｐｕｎｕｍ”はこのファイルの中で定義する。
【００５７】
破線部１２０４に示すように、“ｇｅｔ＿ｃｐｕｎｕｍ”関数は、オペレーティングシステムから提供される“ｓｙｓｃｏｎｆ”関数を実行して利用可能なＣＰＵ数を返す。
破線部１２０５に示すように、“ｇｅｔ＿ｔｈｒｅｓｈｏｌｄ”関数は、ループ内処理長情報２０８、並列化オーバーヘッド情報２０９、ＣＰＵ数２１２の値から、並列化閾値情報２１１に格納する値を算出して返す。本関数の具体的な動作例は図１３で後記する。
【００５８】
破線部１２０３に示すように、“ｐａｒａｌｌｅｌｉｚｅ”関数は、オペレーティングシステムから提供される“ｐｔｈｒｅａｄ＿ｃｒｅａｔｅ”関数を実行して主記憶装置２０３上の目的プログラム１０９を複製する。この関数は、複製した目的プログラム１０９に実行させる関数と、関数に渡す引数を１つ指定できる。
実行する関数は、この関数の呼び出し側が決定できる。本例では呼び出し側が“ｐａｒａｌｌｅｌｉｚｅｄ＿ｌｉｓｔｌｏｏｐ”関数を指定している。“ｐａｒａｌｌｅｌｉｚｅｄ＿ｌｉｓｔｌｏｏｐ”関数に渡す引数は、「０」から「ｃｐｕｎｕｍ−１」の範囲をとり重複しない値であり、その値が各ＣＰＵ２０２でＣＰＵ番号２１３として利用される。複製の終了後は、“ｐｔｈｒｅａｄ＿ｊｏｉｎ”関数を実行して複製した目的プログラム１０９が全て終了したことを確認してから、“ｐａｒａｌｌｅｌｉｚｅ”関数を終了する。
【００５９】
図１３は、並列化閾値決定部２０４の動作例（動作例３）を表す図である。
目的プログラム１３０１中に破線部１３０２（図１１の破線部１１０５と同様）に示す要素数判定部２０５が生成された場合を例に説明する。
破線部１３０２に示す並列化閾値決定部（２０４）“ｇｅｔ＿ｔｈｒｅｓｈｏｌｄ”の内部実装例を符号１３０６（図１２の破線部１２０５と対応）に示す。本例では、ｐがループ内処理長、ｏが並列化オーバーヘッド、ｎがＣＰＵ数、ｔｉがＣＰＵ２０２がｉ個の時の実行時間、ｌがループ長であるとき、共有メモリ型並列計算機２０１上における逐次実行時の実行時間ｔ１＝ｐ＊ｌ、並列実行時の実行時間ｔｎ＝ｏ＋（ｐ＊ｌ）／ｎになるものとし、ｐ＊ｌ＞＝ｏ＋（ｐ＊ｌ）／ｎの時に並列化の効果を期待できるとする。よって、この不等式を方程式としてｌについて解くと、並列化閾値となるループ長はｌ＝（ｏ＊ｎ）／（ｐ＊（ｎ−１））（端数切り上げ）となる。なお、仮にｎ＝１である時はＣＰＵ数が「１」であり並列実行ができないので、この関数はそれを示すために「−１」を返す。また、ｃｅｉｌ関数は、浮動小数点（ここではｄｏｕｂｌｅ型）の値を切り上げる関数である。
【００６０】
符号１３０６で示す“ｇｅｔ＿ｔｈｒｅｓｈｏｌｄ”の入力となるループ内処理長情報１３０３の値が「５０」であり、ＣＰＵ数１３０４の値が「３」であり、並列化オーバーヘッド情報１３０５の値が「１００」である場合、並列化閾値決定部２０４は、上記の計算式を用いてｌを求め、並列化閾値情報１３０７に「３」を設定する。
【００６１】
図１４は、要素数判定部２０５の動作例（動作例４）を表す図である。
この例では、目的プログラム１４０１中に破線部１４０２（図１１の破線部１１０６と同様）に示す要素数判定部２０５が生成され、符号１４０４に示す“ｔｈｒｅｓｈｏｌｄ”（並列化閾値情報２１１）の値が「３」であり、リスト構造データの要素数が「３」以上ある場合について説明する。
【００６２】
破線部１４０２に示す要素数判定部２０５は、リスト構造ループ変形部１０７の処理６０４で生成されたループを実行することで、符号１４０３に示すリスト構造データの要素をひとつずつ辿りながら、“ｅｌｅｍｎｕｍ”（要素数カウンタ）を「１」ずつ増やしていく。
上記の処理を３回実行し、“ｅｌｅｍｎｕｍ”（要素数カウンタ）の値が「３」になった時、“ｔｈｒｅｓｈｏｌｄ”（並列化閾値情報２１１）の値が「３」であることから“ｉｆ（ｅｌｅｍｎｕｍ＞＝ｔｈｒｅｓｈｏｌｄ）”の条件を満たしているので、ｂｒｅａｋ文を実行して破線部１４０２に示す要素数判定部２０５のループ処理から抜け出す。
【００６３】
破線部１４０２に示す要素数判定部２０５の処理の後、改めて“ｉｆ（（ｅｌｅｍｎｕｍ＞＝ｔｈｒｅｓｈｏｌｄ）＆＆（ｔｈｒｅｓｈｏｌｄ！＝−１））”の条件判定を行い（図１１の破線部１１０９）、この条件を満たすなら並列化の効果が期待できるとみなし、リスト構造ループ並列処理部２０６（図１１の破線部１１０７）を実行する。
【００６４】
図１５は、リスト構造ループ並列処理部２０６の動作例（動作例５）を表す図である。
目的プログラム１５０１中に、破線部１５０２（図１１の破線部１１０３と同様）に示すリスト構造ループ並列処理部２０６が生成され、リスト構造データ１５０６の要素数が「７」であり、“ｃｐｕｎｕｍ”（ＣＰＵ数２１２）が「３」であり、それぞれのＣＰＵ２０２の“ｃｐｕｉｄ”（ＣＰＵ番号２１３）に「０」、「１」、「２」が設定された場合を例に説明する。
【００６５】
まず、破線部１５０９に示すように、複数のＣＰＵ２０２で破線部１５０２に示す“ｐａｒａｌｌｅｌｉｚｅｄ＿ｌｉｓｔｌｏｏｐ”を実行するために、“ｐａｒａｌｌｅｌｉｚｅ”関数を呼び出す。次に、“ｐａｒａｌｌｅｌｉｚｅ”関数の中で、複数のＣＰＵ２０２を用いて“ｐａｒａｌｌｅｌｉｚｅｄ＿ｌｉｓｔｌｏｏｐ”関数を実行する。
【００６６】
この処理を実行した時、例えば、“ｃｐｕｉｄ”（ＣＰＵ番号２１３）が「１」であるＣＰＵ２０２で破線部１５０３に示すループ制御文を１回目に実行した時には、“ｅｌｅｍｉｄ”が「０」であり、“ｉｆ（ｅｌｅｍｉｄ＋＋％ｃｐｕｎｕｍ＝＝ｃｐｕｉｄ）”の条件を満たさないので、処理１５０４を実行せずにループ制御文に戻る。そしてループ制御文を２回目に実行した時には、“ｅｌｅｍｉｄ”が「１」であり、“ｉｆ（ｅｌｅｍｉｄ＋＋％ｃｐｕｎｕｍ＝＝ｃｐｕｉｄ）”の条件を満たすので、処理１５０４を実行してからループ制御文に戻る。この処理をリスト構造データ１５０６の終端１５０８が見つかるまで繰り返すと、破線部１５０７に示すように、“ｃｐｕｉｄ”（ＣＰＵ番号２１３）が「１」であるＣＰＵ２０２ではリスト構造データの要素のうち２個目と５個目の要素のみに対してｄ（ＤＡＴＡ型）に関する処理を行い、他の要素のｄ（ＤＡＴＡ型）に関する処理は他のＣＰＵ２０２が行うことになる。
【００６７】
この結果、各ＣＰＵ２０２がリスト構造データ１５０６の全要素を均等に選択して処理を行い、並列化と同等の効果が得られる。つまり、各ＣＰＵ２０２は、各要素のポインタを参照することでリスト構造データ１５０６の全要素を辿るが、ｄ（ＤＡＴＡ型）に関する処理は自身の担当分しか行わないので、作業配列等を使うことなくリスト構造ループの処理を並列化して高速化することができる。
【００６８】
以上で実施形態の説明を終えるが、本発明の態様はこれらに限定されるものではない。
例えば、ｄ（ＤＡＴＡ型）に関する処理は、リスト構造データの全ての要素に対して同じ内容ではなく、独立した処理であれば、異なった処理であってもよい。
また、原始プログラムの言語は、Ｃ言語でなくても、Ｃ＋＋やＪａｖａ（登録商標）など、他の言語であってもよい。
その他、ハードウェア、プログラム、表示画面などの具体的な構成について、本発明の主旨を逸脱しない範囲で適宜変更が可能である。
【図面の簡単な説明】
【００６９】
【図１】本実施形態によるリスト構造ループの処理の高速化方法が適用されたコンパイラを含む計算機の構成を表す図の一例である。
【図２】本実施形態のコンパイラ１０５から生成された目的プログラム１０９を実行する時の共有メモリ型並列計算機２０１の構成を表す図の一例である。
【図３】リスト構造ループ処理部１１０内の参照や設定の関係を表す図の一例である。
【図４】本実施形態のコンパイラ１０５の処理の流れを表す図の一例である。
【図５】リスト構造ループ解析部１０６の処理の流れを表す図の一例である。
【図６】リスト構造ループ変形部１０７の処理の流れを表す図の一例である。
【図７】リスト構造ループ処理部１１０の処理の流れを表す図の一例である。
【図８】処理７０３における要素数判定部２０５の処理の流れを表す図の一例である。
【図９】処理７０５におけるリスト構造ループ並列処理部２０６の処理の流れを表す図の一例である。
【図１０Ａ】コンパイラ１０５が原始プログラム１０００（１０８）を入力にしてコンパイルする時の、リスト構造ループ解析部１０６の動作例（動作例１）を表す図である。
【図１０Ｂ】図１０Ａに示す原始プログラムを実行した場合にメモリ上に生成されるデータの概要図である。
【図１１】コンパイラ１０５が図１０Ａ中の原始プログラム１０００をコンパイルして目的プログラム１１００を生成する時の、リスト構造ループ変形部１０７の動作例（動作例２）を表す図である。
【図１２】リスト構造ループ処理部１１０を構成する関数を持つ“ｐａｒａｌｌｅｌｉｚｅ．ｈ”の実装例を示す図である。
【図１３】並列化閾値決定部２０４の動作例（動作例３）を表す図である。
【図１４】要素数判定部２０５の動作例（動作例４）を表す図である。
【図１５】リスト構造ループ並列処理部２０６の動作例（動作例５）を表す図である。
【符号の説明】
【００７０】
１０１  計算機
１０２  主記憶装置
１０３  ＣＰＵ
１０４  補助記憶装置（記憶装置）
１０５  コンパイラ
１０６  リスト構造ループ解析部
１０７  リスト構造ループ変形部
１０８  原始プログラム
１０９  目的プログラム
１１０  リスト構造ループ処理部（リスト構造ループ処理機能）
２０１  共有メモリ型並列計算機
２０２  ＣＰＵ
２０３  主記憶装置
２０４  並列化閾値決定部（並列化閾値決定機能）
２０５  要素数判定部（要素数判定機能）
２０６  リスト構造ループ並列処理部（リスト構造ループ処理機能）
２０７  リスト構造ループ逐次処理部（リスト構造ループ逐次処理機能）
２０８  ループ内処理長情報
２０９  並列化オーバーヘッド情報
２１０  実行時ＣＰＵ情報テーブル
２１１  並列化閾値情報
２１２  ＣＰＵ数
２１３  ＣＰＵ番号
【図１】
2009140451.tif 000003
【図２】
2009140451.tif 000004
【図３】
2009140451.tif 000005
【図４】
2009140451.tif 000006
【図５】
2009140451.tif 000007
【図６】
2009140451.tif 000008
【図７】
2009140451.tif 000009
【図８】
2009140451.tif 000010
【図９】
2009140451.tif 000011
【図１０Ａ】
2009140451.tif 000012
【図１０Ｂ】
2009140451.tif 000013
【図１１】
2009140451.tif 000014
【図１２】
2009140451.tif 000015
【図１３】
2009140451.tif 000016
【図１４】
2009140451.tif 000017
【図１５】
2009140451.tif 000018