(57)【要約】多次元多面体を記述する連立不等式を作成し、多面体をある低次元空間に投影することによりシステムを解き、解をストリームプログラムにマッピングすることにより、ストリーム演算子処理を最適化することにより、多面体を１つのより少しの次元の間に投影することによってシステムを解いて、溶液を流れプログラムにマップすることによってストリーム演算子処理を最適化する方法。アフィンパーティショニングに基づく他のプログラム最適化方法も記載され、請求される。【選択図】図３
2009509267.tif 000002
【特許請求の範囲】
【請求項１】
一のオリジナルコンピュータプログラム内の一のストリーム演算子を識別することと、
前記ストリーム演算子のための、一の多次元多面体を記述する一の連立不等式を生成することと、
前記多次元多面体の一の次元より一次元小さい次元の一の空間に前記多次元多面体を投影することにより、前記連立不等式の一の解を得ることと、
前記オリジナルコンピュータプログラムに前記連立不等式の前記解をマッピングすることにより、一の修正されたコンピュータプログラムを生成することと、
を含む方法。
【請求項２】
前記修正されたコンピュータプログラムは、前記オリジナルコンピュータプログラムより小さい一のメモリフットプリントを有する、請求項１に記載の方法。
【請求項３】
前記修正されたコンピュータプログラムは、前記オリジナルコンピュータプログラムよりデータの依存性が少ない、請求項１に記載の方法。
【請求項４】
一の第１のコンピュータプログラム内の複数のネスト化ループを識別することと、
前記複数のネスト化ループの複数の誘導変数を一の独立誘導変数の複数の線形関数に変換することと、
前記独立誘導変数の一の新たなループ範囲内に前記複数のネスト化ループの一の関数内容を含む一の第２のコンピュータプログラムを出力することと、を含み、
前記複数のネスト化ループの前記関数内容は、前記複数の線形関数から導かれる一の連立不等式に従い、複数のパーティションに分割される、方法。
【請求項５】
前記新たなループの複数の繰返しは、並列に実行されるべきである、請求項４に記載の方法。
【請求項６】
前記複数のパーティションは、前記独立誘導変数と、前記複数の誘導変数の少なくとも１つとを含む複数の条件式の複数の帰結である、請求項４に記載の方法。
【請求項７】
複数の空のパーティションを取り除くよう前記第２のコンピュータプログラムを最適化することをさらに含む、請求項４に記載の方法。
【請求項８】
一の第１のコンピュータプログラム内の複数のネスト化反復構造を識別することと、
前記複数のネスト化反復構造を一のアフィン空間でモデル化することと、
前記アフィン空間内で前記モデルを分割することと、
前記第１のコンピュータプログラムの一の論理機能を維持する一の第２のコンピュータプログラム内で一の第２の複数のネスト化反復構造を発行することと、を含み、
前記第２の複数のネスト化反復構造の一の最外反復構造は、前記第２の複数のネスト化反復構造の残りの反復構造から独立している、方法。
【請求項９】
第１のコンピュータプログラムは、Ｂｒｏｏｋコンピュータ言語およびＳｔｒｅａｍＩｔコンピュータ言語のうちどちらかの一のプログラムである、請求項８に記載の方法。
【請求項１０】
前記第２のコンピュータプログラムは、Ｃコンピュータ言語およびＣ＋＋コンピュータ言語のうちのどちらかの一のプログラムである請求項８の方法。
【請求項１１】
前記第２のコンピュータプログラムは、一の中間表現の一のデータ構造である、請求項８に記載の方法。
【請求項１２】
一のデータ処理機械によって実行されると、該機械に動作を実行させる複数の命令を含む機械可読媒体であって、前記動作は、
一の第１のコンピュータプログラムを読み込むことと、
一のアレイ内のデータを処理すべく、前記第１のプログラム内で一の第１の複数のネスト化ループを識別することと、
前記第１の複数のネスト化ループを分析することと、
前記第１のコンピュータプログラムの一の関数を実行する一の第２のコンピュータプログラムを生成することと、を含み、
前記第２のコンピュータプログラムは、一のアレイ内のデータを処理する一の第２の複数のネスト化ループを含み、
前記第２の複数のネスト化ループは、前記第１の複数のネスト化ループより少なくとも１つ多いループを含み、
前記第２の複数のネスト化ループの一の外側ループの複数の繰返しは、互いに独立している、機械可読媒体。
【請求項１３】
前記第１の複数のネスト化ループにおける一のプログラムステートメントは、前記第２の複数のネスト化ループにおける一の条件ステートメント内に現れ、該条件ステートメントは、前記外側ループの一の誘導変数と一の内側ループの一の誘導変数とを比較する、請求項１２に記載の機械可読媒体。
【請求項１４】
前記第１のプログラムは、一の多次元配列内のデータを処理する、請求項１２に記載の機械可読媒体。
【請求項１５】
前記第１の複数のネスト化ループを分析することは、
一の第１のアレイアクセスを一の第１の線形方程式として表すことと、
一の第２のアレイアクセスを一の第２の線形方程式として表すことと、
前記第１および第２の線形方程式への一の同時解答を見つけることと、を含む、請求項１２に記載の機械可読媒体。
【請求項１６】
前記外側ループの複数の繰返しは、前記第１および第２の線形方程式への前記同時解答に対応する、請求項１５に記載の機械可読媒体。
【請求項１７】
システムであって、
複数のプロセッサと、
一のメモリと、
一のデータ記憶デバイスとを含み、
前記データ記憶デバイスは、前記複数のプロセッサに、
一の第１のコンピュータプログラムを前記メモリへロードさせることと、
一のアレイ内のデータを処理すべく、前記第１のコンピュータプログラムにおいて一の第１の複数のネスト化ループを識別することと、
前記第１のプログラムの一の関数を実行すべく一の第２のコンピュータプログラムを生成することと、
を実行させる複数の命令を含み、
前記第２のコンピュータプログラムは、一のアレイ内のデータを処理する一の第２の複数のネスト化ループを含み、前記第２の複数のネスト化ループは、前記第１の複数のネスト化ループより１つ多くループを含み、
前記第２の複数のネスト化ループ内の複数のプログラムステートメントは、一の外側ループの一の誘導変数を一の内側ループの一の誘導変数と関連付ける複数の条件式によって複数のパーティションに分割される、システム。
【請求項１８】
前記外側ループの複数の繰返しは、前記複数のプロセッサにより並列に実行される、請求項１７に記載のシステム。
【請求項１９】
前記複数のプロセッサは、単一の物理的プロセッサの複数の実行コアを含む、請求項１７に記載のシステム。
【請求項２０】
前記複数のプロセッサは、複数の物理的プロセッサを含み、それぞれの物理的プロセッサは、少なくとも１つの実行コアを含む、請求項１７に記載のシステム。
【発明の詳細な説明】
【技術分野】
【０００１】
本発明は、コンピュータプログラムを最適化するための技術に関する。より詳しくは、本発明は、コンピュータプログラムにおける並列性を見えるようにして利用するための技術に関する。
【背景技術】
【０００２】
中央処理装置（ＣＰＵ）を複数含むコンピュータシステムがより一般的になっている。しかし、一般にシステムのＣＰＵの数を増やすことによるパフォーマンスの向上は、ＣＰＵの数と直線的には比例しない。とはいうものの、成長中のアプリケーションであるストリーミングメディアアプリケーションなどは、多くの場合、多重ＣＰＵをより効率的に利用できる処理パターンを提示している。だが、そのストリーミングメディアアプリケーションのパフォーマンスもＣＰＵの数とは完全に直線的には比例せず、多重ＣＰＵの並列処理能力を利用すべくアプリケーションを設計するのは難しい仕事である。並列のアプリケーション設計を単純化して、並列のアプリケーションパフォーマンスを改良する作業は、新たなコンピュータ言語の設計および新しい最適化スキームの実装を含むいくつかの問題に直面している。
【０００３】
コンピュータプログラムは、通常、Ｃ、Ｃ＋＋またはＦｏｒｔｒａｎなどの高級言語で表される。プログラムは、コンパイラとして知られるプログラムにより分析され、特定のタイプのＣＰＵで実行されるべく一連の機械語命令へと変換される。コンパイラは、高水準プログラムにより記述される論理的プロセスを正しく実行する命令シーケンスを生成する役目をもっている。コンパイラは、多くの場合、メモリアクセス特徴を向上させるか、または、結果が決して使われない計算を除去すべく動作を再び順序付けることによって命令シーケンスのパフォーマンスを向上させる最適化機能を含む。コンパイラの中には、相互依存性を持たない論理プログラムパスを検出することができ、これらのパスを、多重ＣＰＵを有するマシンで並列に実行されるよう設定するものもある。ＢｒｏｏｋおよびＳｔｒｅａｍＩｔのようなコンピュータ言語は、具体的には、コンパイラが並列処理のチャンスを識別する手助けをするように設計されてきた。
【０００４】
現在のコンパイラによる最適化戦略は、場当たり的に前進したところがあり、一連の経験則主導変換式をプログラムの中間表現上の独立した「パス」の順序で実行している。例えば、「ループインターチェンジ」パスは、アレイにおけるデータを列順序優先というよりむしろ行順序優先で処理するようプログラムを変更し、その結果、ＣＰＵのキャッシュは、より効率よく稼動できるようになる。あるいは、「不要コード」パスが、決して実行されない命令を探して取り除いてもよい。これらのパスは、順序に依存し得る。１つのタイプの最適化は、他のタイプの最適化のチャンスを隠蔽するかまたは排除し得るので、最適化パスの順序を変えることにより、コンパイル済みプログラムのパフォーマンスを変えることができる。しかしながら、様々な最適化が多数あるので、どの順序が所定のプログラムの最適化を提供するかを見るために異なる最適化パス順序でプログラムをコンパイルすることは非現実的である。
【図面の簡単な説明】
【０００５】
本発明の実施形態が添付の図面を例に示されるが、本発明はこれに限定されない。また、図面を通じて同じ参照符号は、同じ構成要素を示す。本開示における「一の」または「１つの」実施形態への言及は、必ずしも同じ実施形態を示すものではなく、「少なくとも１つの」を意味する。
【０００６】
【図１】二次元データアレイ、および、二次元データアレイをコンピュータメモリにマッピングした特徴を示す。
【０００７】
【図２】２つの二次元配列に対し行うプログラムフラグメントのデータアクセスパターンを示す。
【０００８】
【図３】本発明の一実施形態に従うコンパイラによる最適化動作のフローチャートである。
【０００９】
【図４】本発明の一実施形態により最適化されるプログラムの動作をプログラムの動作を視覚化する他の方法を示す。
【００１０】
【図５】ストリーミングプログラムにおけるコンパイラによる最適化のフローチャートである。
【００１１】
【図６】本発明の一実施形態をホストし、一実施形態により生成される最適化プログラムを実行するコンピュータシステムを示す。
【発明を実施するための最良の形態】
【００１２】
本発明の実施形態は、参照の局所性を改善し、コンピュータプログラムにおける並行実行のチャンスを検出し、かつ、メモリフットプリントを減少させて、内部スレッド依存を増やすようプログラムを再編成できる。これらの有益な結果を達成する解析的モデルは、単純なおよび／または非効率的な動作（例えば、連続合計の計算）をしばしば含む可能性がある−なぜなら、読み出された後のデータに実行される動作は無関係なので−例を参照することにより説明される。本発明の実施形態はデータ上で任意の複雑な計算を実行するプログラムのメモリアクセスパターンおよび並列化を改善することができるが、複雑な計算を伴う実施形態は、記載されるべき特徴をあいまいにするだけであろう。
【００１３】
図１は、データ１１０の二次元配列を示し、それぞれの行１２０、１３０の内容が、行順序の多次元配列に配置したコンピュータ言語によってどのようにメインメモリ１４０のメモリーロケーションの一次元配列にマップされ得るかを示す。（いくつかの言語は多次元配列を列順序に格納するが、データ処理動作の分析は容易に適応される。
特に明記しない限り、以下では行優先記憶と仮定する。）
【００１４】
配列１１０におけるデータを処理するプログラムは、要素に対し行１５０を左から右、列１６０を上から下に、あるいはより複雑な対角線パターン１７０で調べるかまたは動作する。最新のＣＰＵは、通常隣接するマルチワードブロック（例えば１８０）の内部キャッシュにメモリからデータをロードするので（「キャッシュラインの充填として知られるプロセス）、ＣＰＵが新たなキャッシュラインをロードすることを要求する前に１つのキャッシュラインにロードされる全てのデータに働きかけることができる処理パターンは、未キャッシュの場所からデータを要求する前にキャッシュラインの１つだけのアイテムに働きかけるパターンよりかなり速く実行することができる。
【００１５】
このように、たとえば、アレイ１１０の行のデータを合計するプログラムは、およそｃ／ｌキャッシュラインフィル（ｃはアレイの列数であり、ｌはキャッシュラインのワード数である）により行を完成することができる。対照的に、アレイ１１０の列のデータを合計するプログラムは、一列を完成させるためにｒキャッシュラインフィル（ｒは、アレイの行数である）を必要とする。−プログラムは、ＣＰＵのキャッシュ能力からほとんど恩恵を受けないこということである。
さらに、第１の列の完了後、第２の列の処理を始めるために、ＣＰＵは、アレイ［０］［０］からアレイ［０］［ｌ−１］をキャッシュラインへと再びデータをロードしなければならない（アレイ内の行数は、利用可能なキャッシュライン数を上回るので、前にロードされたデータは外に出されていると仮定した場合）。
【００１６】
他の視点では、キャッシュ−ラインフィルにおいてロードされるデータを効率的に利用することにより、処理の間、データを保持することを要求されるキャッシュメモリ量を減らすことができる。キャッシュの利用は、コードシーケンスの「メモリフットプリント」とみなされることができる。キャッシュメモリは、不十分な資源であるので、メモリフットプリントを減らすことは、パフォーマンスにとって非常に有益である。
【００１７】
アレイの行を合計するための左から右への行ごとのアクセスパターン１５０は、ほとんど改善の余地がないが、アレイの列を合計するための上から下への列ごとのアクセスパターン１６０は、１の列のグループを同時に合計することにより改善できることが容易にわかる。後者は、従来技術のループ−インターチェンジ経験則により適切に実行されることができる最適化である。しかしながら、対角線１７０のようなより複雑なパターンについては、経験則はあまり成功していない。
【００１８】
図２は、本発明の実施形態の一側面を示す２アレイ最適化問題を導く。アレイＡ２１０およびＢ２２０の要素は、結合アレイ２３０に重畳して示される。２つのアレイは、擬似コードプログラムフラグメント２４０に従い演算される。ループ２４３および２４６がアレイ上で行ごと、列ごとに繰り返される一方で、ステートメントＳ１（２５０）、Ｓ２（２６０）は、アレイ要素に単純な計算を実行する（実際の計算は問題でなく、メモリアクセスパターンのみが重要）。矢２７０および２８０は、ステートメントＳ１およびＳ２が異なる行および列からどのようにアレイ要素にアクセスするかについて示す。
【００１９】
本発明の一実施形態は、図３のフローチャートに従い、コードフラグメント２４０を最適化することができる。まず、プログラム内の複数のネスト化ループが識別され（３１０）、分析される（３２０）。このようなネスト化ループは、プログラムが多次元配列のデータを処理するところで頻繁に起きる。この例では、ネスト化ループ２４３および２４６は、誘導変数ｉおよびｊによりアレイＡおよびＢの行および列で繰り返される。次に、複数のループの誘導変数は、独立誘導変数Ｐ（３２０）の線形関数に変換される。ステートメントＳ１およびＳ２に対して以下の一般形の線形関数が仮定される。
P = ai + bj + c  （ステートメントＳ１）
P = di + ej + f  （ステートメントＳ２）
【００２０】
Ｓ１およびＳ２がループの異なる繰返しの間、同一データにアクセスするので、それらは一緒に処理される。または、より正確には、以下の従属関係による。
【数１】
2009509267.tif 000003
ステートメントは、同じアフィン区画に配置される。
ai  + bj + c = P = di + e(j+1) + f
a(i+1) + bj + c = P = di +   ej  + f
【００２１】
これらの方程式を再編成すると、以下を得ることができる。
(a-d)i+(b-e)j=f+e-c
(a-d)i+(b-e)j=f-c-a
または、
a=d;b=e;f+e=c
a=d;b=e;f-c=a
【００２２】
一般性の損失なしに、ｃがゼロに等しく設定されることができ、a-fに対する以下の解が得られる。
(a、b、c、d、e、f)=(1、-1、0、1、-1、1)
Ｓ１およびＳ２への結果として生じるアフィン変換は、以下の通りである。
P=i-j     （ステートメントＳ１）
P=i-j+1  （ステートメントＳ２）
【００２３】
最後に、プログラムフラグメント２４０における複数のネスト化ループの関数内容は、以下に示すように書き直されることができる（３３０）。ここでは、ネスト化ループは、独立誘導変数の新たなループ内に配置され、ステートメントは、線形関数から導かれる連立不等式に従いパーティションに区分される。
【数２】
2009509267.tif 000004
【００２４】
新たな定式化は、オリジナルのフラグメントより非常に複雑に見えるにもかかわらず、従来の不要コードの除去および類似した最適化技術は、この一般形の解の多くの分岐をしばしば取り除く（空のパーティションを取り除く）ことができる（３４０）。さらに、外側ループおよび条件式が作成されているアフィンパーティショニング方法なので、外側ループのそれぞれの繰返し（および２つの内側ループの完全な実行）は、オリジナルフラグメントの２つのループの完全な実行より小さいメモリフットプリントを有する。外側ループの繰返しの間におけるデータの依存性はより少なく、それらの繰返しは、それらが並列に実行されることができるように独立している。このように、本方法は、オリジナルプログラムに内在する並列性を顕在化させる。本発明の一実施形態を実行するコンパイラは、多くのスレッドのそれぞれに、外側ループの１つを（並列に）繰り返すことを開始させるコードを発行することができる。結果として生じるプログラムは、その改良されたメモリアクセスパターン、および、システムのマルチプロセッサを利用するその能力のおかげで、非常に速く２つのアレイで同じ演算を実行できる。
【００２５】
それぞれのパーティションに対し実行される計算は条件式の帰結に配置され、その述語は、独立誘導変数とオリジナルの複数のループの誘導変数とを比較する不等式である。
【００２６】
図４は、アフィンパーティショニングによるプログラムの最適化についての他の考え方を示す。線形方程式の変換および解は、通常、アレイ４１０を介したデータアクセス４２０の並列パスを見つける。これらの並列パスは、アレイ（行４３０および列４４０）の２本の主軸のいずれにも整列されない。したがって、特定の領域４５０、４６０、４７０および４８０は、外側の独立ループの処理から除外されなければならない。連立不等式は、独立誘導変数のより大きい空間内のアレイポリゴン（この場合は単に長方形であり、より高次元だと多面体）の境界を記述する。
【００２７】
前述の説明では、単純な、二次元の例を中心に考えられている。しかしながら、本方法は、任意の多次元を理解可能なように図で示すのは難しいにも関わらず、そのような次元にも適用できる。ＢｒｏｏｋおよびＳｔｒｅａｍＩｔのようなコンピュータ言語は、可変多次元データストリームを扱うための簡単な抽象概念を提供する。ストリーミング演算子は、複数のネスト化ループを本質的に含むので、プログラムは、ストリーミングデータ全体に働きかけることができるのだが、Ｃ、および、Ｃ＋＋などの非ストリーミング言語では、言語のセマンティクスがいくつかのプログラミングコンストラクトを妨げ、確実な最適化を妨害するかあるいは、それらを安全でなくすこともあり得る。本発明の実施形態は、図５のフローチャートに従い、ストリーミングプログラムを最適化すべく有効に適用されることができる。
【００２８】
まず、ストリーム演算子は、オリジナルコンピュータプログラム（５１０）内で識別され、多次元多面体を記述すると考えられ得る連立不等式が演算子に対し作成される（５２０）。多面体は連立不等式の解を得るべくある低次元の空間に投影（５３０）され、最終的に、解はオリジナルプログラムに再びマッピングされることにより、プログラムの最適化バージョンが作成される（５４０）。前述のように、最適化されたプログラムは、おそらく、オリジナルより非常に複雑に見えるだろうが、実際、それはオリジナルプログラムよりメモリフットプリントが小さく、（そんなフットプリントが可能ならば）、データの依存性も少ない。
【００２９】
本発明の一実施形態を実行するコンパイラにより発行される最適化されたプログラムでは、ストリーム演算子の付随するネスト化反復構造は、独立誘導変数の最外ループ内に配置されるだろう。ループの関数内容は、独立誘導変数と内側ループの誘導変数とを比較する条件ステートメントによってパーティションに分割され、演算順序が正確でない場合、プログラムは、オリジナルプログラムの論理関数を維持する。
【００３０】
表１は、Ｂｒｏｏｋ演算子およびそれらの関連する不等式の一覧を示す。
【表１】
2009509267.tif 000005
演算子および他のコンピュータ言語のパラダイムに対しては類似した連立不等式が準備され得る。
【００３１】
本発明の実施形態を実行する最適化コンパイラは、大容量記憶装置のファイルからオリジナルのコンピュータプログラムを読み込むことができるか、またはパイプまたは他のプロセス間通信機能を介し前処理段階の出力を受信することができる。コンパイラの中には、プログラムソースファイルまたは他の入力から階層的なデータ構造を構築し、データ構造自体に働きかけることができるものもある。コンパイラは、最適化されたプログラムをファイルに書き込むか、パイプまたはプロセス間通信機構を介しプログラムを送信するか、または、例えば最適化を含むデータ構造など新規または修正された中間表現を作成することによって、出力することができる。出力は、第２のコンパイラによりコンパイルまたはアセンブルされるＣ、Ｃ＋＋またはアセンブラ言語のような他の言語の人間に解読可能なプログラムテキストであってもよい。あるいは、直接実行されることができるか、または、他のコンパイルされたモジュールまたはライブラリにリンクされることができるマシンコードであってもよい。
【００３２】
図６は、本発明の一実施形態を実行するコンパイラをサポートすることができるコンピュータシステムを示す。システムは、一つ以上のプロセッサ６１０、６２０、メモリ６３０、および、大容量記憶装置６４０を含む。プロセッサ６１０および６２０は、アドレス、データバス、および、キャッシュなど特定の他の内部構造体、および、関連した支持回路を共有する多数の実行コアを含むことができる。マルチコアＣＰＵは、論理的には、物理的に別々のＣＰＵと同じあると言ってよいが、コストまたは電力削減をもたらすことができる。この図に示されるシステムにホストされるコンパイラは、システム自体を対象として実行可能なファイル、あるいは、第２の異なるシステムに対する実行可能ファイルを作成することができる。多重ＣＰＵ（または単一の物理的ＣＰＵにおけるマルチコア）が利用可能な場合、実行可能ファイルでは、異なるＣＰＵ上で同時に外側ループの個別の繰返しを実行することにより多重ＣＰＵを利用することができる。コンパイラによって生成される最適化されたプログラムは、同じプログラムの非最適化バージョンより速く動作することができ、利用できるプロセッサおよびキャッシュ機能をもっとうまく利用することができる。システムのプロセッサが１つだけであっても、改良されたキャッシュ利用により、最適化プログラムを非最適化プログラムより速く実行させることができる。
【００３３】
本発明の一実施形態は、上述のようにプロセッサに演算を実行させる命令を格納した機械可読媒体であってよい。他の実施形態では、演算は、ハードワイヤードロジックを含む特定のハードウェアコンポーネントにより実行され得る。それらの演算は、あるいは、プログラムされたコンピュータ構成要素およびカスタムハードウェアコンポーネントのいかなる組合せにもよって実行され得る。
【００３４】
機械可読媒体は、これらに限定されないが、読み取り専用コンパクトディスク（ＣＤ−ＲＯＭ）、リード・オンリー・メモリ（ＲＯＭ）、ランダム・アクセス・メモリ（ＲＡＭ）、消去可能ＰＲＯＭ（ＥＰＲＯＭ）および、インターネット上の伝送を含む、機械（コンピュータなど）によって読み取られ得る形式で情報を格納するかあるいは伝送するいかなる機械をも含み得る。
【００３５】
これまで、主に特定の例を参照しておよび特定のハードウェアおよび／またはソフトウェアコンポーネントへの機能の特定の割り当てに関して本発明の適用を説明してきた。しかしながら、当業者であれば、並行実行のためのプログラムの最適化は、本願明細書とは異なる本発明の実施形態の機能をもたらすソフトウェアおよびハードウェアによっても実行できることが理解されよう。このような相違および実施態様は、添付の請求項に従い把握されることと理解する。
【図１】
2009509267.tif 000006
【図２】
2009509267.tif 000007
【図３】
2009509267.tif 000008
【図４】
2009509267.tif 000009
【図５】
2009509267.tif 000010
【図６】
2009509267.tif 000011
【国際調査報告】
2009509267.tif 000012
2009509267.tif 000013
2009509267.tif 000014
2009509267.tif 000015