(57)【特許請求の範囲】
【請求項１】
相互に接続された複数のプロセシングコアと、
ストリームドメインコードを記憶するメモリであって、当該ストリームドメインコードが、ストリーム、当該ストリームに対するデータの型、当該ストリームに対するストリームソースモジュール、及び、ストリームデスティネーションモジュールを定義する、メモリと、
前記メモリから前記ストリームドメインコードを受け取り、前記ストリームドメインコードで定義された前記ストリームに対するデータ構造を生成し、当該データ構造が、前記ストリームに対するデータの型、前記ストリームソースモジュール、及び、前記ストリームデスティネーションモジュールを示すものであり、前記ストリームソースモジュールがデータ値を前記ストリームに代入するときに、当該代入があったことを検出し、前記データ構造を更新することによって、当該データ値を、前記ストリームデスティネーションモジュールに対して利用可能にし、前記複数のプロセシングコアのうちの１つ上で実行されるよう前記ストリームデスティネーションモジュールをスケジュールする、
ランタイムシステムと、
を備える、コアベースのプログラム可能な演算デバイス。
【請求項２】
前記ストリームドメインコードは、入力ストリームおよび出力ストリームを含むストリーム式を含み、前記ストリーム式により、データ値が前記入力ストリームから消費され、データ値が前記ストリーム式の前記出力ストリームにおいて作られる、請求項１に記載の装置。
【請求項３】
前記ストリーム式は、関数に対応する関数コールを含み、前記ストリーム式により、前記入力ストリームから取得された前記データ値を用いて関数がコールされ、前記関数コールにより返される結果は、前記関数コールと関連付けられた前記出力ストリームに代入される、請求項２に記載の装置。
【請求項４】
ソースモジュールのストリームの前記出力ストリームで生成されたデータ値が、前記ストリームデスティネーションモジュールに伝えられ、当該伝えられたデータ値が、第2の、以前に定義されたストリームである、
請求項２に記載の装置。
【請求項５】
前記ストリーム式は複数のストリーム式のうちの１つであり、前記複数のストリーム式のそれぞれは、対応する別個のプロセシングコア上で実行される、請求項２に記載の装置。
【請求項６】
前記ストリームドメインコードは、入力ストリームおよび出力ストリームを有するモジュールを含み、前記モジュールは前記入力ストリームからデータ値を消費し、前記出力ストリームにおいてデータ値を作る、請求項１に記載の装置。
【請求項７】
前記モジュールは複数のモジュールのうちの１つであり、各モジュールは、前記複数のプロセシングコアのうちの別個の１つ上で実行される、請求項６に記載の装置。
【請求項８】
前記モジュールはその本体内に第２のモジュールを含む、請求項６に記載の装置。
【請求項９】
前記モジュールは逐次的に実行されるスレッドドメインコードを含む、請求項６に記載の装置。
【請求項１０】
前記出力ストリームは、前記モジュールに含まれる複数の出力ストリームのうちの１つである、請求項６に記載の装置。
【請求項１１】
前記モジュールは、前記ストリームドメインコードに含まれるモジュール配列に編成された複数のモジュールのうちの１つのモジュールであり、前記モジュール配列は各モジュールへのアクセスを可能にする複数のインデックスを有する、請求項６に記載の装置。
【請求項１２】
前記ストリームドメインコードは、複数の配列インデックスと関連付けられたストリームの配列を含み、前記ストリームの配列内の各ストリームは、ストリームソースの配列からストリームデスティネーションの配列へとデータ値を伝え、各ストリームは配列インデックスを介してアクセス可能である、請求項１１に記載の装置。
【請求項１３】
前記ストリーム配列内の前記ストリームのそれぞれは、前記モジュール配列の前記入力ストリームまたは前記モジュール配列の前記出力ストリームに結合された、請求項１２に記載の装置。
【請求項１４】
前記ランタイムシステムは、プロセシングコア、ハードウェア、または再設定可能なハードウェアにより実行され得る、非一時的媒体上に記憶された１つの命令で実装される、請求項１に記載の装置。
【請求項１５】
前記ランタイムシステムは、利用可能なデータ値に対しての先入れ先出しキューを前記ストリームデスティネーションモジュールにおいて有する、請求項１に記載の装置。
【請求項１６】
前記ランタイムシステムが、各プロセシングコアに、実行されるべきゼロ個かまたはそれ以上のタスクを割り当て、各タスクは、ストリーム式または前記ストリームドメインコードに現れるスレッドドメインコードのインスタンスを実装するものであり、
前記装置が更に、前記プロセシングコアに割り当てられた前記タスクの実行を管理するタスクマネージャを備える、
請求項１５に記載の装置。
【請求項１７】
前記タスクマネージャは、各タスクの各入力ストリームに対する消費側カウントと、各タスクの各出力ストリームに対する作成側カウントと、実行される準備が整ったタスクの実行準備完了タスクキューと、タスクが実行可能となるために有効化されることが要求されるタスク入力ストリームの個数を判定する各タスクに対するタスク入力カウントと、タスクが実行可能となるために有効化されることが要求されるタスク出力ストリームの個数を判定する各タスクに対するタスク出力カウントとを保持し、
前記タスクマネージャは、
ストリームソースにおける前記タスクから送信されたフォワードアクノレッジメントに応答して、入力ストリームの前記消費側カウントをインクリメントし、前記フォワードアクノレッジメントは、追加的なデータ値が、前記入力ストリームに関連付けられた前記先入れ先出しキューにすでに格納されていることを示すことと、
ストリームデスティネーションにおける前記タスクから送信されたバックワードアクノレッジメントに応答して出力ストリームの前記作成側カウントをデクリメントし、前記バックワードアクノレッジメントは、データ値が、前記ストリームデスティネーションモジュールにおける前記入力ストリームに関連付けられた前記先入れ先出しキューからすでに除去されていることを示すことと、
作成側カウントおよび消費側カウントの有効化を監視することと、
有効化された消費側カウントの個数が前記タスク入力カウントに到達し、且つ有効化された作成側カウントの個数が前記タスク出力カウントに到達したとき、タスクを前記実行準備完了タスクキューに置くことと、
タスクが前記実行準備完了タスクキューの先頭にあり、且つ前記プロセシングコアが利用可能であるとき、前記タスクを実行することと、
ストリームアクセス命令によりブロックされたかまたはタイムアウトした前記タスクが実行を完了した場合に前記タスクの実行を停止することと、
をさらに実行する、請求項１６に記載の装置。
【請求項１８】
前記タスクマネージャは、プロセシングコア、ハードウェア、または再設定可能なハードウェアにより実行され得る、非一時的媒体上に記憶された１つの命令で実装される、請求項１６に記載の装置。
【請求項１９】
コンピュータシステムを動作させるための方法であって、当該コンピュータシステムが、互いに結合された複数のプロセシングコアを含むものであり、当該方法が、
ストリーム、当該ストリームに対するデータの型、当該ストリームに対するストリームソースモジュール、及び、メモリ内のストリームに対するストリームデスティネーションモジュールを定義するストリームドメインコードを記憶し、
前記メモリから前記ストリームドメインコードを取得し、
ランタイムシステムを介して、前記ストリームドメインコードで定義された前記ストリームに対するデータ構造を生成し、当該データ構造が、前記ストリームに対するデータの型、前記ストリームソースモジュール、及び、前記ストリームデスティネーションモジュールを示すものであり、
前記ストリームソースモジュールがデータ値を前記ストリームに代入するときに、当該代入があったことを検出し
前記データ構造を更新することによって、当該データ値を、前記ストリームデスティネーションモジュールに対して利用可能にし、
前記複数のプロセシングコアのうちの１つ上で実行されるよう前記ストリームデスティネーションモジュールをスケジュールすること、
を含む方法。
【発明の詳細な説明】
【技術分野】
【０００１】
関連出願の相互参照
本願は、２０１０年１月２１に出願された米国仮特許出願整理番号第６１／２９７，１３９号の優先権を主張するものである。本願は、現在は米国特許第６，８３６，８３９号となった２００１年３月２２日に出願された「ＡＤＡＰＴＩＶＥ  ＩＮＴＥＧＲＡＴＥＤ  ＣＩＲＣＵＩＴＲＹ  ＷＩＴＨ  ＨＥＴＥＲＯＧＥＮＥＯＵＳ  ＡＮＤ  ＲＥＣＯＮＦＩＧＵＲＡＢＬＥ  ＭＡＴＲＩＣＥＳ  ＯＦ  ＤＩＶＥＲＳＥ  ＡＮＤ  ＡＤＡＰＴＩＶＥ  ＣＯＭＰＵＴＡＴＩＯＮＡＬ  ＵＮＩＴＳ  ＨＡＶＩＮＧ  ＦＩＸＥＤ，  ＡＰＰＬＩＣＡＴＩＯＮ  ＳＰＥＣＩＦＩＣ  ＣＯＭＰＵＴＡＴＩＯＮＡＬ  ＥＬＥＭＥＮＴＳ」を発明の名称とする米国特許出願整理番号第０９／８１５，１２２号、現在は米国特許第７，３２５，１２３号となった「ＨＩＥＲＡＲＣＨＩＣＡＬ  ＩＮＴＥＲＣＯＮＮＥＣＴ  ＦＯＲ  ＣＯＮＦＩＧＵＲＩＮＧ  ＳＥＰＡＲＡＴＥ  ＩＮＴＥＲＣＯＮＮＥＣＴＳ  ＦＯＲ  ＥＡＣＨ  ＧＲＯＵＰ  ＯＦ  ＦＩＸＥＤ  ＡＮＤ  ＤＩＶＥＲＳＥ  ＣＯＭＰＵＴＡＴＩＯＮＡＬ  ＥＬＥＭＥＮＴＳ」を発明の名称とする米国特許出願整理番号台１０／３８４，４８６号、および現在は米国特許第７，６０９，２９７号となった「ＨＡＲＤＷＡＲＥ  ＴＡＳＫ  ＭＡＮＡＧＥＲ」を発明の名称とする米国特許出願整理番号第１０／４４３，５０１号に関する。これらの特許出願の全部は参照することにより本明細書に援用される。
【０００２】
本発明は、全般的には、複数プロセッサシステムをプログラムすることに関し、さらに詳細には、ストリームおよびスレッドの両方を組み込む並列プログラミング言語の構文を効果的に利用するハードウェアタスクマネージャに関するものである。
【背景技術】
【０００３】
一般に、デジタルシステムにおける処理性能に制限を加えるものは、デジタルシステム内の異なる構成要素およびサブシステムの間で命令、データ、および他の情報を伝送する際の効率および速度となっている。例えば、汎用フォンノイマン型アーキテクチャにおけるバス転送速度がプロセッサとメモリとの間のデータ転送速度を支配し、その結果として、演算性能（例えば、１００万命令／秒（ＭＩＰＳ：ｍｉｌｌｉｏｎ  ｉｎｓｔｒｕｃｔｉｏｎｓ  ｐｅｒ  ｓｅｃｏｎｄ）、浮動小数点演算回数／秒（ＦＬＯＰＳ：ｆｌｏａｔｉｎｇ−ｐｏｉｎｔ  ｏｐｅｒａｔｉｏｎｓ  ｐｅｒ  ｓｅｃｏｎｄ）、その他）に限界が生じてしまうこととなる。
【０００４】
例えばマルチプロセッサ設計または並列プロセッサ設計等の他の型のコンピュータアーキテクチャ設計においては、異なるプロセッサのそれぞれが他のプロセッサ、複数のメモリ装置、入出力（Ｉ／Ｏ）ポート、その他と通信することが可能となるよう、複雑な通信能力、すなわち相互接続能力が必要となる。今日、プロセッサシステム設計が複雑化されるとともに、効果的かつ高速な相互接続機構の重要度が飛躍的に高まった。
【０００５】
しかし、速度、設計の柔軟性、および簡易性の目的を最適化する係る機構を設計することは困難である。
【０００６】
現在のところ、並列プログラミングは、スレッドを演算の中心的・統率的原理としてスレッドに基づいて行われている。しかし、スレッドは演算モデルとしては顕著な欠点を有する。なぜなら、スレッドは極めて非決定的であり、係る非決定性を抑えて決定的な目的を達成するには、プログラミングスタイルに依存することになるからである。テストおよび検証は、この甚だしい非決定性が存在すると困難なものとなる。ＧＰＵ（グラフィックス処理ユニット）ベンダーにより提案されてきた１つのソリューションは、プログラミングモデルにおいて表現可能な並列性の形態を、狭めることである。しかし、データ並列性に関するＧＰＵベンダーの焦点は、プログラマたちの手を拘束し、マルチコアプロセッサの全潜在能力を利用する機会を妨げてしまうものである。
【０００７】
さらに、スレッドは同一コアのバンク上で実行されるとは限らない。現代のコンピュータ（スーパーコンピュータ、ワークステーション、デスクトップ、およびラップトップ）は、異なる異種コアの困惑的な配列を含み、それらすべてが、プログラムするにあたり、別個のプログラミングモデルを要求する。例えば、マザーボードは１個から４個の主要なＣＰＵ（中央処理装置、例えばＰｅｎｔｉｕｍプロセッサ）を有し、各ＣＰＵは、オンダイまたはオンパッケージのＧＰＵ（グラフィックス処理ユニット、例えばＮＶＩＤＩＡのＧＰＵ）とともに、１個から６個のオンダイのＣＰＵコアを有し、ＧＰＵ自体が、いくつかの、別個のビデオおよびオーディオ・エンコードおよびデコード・コア（複数のビデオ規格、例えばＭＰＥＧ２、ＭＰＥＧ４、ＶＣ−１、Ｈ．２６４その他をエンコードおよびデコードするための）とともに、１６個から２５６個のＧＰＵコアを備える。マザーボード上にも、１個から４個の別個のハイエンド、および設定可能な（コアが、様々なあらかじめ存在する規格をエンコード／デコードするために選択され得ることを意味する）ビデオ／オーディオ・エンコードおよびデコード・コア（複数のビデオ規格、例えば、解像度が高く複数の音響チャンネルを有するＭＰＥＧ２、ＭＰＥＧ４、ＶＣ−１、およびＨ．２６４）も存在する。プロセシングコアからなる追加的なサブシステムが、通信コアの形で、マザーボードに追加される（例えば、ＴＣＰ／ＩＰ機能を肩代わりするためのコア。これらのコアは、典型的には１つまたは複数のＣＰＵコアおよび１つまたは複数のパケットプロセシングコアから作られる。１つまたは複数のブロードバンド／ベースバンドプロセシングコアから作られたＷｉＦｉコア、ＢｌｕｅＴｏｏｔｈコア、ＷｉＭａｘコア、３Ｇコア、４Ｇコア）。
【０００８】
スーパーコンピュータ等の、現代のハイエンドのスペクトル装置においては、１個のマザーボードに対して１個または４個のＦＰＧＡ（フィールド・プログラマブル・ゲートアレイ：ｆｉｅｌｄ  ｐｒｏｇｒａｍｍａｂｌｅ  ｇａｔｅ  ａｒｒａｙ）が追加される。各ＦＰＧＡは、複数のハードＩＰまたはソフトＩＰのＣＰＵコアおよび複数のＤＳＰコアとともに、１０万個から１０００万個の非常に簡単なＣＬＢプロセシングコアからなる）。次に、これらのマザーボード自体が１００個から１０００個単位で複写および相互接続されて、現代のスーパーコンピュータが作られる。次いで、これらのシステム（デスクトップ／ワークステーション／ラップトップおよび／またはスーパーコンピュータ）がインターネットを介して相互接続されることにより、全国的なまたはグローバルな演算能力が提供されることとなる。
【０００９】
係る多能な一連のコアを「管理」および「プログラミング」することは、極めて困難である。大多数のプログラマは、このような労を取ろうとすることさえなく、他の複数のコアに関しては無視して、１つのコアのみについてプログラミングすることに落ち着く。当該技術分野においては、「自明な並列性の問題（ｅｍｂａｒｒａｓｓｉｎｇｌｙ  ｐａｒａｌｌｅｌ  ｐｒｏｂｌｅｍｓ）」（例えば、グーグル検索アルゴリズムは、並列スレッド間において双方向性がほとんど存在しないかまたはまったく存在しないという事実により、複数のＣＰＵ間に分配することが容易である）として知られる一定数のアルゴリズムが存在する。しかしながら大部分の問題はこれらの特性を有さず、高程度の双方向性および同期化が複数のスレッド間で要求される。
【００１０】
したがって、現代のプログラミング言語のストリームにおけるような、マルチスレッド化、無制限の並列性、および決定的挙動を組み込むことが望まれるであろう。ストリームは、少なくとも１９７8年におけるＣプログラム言語の導入時にまでさかのぼり、Ｃ＋＋、Ｊａｖａ（登録商標）、Ｖｉｓｕａｌ  Ｂａｓｉｃ、およびＦ＃等の言語に組み込まれてきた。しかし、これらの言語において、ストリームは、Ｉ／Ｏおよびファイルアクセスに対するフレームワーク等の、むしろ狭い役割が委ねられている。したがって、並列プログラミングにおけるストリームの役割を、第１クラスのオブジェクト、すなわち変数の地位にほぼ匹敵する地位へと拡張することが望まれる。
【発明の概要】
【課題を解決するための手段】
【００１１】
１つの例によれば、プログラム可能なコアに基づく演算デバイスが開示される。係る演算デバイスは、相互に接続された複数のプロセシングコアを備える。メモリは、ストリームデスティネーションモジュールおよびストリームソースモジュールを定義するストリームを含むストリームドメインコードを記憶する。ストリームソースモジュールはデータ値をストリームに代入し、ストリームは、ストリームソースモジュールからストリームデスティネーションモジュールへとデータ値を伝える。ランタイムシステムは、いつデータ値がストリームデスティネーションモジュールに対して利用可能となるかを検出し、複数のプロセシングコアのうちの１つ上で実行されるようストリームデスティネーションモジュールをスケジュールする。
【００１２】
本発明の追加的な態様は、図面を参照してなされる様々な実施形態の詳細な説明を鑑みると、当業者に明らかとなるであろう。なお、図面の簡単な説明は以下で提供される。
【図面の簡単な説明】
【００１３】
【図１】開示されたストリームに基づくプログラミングモデルと互換性を有する適応的演算エンジンの概略図である。
【図２】プログラミングモデルと互換性を有する適応的演算機械のブロック図である。
【図３】図２に示す適応的演算機械のネットワークにおけるネットワークワードを示す図である。
【図４】図１のＡＣＥアーキテクチャまたは図２のＡＣＭアーキテクチャにおける、異質ノードと同種ネットワークとの間のノードラッパーインターフェースを示す図である。
【図５】図４におけるノードラッパーに使用されるハードウェアタスクマネージャの基本的構成品を示す図である。
【図６】図２に示すＡＣＭアーキテクチャにおいてデータを流すために使用されるポイント・トゥ・ポイント・チャンネルを示す図である。
【図７】図６におけるポイント・トゥ・ポイント・チャンネルにより使用されるポイント・トゥ・ポイント・ネットワークワードを示す図である。
【図８Ａ】異なるストリームの流れに対するノードに関するモジュールを示す図である。
【図８Ｂ】異なるストリームの流れに対するノードに関するモジュールを示す図である。
【図８Ｃ】異なるストリームの流れに対するノードに関するモジュールを示す図である。
【図８Ｄ】異なるストリームの流れに対するノードに関するモジュールを示す図である。
【図９Ａ】ストリームへの値の代入を示す図である。
【図９Ｂ】ストリームへの値の代入を示す図である。
【図１０】モジュール概念およびストリーム概念を用いてモデル化され得る５タップＦＩＲフィルタを示す図である。
【図１１Ａ】異なる構成のＦＩＦＯを有するモジュールを示す図である。
【図１１Ｂ】異なる構成のＦＩＦＯを有するモジュールを示す図である。
【図１１Ｃ】異なる構成のＦＩＦＯを有するモジュールを示す図である。
【図１２】プログラム言語例において使用されるスレッドのフローチャートである。
【図１３Ａ】プログラム言語例の結合演算の形態を示す図である。
【図１３Ｂ】プログラム言語例の結合演算の形態を示す図である。
【発明を実施するための形態】
【００１４】
適応的演算エンジンおよび適応的演算機械
【００１５】
図１は、１つの演算モデル例を使用するマルチプロセッサシステムの１例を示すブロック図である。本明細書において適応的演算エンジン（ＡＣＥ）１００と称される装置１００は、好適には、集積回路として、または他の追加的な構成品を有する集積回路の１部分として具体化される。模範的な実施形態において、および、以下でより詳細に論じられるように、ＡＣＥ１００は、図示したマトリックス１５０Ａ〜マトリックス１５０Ｎ等の１つまたは複数の再設定可能なマトリックス（またはノード）１５０と、マトリックス相互接続ネットワーク１１０とを備える。模範的な実施形態において、および以下でより詳細に論じられるように、マトリックス１５０Ａおよびマトリックス１５０Ｂ等の１つまたは複数のマトリックス１５０は、コントローラ１２０として機能するよう構成され、一方、マトリックス１５０Ｃおよびマトリックス１５０Ｄ等の他のマトリックスは、メモリ１４０として機能するよう構成される。様々なマトリックス１５０およびマトリックス相互接続ネットワーク１１０は、フラクタルサブユニットとして、ともに実装され得、このフラクタルサブユニットは数個から１０００個のノードの規模となり得る。
【００１６】
好適な実施形態において、ＡＣＥ１００は、再設定可能マトリックス１５０、コントローラ１２０、およびメモリ１４０間における信号発信および他の伝送のために、または他の入力／出力（「Ｉ／Ｏ」）機能のために、従来の（典型的な別個の）データ、ＤＭＡ、ランダムアクセス、構成、および命令バスを利用しない。むしろ、データ、制御、および設定の情報は、マトリックス相互接続ネットワーク１１０を利用して、これらのマトリックス１５０間で伝送される。なお、このマトリックス相互接続ネットワーク１１０は、コントローラ１２０およびメモリ１４０として設定されたこれらのマトリックス１５０を含む再設定可能なマトリックス１５０間における任意の所与の接続を提供するよう、リアルタイムで設定および再設定が可能である。
【００１７】
メモリ１４０として機能するよう設定されたマトリックス１５０は、固定メモリ要素の演算要素（以下で説明する）を利用して任意の所望の方法または模範的な方法で実装されてもよく、ＡＣＥ１００内に含まれるか、もしくは他のＩＣ内あるいはＩＣの１部分の内部に組み込まれてもよい。模範的な実施形態において、メモリ１４０はＡＣＥ１００の内部に含まれ、好適には、低電力消費型ランダムアクセスメモリ（ＲＡＭ：ｒａｎｄｏｍ  ａｃｃｅｓｓ  ｍｅｍｏｒｙ）である演算要素から構成され得るが、フラッシュ、ＤＲＡＭ、ＳＲＡＭ、ＭＲＡＭ、ＲＯＭ、ＥＰＲＯＭ、またはＥ２ＰＲＯＭ等の他の任意の形態のメモリの演算要素から構成されてもよい。模範的な実施形態において、メモリ１４０は好適には、特には図示しないダイレクトメモリアクセス（ＤＭＡ：ｄｉｒｅｃｔ  ｍｅｍｏｒｙ  ａｃｃｅｓｓ）エンジンを含む。
【００１８】
コントローラ１２０は、好適には、適応的有限状態機械（ＦＳＭ：ｆｉｎｉｔｅ  ｓｔａｔｅ  ｍａｃｈｉｎｅ）、縮小命令セット（「ＲＩＳＣ：ｒｅｄｕｃｅｄ  ｉｎｓｔｒｕｃｔｉｏｎ  ｓｅｔ」）プロセッサ、以下に説明する２種類の機能を実行する能力を有するコントローラまたは他の装置もしくはＩＣとして設定されたマトリックス１５０Ａおよびマトリックス１５０Ｂを用いて実装される。代替的には、これらの機能は従来のＲＳＣまたは他のプロセッサを利用して実装され得る。「カーネル」制御と称される第１制御機能は、マトリックス１５０Ａのカーネルコントローラ（「ＫＡＲＣ」）として図示され、「マトリックス」制御と称される第２制御機能は、マトリックス１５０Ｂのマトリックスコントローラ（「ＭＡＲＣ」）として図示される。コントローラ１２０のカーネルおよびマトリックスの制御機能は、様々なマトリックス１５０の設定可能性および再設定可能性を参照して、および本明細書において「シルバーウェア」モジュールと称される結合されたデータ、設定、および制御情報の模範的な形態を参照して、以下で詳細に説明される。
【００１９】
図１のマトリックス相互接続ネットワーク１１０は、サブセットの相互接続ネットワーク（図示せず）を備える。これらの相互接続ネットワークは、ブール相互接続ネットワーク、データ相互接続ネットワーク、および本明細書において集合的および一般的に「相互連結（ｉｎｔｅｒｃｏｎｎｅｃｔ）」、「相互接続（ｉｎｔｅｒｃｏｎｎｅｃｔｉｏｎ）（単数または複数）」、「相互接続ネットワーク（ｉｎｔｅｒｃｏｎｎｅｃｔｉｏｎ  ｎｅｔｗｏｒｋ）（単数または複数）」、または「ネットワーク」と称される他のネットワークまたは相互接続方式を含み、当該技術分野において一般に知られるように、ＦＰＧＡ相互接続ネットワークまたはスイッチングファブリックを利用する等のさらに多様な様式で実装され得る。模範的な実施形態において、様々な相互接続ネットワークは、例えば米国特許第５，２１８，２４０号、米国特許第５，３３６，９５０号、米国特許第５，２４５，２２７号、および米国特許第５，１４４，１６６号において説明されるように実装される。これらの様々な相互接続ネットワークは、コントローラ１２０、メモリ１４０、様々なマトリックス１５０、演算ユニット（または「ノード」）、および演算要素の間で選択可能（スイッチング可能）な接続を提供し、それにより、本明細書で一般に「設定情報」と称される設定信号に応答して、またはその制御下で、本明細書で説明される設定および再設定を行うための物理的基盤が提供される。加えて、様々な相互接続ネットワーク（１１０、２１０、２４０、および２２０）は、任意形態の従来のまたは個別の入力／出力バス、データバス、ＤＭＡ、ＲＡＭ、設定および命令バスに代わって、コントローラ１２０、メモリ１４０、様々なマトリックス１５０、および、演算ユニット、構成品、および要素間における、選択可能なまたはスイッチング可能な、データ、入力、出力、制御、および設定経路を提供する。
【００２０】
しかし、様々な相互接続ネットワークの、または様々な相互接続ネットワーク内における、スイッチングまたは選択の操作は当該技術分野において周知の方法で実装され得るが、本発明に係る相互接続ネットワークの設計およびレイアウトが以下に詳細に説明するように新しく新規であることは指摘されるべきである。例えば、様々なレベルの相互接続が、様々なレベルのマトリックス、演算ユニット、および要素に対応して提供される。マトリックス１５０のレベルにおいては、従来技術のＦＰＧＡ相互接続と比較すると、所与のエリアにおける接続能力がより低いマトリックス相互接続ネットワーク１１０は、より制限され且つ「豊富さ」がより低く、そのために、キャパシタンスが低減され、動作スピードが増加する。しかし、特定のマトリックスまたは演算ユニット内においては、相互接続ネットワークは、顕著に濃度が高く豊富であるため、狭いまたは近接した参照の局所性においては、より高い適応および再設定の能力を有し得る。
【００２１】
様々なマトリックスまたはノード１５０は、再設定可能および異種である。すなわち、一般に、所望の設定に応じて、再設定可能なマトリックス１５０Ａは、一般に、再設定可能なマトリックス１５０Ｂからマトリックス１５０Ｎとは異なり、再設定可能なマトリックス１５０Ｂは、一般に、再設定可能なマトリックス１５０Ａおよびマトリックス１５０Ｃからマトリックス１５０Ｎとは異なり、再設定可能なマトリックス１５０Ｃは、一般に、再設定可能なマトリックス１５０Ａ、マトリックス１５０Ｂ、マトリックス１５０Ｄからマトリックス１５０Ｎとは異なり、以下同様に異なる。様々な再設定可能なマトリックス１５０はそれぞれ、一般に、異なる、または多様な混合の、適応的且つ再設定可能なノードまたは演算ユニットを含み、次いでノードは、一般に、異なった、または多様な混合の、様々な方法で適応的に接続、設定、および再設定され得ることにより、様々な相互接続ネットワークを通して様々な機能を実行する、固定の、特定用途向けの演算構成品および要素を含む。多様な内部設定および再設定に加えて、様々なマトリックス１５０は、マトリックス相互接続ネットワーク１１０を通して他のマトリックス１５０のそれぞれに対して、高レベルで接続、設定、および再設定され得る。ＡＣＥアーキテクチャの詳細は、上記で参照した関連する特許出願において見出され得る。
【００２２】
並列演算モデルを使用し得る適応的演算機械１６０の他の例が図２に示される。この例における適応的演算機械１６０は、ネットワーク１６２を介してともに接続された３２個の異種リーフノード１８０を有する。ネットワーク１６２は、１群のネットワーク入力ポート１６４、１群のネットワーク出力ポート１６８、省略可能なシステムインターフェースポート１７０、外部メモリインターフェース１７２、および内部メモリインターフェース１７４に接続された単一のルート１６４を有する。スーパーバイザーノードまたはＫノード１７８もルート１６４に接続される。
【００２３】
ノード１８０は、それぞれが、４進木１８２等の４進木にグループ化される。４進木１８２等の４進木は、それぞれが単一の親ノードおよび最大４個の子ノード１８０に接続された、５ポートのスイッチ要素１８４を用いて実装される。このスイッチ要素は、公正なラウンドロビン調停方式を実装し、性能増強のためにマルチレベルの先読みを有するパイプラインを提供する。この例において、全経路の幅は一定（５１ビット）であるが、ネットワークのバンド幅を増強するために、Ｌｅｉｓｅｒｓｏｎのファットツリーのスタイルで、木が上昇するにつれて経路を広げるためのオプションが利用可能である。
【００２４】
この例において、ネットワーク１６２上のすべてのトラフィックは、図３のネットワークワード１８８に示すように、５１ビットネットワークワードの形態である。ネットワークワード１８８は、ルートフィールド１９０、セキュリティビット１９２、サービスフィールド１９４、補助フィールド１９６、およびペイロードフィールド１９８を有する。ルートフィールド１９０は、ネットワークワード１８８の宛先アドレスである。ルートフィールド１９０における上位２ビットはチップＩＤである。セキュリティビット１９２は、設定メモリに対するピーク（読み出し）およびポーク（書き込み）を可能にする。セキュリティビット１９２は、Ｋノード１７８により送信されるワードに対してのみ設定される。サービスフィールド１９４はサービス種類を定義し、補助フィールド１９６はサービス種類に依存する。サービスフィールド１９４はポイント・トゥ・ポイント（ＰＴＰ）を含む１６のサービス種類のうちの１つを定義する。なお、このＰＴＰは、データおよびＰＴＰアクノレッジメントを流すことに関し、ＰＴＰアクノレッジメントは、データに対するフロー制御をサポートし、宛先ノードにおける消費側カウントまたは製作側カウントをインクリメントまたはデクリメントさせるものである。
ノードラッパー
【００２５】
図４は、図１のＡＣＥアーキテクチャまたは図２のＡＣＭアーキテクチャにおける異質ノードと同種ネットワークとの間のインターフェースを示す。このインターフェースは、各ノードに対して共通の入力および出力の機構を提供するために用いられるため、「ノードラッパー」と称される。ノードの実行ユニットおよびメモリは、ノードラッパーを介して、ネットワークおよび制御ソフトウェアに対して接続され、それにより、均一で一貫したシステムレベルのプログラミングモデルが提供される。この例において、ノード１８０はメモリ２１０および実行ユニット２１２を備える。ノードラッパーの詳細は、上記で参照した関連する特許出願に見出され得る。
【００２６】
好適な実施形態において、各ノードラッパーは、ハードウェアタスクマネージャ（ＨＴＭ：ｈａｒｄｗａｒｅ  ｔａｓｋ  ｍａｎａｇｅｒ）２００を備える。ノードラッパーは、データディストリビュータ２０２、省略可能なダイレクトメモリアクセス（ＤＭＡ）エンジン２０４、およびデータアグリゲータ２０６も備える。ＨＴＭは、ノードプロセッサおよびリソースの実行または使用をそれぞれ調整する。ＨＴＭは、タスクリストを処理し、実行準備完了キューを作成することにより、これを行う。ＨＴＭは、図２のＫノード１７８と称される専用ノードまたは制御ノード（図示せず）により設定および管理される。しかし他のＨＴＭ制御手法も使用され得る。
【００２７】
図４におけるノードラッパーは、ノード１８０を、その内部構造または機能に関わらず、図２における適応的演算機械１６０のすべての他のノードまたは図１における適応的演算エンジン１００と、外観において同等にする。ノードラッパーはまた、タスク管理およびネットワーク相互作用に関連する無数の活動を処理しなければならない状況から実行ユニット２１２を解放する。とりわけ、ノードラッパーは、図２のネットワークワード１８８等の受信するネットワークワードのそれぞれを１つのクロックサイクル内で適正な方法で処理する機能を担当する。
【００２８】
図４における実行ユニット２１２は、タスク実行機能を担当する（タスクはモジュールインスタンスと同等である）。実行ユニット２１２はデジタル・シグナル・プロセッサ（ＤＳＰ：ｄｉｇｉｔａｌ  ｓｉｇｎａｌ  ｐｒｏｃｅｓｓｏｒ）、縮小命令セット（ＲＩＳＣ）プロセッサ、ドメイン固有プロセッサ、特定用途集積回路（ＡＳＩＣ：ａｐｐｌｉｃａｔｉｏｎ−ｓｐｅｃｉｆｉｃ  ｉｎｔｅｇｒａｔｅｄ  ｃｉｒｃｕｉｔ）、または再設定可能（ＦＰＧＡ）ファブリックを含み得る。その形態に関わらず、実行ユニット２１２は、標準的インターフェースを通してノードラッパーと相互作用する。
【００２９】
ノードメモリ２１０は、ノードラッパーおよび実行ユニット２１２の両方に対してアクセス可能である。ノードメモリ２１０は、受信するストリーミングデータをノードラッパーが格納し且つそのデータを実行ユニット２１２がアクセスする位置にある。ノード自体のメモリ２１０は、典型的には、実行ユニット２１２が出力データを送信する位置にはない。メモリアクセスを最小限とするために、出力データは、通常は、そのデータを要求しているノード、すなわち消費側ノード（単数または複数）に直接的に送信される。ノードメモリ２１０は、タスクパラメータを記憶するためにも用いられ、一時的（作業用）記憶領域として、タスクに対して利用可能である。
【００３０】
ノード１８０がストリーミングデータの消費側であると同時に作成側である図２のＡＣＭ１６０等のマルチノードシステムにおいては、作成速度と消費速度とが一致することが望ましい。１つのノード上の作成側タスクは、他のノード上の消費側タスクが対処可能であるよりも、より高いかまたはより低い速度で、データを作成し得る。作成側が、消費側が処理可能な速度よりも高い速度でデータを送信するならば、データはやがて失われる。作成側が、消費側が対処可能な速度よりも低い速度でデータを送信するならば、消費側はデータに対して飢餓状態となり、その結果、消費側は無駄に座して追加的なデータを待つことを余儀なくされ得る。
【００３１】
ＡＣＭ１６０は、ポイント・トゥ・ポイント・プロトコルおよび図４のノードラッパーを介して、タスク管理、フロー制御、および負荷分散のための均一且つ一貫した機構を提供する。タスク管理により、タスクが十分な入力データを有し且つタスクにより生成されたデータを消費側ノード（単数または複数）において収容する十分なスペースが存在する場合にのみタスクが実行状態となることが確実なものとなる。フロー制御により、作成側タスクが、短すぎる時間内に多すぎるデータにより消費側タスクを圧倒しないことが保証される。負荷分散は、作成側タスクがデータをいくつかの代替的な消費側ノード間に分散することを可能にし、その結果、作成側タスクはより高い速度で動作することが可能となる。
【００３２】
ストリーミングデータは、図５に示すポイント・トゥ・ポイント・チャンネル（ポイント・トゥ・ポイント・ストリーム）２５０を介して、ノード１８０（ポイント）間で転送される。ポイント・トゥ・ポイント・チャンネル等の各ＰＴＰチャンネルは、作成側ノード２５２、作成側タスク２５４、出力ポート２５６、消費側ノード２５８、入力ポート２６０、入力バッファ２６２、および消費側タスク２６４を含む。作成側タスク２５４は、作成側ノード２５２の実行ユニット上で実行され、タスク活性化毎に有限サイズブロックのＰＴＰデータを製作する。データのブロックは、一連のＰＴＰワードとしてＰＴＰチャンネル２５０上で送信される。ブロックの送信は、図５においてタスクとして示される。作成側ノード２５２上の出力ポート２５６は、作成側タスク２５４と関連付けられる。
【００３３】
消費側タスク２６４は、消費側ノード２５８上の入力ポートを介して、ＰＴＰチャンネル２５２からＰＴＰデータを受信する。消費側ノード２５８のノードメモリ内の循環型入力バッファ２６２は、受信するＰＴＰデータを記憶する。消費側タスク２６４等の消費側タスクは、タスクの活性化（図５のタスク２）毎に、消費側ノード２５８の実行ユニット上で実行され、循環型入力バッファ２６２に存在する有限量のＰＴＰデータを消費する。
【００３４】
データは、作成側タスク２５４が、図６に示す５０ビットポイント・トゥ・ポイント・ワード２７０を作成側ノード２５２のノードラッパーへと伝送すると、ＰＴＰチャンネル２５２上で伝えられる。ポイント・トゥ・ポイント・ワード２７０は、図３のネットワークワード１８８と同一のフィールドを有し、同じ要素／フィールドには図５の要素番号と同じ要素番号が付される。ポイント・トゥ・ポイント・ワード２７０は、ルートフィールド１９０においてノードワード２７２、補助フィールド１９６においてポートワード２７４、およびペイロードフィールド１９８においてデータワード２７６を含む。この例において、第５１番目のビット、すなわちセキュリティビット１９２は、図２のネットワーク１６２により後に追加される。図４のノードラッパー等のノードラッパーは、次いで、ＰＴＰワードを、図５における消費側ノード２５８に伝送するために、パケット交換ネットワークに渡す。ＰＴＰワード２７０の８ビットルートフィールド１９０は、図５におけるノード２５８等の消費側ノードのノードワード２７２の形でアドレスを提供する。ポートワード２７４は、消費側ノードの入力ポートのうちのどの入力ポートにデータが宛てられているかを指示する、補助フィールド１９６の下位５ビットを含む。ＰＴＰワードが消費側ノードに到達すると、ノードラッパーは、ペイロードフィールド１９８における３２ビットのデータワード１７６を、指示された入力ポートに関連付けられた循環型入力バッファに格納する。このようにして伝送が完了する。
【００３５】
ＡＣＭ１６０は、タスク管理、フロー制御、および負荷分散のための機構を備える。各入力ポートと関連付けられた入力バッファが存在する。入力および出力の両方のポートと関連付けられた、２の補数の符号付きカウントも存在する。
【００３６】
入力ポートに関しては、カウントは消費側カウントと称される。なぜなら、このカウントは、関連するタスクにより消費されるために利用可能である、そのポートの入力バッファにおけるデータ量を反映するからである。消費側カウントは、その値が非負である場合、すなわちその符号ビットが０である場合、有効化される。有効化された消費側カウントは、関連する入力バッファが、関連するタスクの活性化により要求される最小量のデータを有することを示す。システム初期化時または再設定時に、消費側カウントは、典型的には−Ｃにリセットされる。なお、Ｃはタスク活性化毎に要求される３２ビットワードの最少個数である。
【００３７】
出力ポートに関しては、カウントは作成側カウントと称される。なぜなら、このカウントは、関連するタスクにより作成されたデータを受け入れるために、下流側入力バッファにおいて利用可能なスペースの量を反映するためである。作成側カウントは、その値が負である場合、すなわちその符号ビットが１である場合、有効化される。有効化された作成側カウントは、下流側関連入力バッファが、関連するタスクの活性化毎に作成されるデータの最大量を収容するために利用可能なスペースを有することを示す。システム初期化時または再設定時に、作成側カウントは、典型的にはＰ−Ｓ−１にリセットされる。なお、Ｐはタスク活性化毎に作成される３２ビットワードの最大個数、Ｓは３２ビットワードにおける下流側入力バッファのサイズである。
【００３８】
消費側カウントおよび作成側カウントの両方は、典型的には負の値に初期化され、それにより、消費側カウントは開始時において無効状態にあり、一方、作成側カウントは開始時において有効状態にある。この初期状態は、入力バッファが通常はシステム初期化／再設定時において空き状態であるという事実を反映するものである。
【００３９】
消費側カウントおよび作成側カウントは、フォワードアクノレッジメントおよびバックワードアクノレッジメントの形の借方のシステムにより更新される。両種のアクノレッジメントは、図７に示すアクノレッジメントネットワークワード２８０等のネットワークワードである。アクノレッジメントネットワークワード２８０は、図３のネットワークワード１８８と同一のフィールドを有し、同じ要素／フィールドは同一の要素番号が付される。アクノレッジメントネットワーク２８０ワードは、タスク活性化の最終ステップとしてタスクにより送信される。両方の場合において、ペイロードフィールド１９８は、４個のサブフィールド、すなわち、アクノレッジメント種類サブフィールド２８２（１ビット）、ポートサブフィールド２８４、（３）タスクサブフィールド２８６、およびＡｃｋ値サブフィールド２８８、を有する。
【００４０】
タスクが各活性化の終了時にタスクが実行する一連のアクノレッジメントが、以下に説明される。タスクの各出力ポートに対して、消費側入力ポートおよび消費側タスクを指定するフォワードアクノレッジメントが消費側ノードに送信される。Ａｃｋ値は、タスクが消費側入力ポートに送信したＰＴＰワードの個数である。出力ポートおよびタスクを指定するバックワードアクノレッジメント（自己ａｃｋ）は、タスクが存在するノードに送信される。Ａｃｋ値は、タスクが出力ポートを介して送信したＰＴＰワードの個数である。
【００４１】
タスクの各入力ポートに対して、作成側出力ポートおよび作成側タスクを指定するバックワードアクノレッジメントは、作成側ノードに送信される。Ａｃｋ値は、タスクが、入力ポートのバッファから消費した３２ビットワードの個数にマイナス符号を付した値である。入力ポートおよびタスクを示すフォワードアクノレッジメント（自己ａｃｋ）は、タスクが存在するノードに送信される。Ａｃｋ値は、タスクが入力ポートのバッファから消費した３２ビットワードの個数にマイナス符号を付した値である。
ハードウェアタスクマネージャ
【００４２】
図４に示すハードウェアタスクマネージャ２００は、受信するアクノレッジメントに応答して消費側カウントおよび作成側カウントを更新する機能を担当するノードラッパーの１部分である。ハードウェアタスクマネージャ２００はまた、これらのカウントの符号ビットを監視し、カウントの適切な組が有効化されるとタスクを起動する。この最後の任務は、ポートではなくタスクと関連付けられた２つの符号付きカウント、すなわちタスク入力カウントおよびタスク出力カウントを用いて満足される。タスクの入力（出力）カウントは、有効化されたタスク消費側（作成側）カウントの個数を反映する。タスクカウントはその値が非負である場合、有効化される。タスクは、その入力カウントおよび出力カウントの両方が有効化されると、有効化され、実行のために利用可能となる。
【００４３】
受信するアクノレッジメントは、様々なカウントを更新し、それによりタスクは以下のように起動される。フォワードアクノレッジメントが受信されると、指定されたポートが入力ポートとして解釈され、Ａｃｋ値が、対応する消費側カウントに加えられる。消費側カウントが、無効状態から有効状態へと（有効状態から無効状態へと）遷移すると、指定されたタスクの入力カウントは、１だけインクリメント（デクリメント）される。バックワードアクノレッジメントが受信されると、指定されたポートが出力ポートとして解釈され、Ａｃｋ値が、対応する作成側カウントに加えられる。作成側カウントが、無効状態から有効状態へと（有効状態から無効状態へと）遷移すると、指定されたタスクの出力カウントは、１だけインクリメント（デクリメント）される。バックワードアクノレッジメントまたはフォワードアクノレッジメントが受信され、指定されたタスクの入力カウントおよび出力カウントの両方が有効化されると、そのタスクは、もし実行準備完了キュー上にない場合は、実行準備完了キュー上に置かれる。タスクは、キューの先頭に到達すると起動される。
【００４４】
これらの動作は、タスクに対する始動規則を具体化する。これらにより、タスクは実行準備完了キューに置かれることとなり、最終的に十分な個数の消費側カウントおよび十分な個数の作成側カウントが有効化されると、タスクは実行される。これらの十分な個数の具体的数値は、タスクの入力カウントおよび出力カウントの初期値により決定される。Ｉ（Ｏ）が、タスクに関連付けられた入力（出力）ポートの個数であり、ＩＣＩｎｉｔｉａｌ（ＯＣＩｎｉｔｉａｌ）がタスクの入力（出力）カウントの初期値であり、且つ、上述のように全部の消費側カウントが最初無効状態にあり全部の作成側カウントが最初有効状態にあると仮定すると、タスクは、
Ｉ個の消費側カウントうちの−ＩＣＩｎｉｔｉａｉｌ個が有効状態にあり、
Ｏ個の作成側カウントうち（Ｏ−ＯＣＩｎｉｔｉａｌ）個が有効状態にある場合、始動する。
例えば、Ｉ＝４に対して、
ＩＣＩｎｉｔｉａｌ＝−１であるならば、４個の消費側カウントのうち１個が有効化されなければならない。
ＩＣＩｎｉｔｉａｌ＝−２であるならば、４個の消費側カウントのうち２個が有効化されなければならない。
ＩＣＩｎｉｔｉａｌ＝−３であるならば、４個の消費側カウントのうち３個が有効化されなければならない。
ＩＣＩｎｉｔｉａｌ＝−４であるならば、４個の消費側カウントのうち４個が有効化されなければならない。
Ｏ＝４に対して、
ＯＣＩｎｉｔｉａｌ＝３であるならば、４個の作成側カウントのうち１個が有効化されなければならない。
ＯＣＩｎｉｔｉａｌ＝２であるならば、４個の作成側カウントのうち２個が有効化されなければならない。
ＯＣＩｎｉｔｉａｌ＝１であるならば、４個の作成側カウントのうち３個が有効化されなければならない。
ＯＣＩｎｉｔｉａｌ＝０であるならば、４個の作成側カウントのうち４個が有効化されなければならない。
【００４５】
図１におけるＡＣＥ１００および図２におけるＡＣＭ２００等のマルチプロセッサシステムのプログラミングは、ストリームＣプログラム言語と称され得るものを用いてなされ得る。
ストリームＣモジュール
【００４６】
ストリームＣプログラムにおいては、並行処理を表現するための機構は１つのみ存在する。その機構とは、すなわち、プログラムのモジュール（およびモジュールのようなストリーム式）の並行演算を用いることである。シンタックス的には、モジュールは、Ｃ関数と極めて類似するが、セマンティクス的には、モジュールと関数とは異なる。Ｃ関数（サブルーチン）は、コールされたときにのみ、動作を始める。コールされると、制御は、通常は入力引数とともに、Ｃ関数に渡される。次いで、Ｃ関数はタスク／演算を実行し、終了すると、出力結果とともに制御を返す。Ｃ関数とは異なり、モジュールはコールされることがなく、また制御は、モジュールに渡されることも、モジュールから返されることもない。代わって、モジュールは、他のモジュールおよび外部世界との進行中の相互作用を、入力ポートおよび出力ポートを通して実行する。これらのポートを通して、モジュールは入力値のストリームを受け取り、出力値のストリームを発行する。
【００４７】
モジュールプロトタイプのシンタックスはＣ関数プロトタイプのシンタックスと、３つの例外を除いて、同じである。第１に、キーワードｓｔｒｅａｍがモジュールプロトタイプに先行する。このキーワードは、各モジュール入力およびモジュール出力が、個別の値とではなく、指定された型の値のストリームと関連付けられていることを、コンパイラ／リンカに伝える。第２に、モジュールが複数の主力ストリームを有することを可能にするために、モジュールの戻り値型は、シンタックスにおいて入力パラメータリストと同一である、括弧で囲まれたリストにより置き換えられ得る。第３に、配列の概念をモジュールへと拡張するために、角括弧で囲まれた配列インデックスのリストは、モジュール名の直後および入力引数リストの直前に挿入され得る。モジュール配列については以下で論じる。
【００４８】
以下の式、
ｓｔｒｅａｍ  ｉｎｔ  ｍｏｄｕｌｅＡ  （ｉｎｔ，  ｉｎｔ）；
ｓｔｒｅａｍ  （ｉｎｔ，  ｉｎｔ）  ｍｏｄｕｌｅＢ  （ｉｎｔ，  ｉｎｔ）；
は、モジュール宣言の２つの例である。パラメータ名はここでは省略される。なぜなら、パラメータ名はモジュール宣言においては不要である（モジュール定義またはモジュールインスタンス化とは対比的に）ためである。しかしパラメータ名はプログラマの自由裁量により通常は記憶の一助として、入力に対して、および複数の出力が存在する場合には、出力に対しても、含まれ得る。例えば、２つの宣言は、次のように表現され得る。
ｓｔｒｅａｍ  ｉｎｔ  ｍｏｄｕｌｅＡ  （ｉｎｔ  ａ，  ｉｎｔ  ｂ）；
ｓｔｒｅａｍ  （ｉｎｔ  ｘ，  ｉｎｔ  ｙ）  ｍｏｄｕｌｅＢ  （ｉｎｔ  ａ，  ｉｎｔ  ｂ）；
【００４９】
第１の宣言は、ｍｏｄｕｌｅＡが２つの入力ストリームを有し、その両方が整数型であること、および単一の出力ストリームを有し、これもまた整数型であることを示す。第２の宣言は、ｍｏｄｕｌｅＢが２つの入力ストリームを有し、その両方が整数型であること、および２つの出力ストリームを有し、これらもまた整数型であることを示す。
【００５０】
Ｃ関数の定義と同様に、モジュールの定義は、波括弧（｛および｝）で囲まれた本体を有する。Ｃ関数の定義の場合と同様に、各モジュール入力（および出力が複数ある場合は、各出力モジュール）は識別子が割り当てられる。以下は、モジュール定義の２つの例である。
ｓｔｒｅａｍ  ｉｎｔ  ｍｏｄｕｌｅＡ  （ｉｎｔ  ａ，  ｉｎｔ，  ｂ）
｛
／／モジュール本体
｝
ｓｔｒｅａｍ  （ｉｎｔ  ｘ，  ｉｎｔ  ｙ）  ｍｏｄｕｌｅＢ  （ｉｎｔ  ａ，  ｉｎｔ  ｂ）
｛
／／モジュール本体
｝
【００５１】
モジュールインスタンス化は、Ｃ関数コールに対するモジュールにおける対応物である。関数コールと同様に、モジュールインスタンス化は、モジュールがどこで用いられるかである。これら２種類の表現のシンタックスは同様であるが、セマンティクスは異なる。Ｃコードの１部分は、以下のように表現され得る。
ｉｎｔ  ｘ，  ｙ；
ｉｎｔ  Ｆ  （ｉｎｔ，  ｉｎｔ）；
．
．
．
ｉｎｔ  ｚ  ＝  Ｆ（４，  ｘ＋５＊ｙ）；
【００５２】
第１の文がｘおよびｙが正数であることを宣言する一方で、第２の文はＦが２つの整数パラメータと、１つの整数の結果とを有する関数であることを宣言する。最後の文が、関数コールＦ（４，  ｘ＋５＊ｙ）を含む代入文であり、関数Ｆ（４，  ｘ＋５＊ｙ）は、２つの引数、すなわちＦの２つのパラメータに対応する、式４および式ｘ＋５＊ｙを有する。
【００５３】
この部分的コードのストリーム版は、以下のようになる。
ｓｔｒｅａｍ  ｉｎｔ  ｘ，  ｙ；
ｓｔｒｅａｍ  ｉｎｔ  Ｆ（ｉｎｔ，  ｉｎｔ）；
．
．
．
ｓｔｒｅａｍ  ｉｎｔ  ｚ  ＝  Ｆ（４，ｘ＋５＊ｙ）；
【００５４】
このストリーム版において、これらの文のそれぞれにおいては、ｓｔｒｅａｍというキーワードが先頭にある。シンタックスが変化しているために、セマンティクスにおいても劇的な変化が生じている。個別の値に代わって、値のストリームが用いられる。したがって、第１の文は、ｘおよびｙが整数ストリームであることを宣言する一方、第２の文は、Ｆが、２つの整数ストリーム入力および整数ストリーム出力を有するモジュールであることを宣言する。最後の文は、モジュールインスタンス化Ｆ（４，ｘ＋５＊ｙ）を含む代入文である。このＦ（４，ｘ＋５＊ｙ）は、２つの引数、すなわちＦの２つのパラメータに対応する、ストリーム式４およびストリーム式ｘ＋５＊ｙを有する。
【００５５】
関数コールの場合は、代入ｚ＝Ｆ（４，ｘ＋５＊ｙ）が実行される毎、式４および式４，ｘ＋５＊ｙが評価され、その結果生じた２つの値がパラメータとしてコール時に関数Ｆへと供給される。一定時間経過後、Ｆが値を返す。モジュールインスタンス化の場合は、代入文ｚ＝Ｆ（４，ｘ＋５＊ｙ）が実行されることもなく、また関数Ｆがコールされることもない。代わって、システム初期化時、ストリームＣプログラムが実行を始める直前に、関数Ｆのインスタンスが作成（インスタンス化）され、それにより、インスタンスは、その２つの入力ポート上で整数のストリームを受け取ること、およびその出力ポート上で整数のストリームを作成することの準備が完了する。プログラム実行が開始されると、Ｆのインスタンスは、プログラム終了時まで、動作状態が保持される（すなわちＦのインスタンスは永続的である）。
【００５６】
この簡単な例は、ストリームＣにおいて相互作用するモジュールの集団を作成するために用いられる一般的な機構を示すものである。各モジュールのインスタンス化により、別個のモジュールインスタンスがシステム初期化時に作成される。ひとたび作成（インスタンス化）されると、モジュールインスタンスは、その入力ポート上で値のストリームを受け取ること、およびその出力ポート上で値のストリームを作成することの準備が完了する。さらに、プログラム実行が開始されると、モジュールインスタンスは、プログラム終了時まで、動作状態を保持する。
【００５７】
複数の出力ポートを有するモジュールのインスタンス化の一般的な形は以下の通りである。
（＜識別子リスト＞）  ＜モジュール識別子＞  （＜式リスト＞）
入力引数が式であるのに対し、出力引数は識別子である。これらの識別子は、名称がない出力ストリームに名称を与えるよう機能する。上記のストリーム代入文は、名称ｚをＦ（４，ｘ＋５＊ｙ）の名称がない出力ストリームに割り当てることにより、同じ役割を果たす。例えば、
ｓｔｒｅａｍ  ｉｎｔ  ｗ，  ｘ，  ｙ，  ｚ；
ｓｔｒｅａｍ  （ｉｎｔ，  ｉｎｔ）  Ｆ（ｉｎｔ，  ｉｎｔ）；
．
．
．
（ｗ，  ｚ）  ＝  Ｆ（４，ｘ＋５＊ｙ）；
【００５８】
前述のように、Ｆは２つの整数ストリーム入力を有するが、前の例とは対比的に、このＦは２つの整数ストリーム出力を有する。これら２つの出力ストリームは、識別子（ｗ，  ｚ）のリストとしてＦのインスタンス化時に現れる。なお、これは、２つの出力ストリームに名称ｗおよびｚを与えるよう機能する。
【００５９】
モジュール本体内部の文は、２つのカテゴリー、すなわち、ストリームのみに関与するストリーム文と、Ｃの文の全範囲を含む他に、スレッドをストリームから読み出すこととスレッドをストリームに書き込むこととを可能にする文を含むスレッド文と、に分類される。各モジュールのインスタンス化により、別個のモジュールインスタンスがシステム初期化時に作成されるため、ストリームＣにおいては、モジュールが、その本体内において、またはサブモジュールの本体内において、モジュール自体のインスタンス化を有することはできない。換言すると、モジュール参照の循環性は不可能である。この禁止は、無限個のモジュールインスタンスのインスタンス化という困難なタスクを回避することを支援する。
【００６０】
ストリームＣモジュールにおいて、制御を返すという概念は存在せず、したがってリターン文は適さない。モジュールにおいて、出力値はモジュールの出力ストリームに単に挿入される。しかし、それを行うためには、出力ストリームは名称を有さなければならない。括弧で囲まれた名称を有する出力ストリームのリストを有するモジュールに対しては、それは問題ではない。しかし、モジュールプロトタイプがモジュールの出力ストリームの型のみを提供する場合は、問題となる。その場合、モジュールの本体内のコードは、ストリームドメインにおいてもまたはスレッドドメインにおいても、キーワードｏｕｔをデフォルトモジュール出力ストリームの名称として用いることができる。この用法は、以下の部分的コードにおいて例示される。
ｓｔｒｅａｍ  ｉｎｔ  ｍｏｄｕｌｅＡ  （ｉｎｔ  ａ，  ｉｎｔ  ｂ）
｛
．
．
．
ｏｕｔ  ＝  ａ＋ｂ；
．
．
．
【００６１】
関数がプログラムの演算ビルディングブロックを提供するのに対して、モジュール、およびモジュールと提携するストリームは、ストリームＣプログラムに典型的な相互作用網および並行動作のための枠組を提供する。モジュールは値のストリームを処理するが、そのことにより、モジュールがストリーム内の個々の値にアクセスすることおよびこれらの値を関数に提供することが不可能となるわけではない。同様に、モジュールは、関数の出力値にアクセスすること、およびその値をストリームに挿入することも可能である。一方、関数はモジュールを参照することができない。なぜなら、関数内には係る相互作用のための機構が存在しないためである。能力におけるこの非対称性により、関数がプログラムヒエラルキー上でより低いレベルにある一方、モジュールはより高いレベルにあることがわかる。
【００６２】
モジュールと関数との違いが実質的である一方で、モジュールと関数とが同様である１つの領域が存在する。それは、モジュールおよび関数の両方が副作用をサポートする点である。すなわち、モジュールおよび関数の両方が、それぞれの入力ポートおよび出力ポートとは独立的に、外部データ構造を操作し得る。これは、モジュールが副作用を有し得るスレッドを含み得る事実に由来する。
【００６３】
図８Ａは、モジュール３００と、モジュール３００に対してデータ／制御を提供するいくつか（ゼロ個からＮ個）の入力ストリーム３０２と、次のモジュール／関数に対してデータ／制御を提供するいくつか（ゼロ個からＮ個）の出力ストリーム３０４とを含む、一般的なモジュールを示す。出力ストリームを有さないモジュールは「シンク」であり、入力ストリームを有さないストリームは「ソース」である。
【００６４】
図８Ｂは、２つのモジュール、すなわちモジュールＡ３００およびモジュールＢ３１０を示す図であり、各モジュールは、入力ストリーム３０２および３１２と、出力ストリーム３０４および３１４とを有する。モジュールＡ３００の出力ストリーム３０４はモジュールＢ３１０の入力ストリーム３１２に接続される。モジュールＡ３００は、ＣＰＵコア３０８上で実行されるようマッピングされ、モジュールＢ３１０は第２のＣＰＵコア３１８上で実行されるようマッピングされる。コア３０８、３１８、および３２８は、図２のノード１８０と同様である。
【００６５】
図８Ｃは、モジュールＡ３００およびモジュールＢ３１０をＣＰＵコア３０８等の同一のＣＰＵコアにマッピングすることを示す図である。この事例において、モジュール３００および３１０は、任意の他の別個の制御スレッドのように動作する。第２コア３１８上で実行されるオペレーティングシステムは、モジュール３００および３１０を、プリエンプティブマルチタスキングに基づいてスケジューリングするか、または完了／解放まで実行し得る。両方のモジュール３００および３１０および入力／出力ストリーム３０２、３１２、および３０４、３１４は、「永続的」である（すなわち、これらは処理実行準備完了状態に留まる）ため、演算を実行するための「十分な」入力ストリームデータの、および出力ストリームが演算されたデータを伝達することができる「十分な」スペースの、両方の利用可能性に基づいてモジュールをいつスケジュールするかに関する追加情報は、従来のオペレーティングシステムに提供されなければならない。
【００６６】
多様な異なるアルゴリズムが、モジュールからコアへのマッピングを実行するために用いられ得る。これらのアルゴリズムは、キャッシュ近傍を含み得る。なお、キャッシュ近傍においては、最大個数のストリームを共有するモジュールは、共有されたＤＲＡＭに後続される共有されたＬ３キャッシュに後続される共有されたＬ２キャッシュに後続されるＬ１キャッシュを共有するコアに置かれる。これらのアルゴリズムは、物理近傍アルゴリズムも含み得る。なお、物理近傍アルゴリズムにおいては、最大個数のストリームを共有するモジュールは、互いに物理的に近接するコアに置かれる。例えば、アルゴリズムはダイから始まり、次いで、マザーボード上の集積回路へと、次いでラック上のマザーボードへと、次いで建物の同一階のラックへと、次いで地理的に近接する建物へと移行し得る。他のアルゴリズムは、次の利用可能なフリーであり得る。なお、そこにおいては、ＣＰＵ使用率（現在の使用率、または一定期間にわたる重み付き平均使用率）または順番上次に利用可能なコアに基づく次の「自由な」コアに基づいて、モジュールがコアに割り当てられる。他のアルゴリズムは、予想負荷であり得る。なお、これは、評価された統計的サンプリングに基づいて、モジュールおよびコアを選択する。コア利用の実行平均が、モジュールを、最も負荷が軽いコアにロードするために用いられ得る。他のアルゴリズムは、ユーザによる指定である。なお、ここでは、ユーザ指定によるバーチャルコアＩＤが、全モジュールを物理コアＩＤ上に置くために用いられる。バーチャルコアＩＤの個数が物理的に利用可能なコアを越えると、複数のモジュールが、利用可能な物理コアにわたって均等にロードされる。
【００６７】
図８Ｄは、モジュールＡ３００に存在し、入力ストリーム３０２および出力ストリーム３０４において用いられ得る、様々なデータ構造３３０、３３２、および３３４を示す。メモリ／キャッシュまたはＴＬＢのいずれかに存在し得るデータ構造３３０、３３２、および３３４は、シングルコアまたはマルチコアシステムが、入力ストリーム３０２等の入力ストリームから出力ストリーム３０４等の出力ストリームへとデータを搬送すること、入力ストリーム３０２をモジュールＡ３００等のモジュールへと搬送すること、およびモジュールＡ３００を出力ストリーム３０４へと入力することをスケジュールするために必要となる重要な情報を含む。各モジュールに対して、そのモジュールをユニークに識別し、そのモジュールに対するすべての入力ストリームをユニークに識別し、そのモジュールのすべての出力ストリームをユニークに識別し、入力ストリームおよび出力ストリームの「接続」方法をユニークに識別し、コアを識別し、モジュールが１つのコアから他のコアへとリロケートされるよう、すなわち仮想メモリを介してスワップアウトされるよう状態情報を保持する、情報が存在する。ストリームは動的にモジュールから追加または削除され得、モジュールは動的にコアから追加または削除され得る。
ストリーム
【００６８】
ストリームＣプログラム言語におけるストリームという用語は、すべて同じデータ型であり且つ典型的に一定期間にわたって利用可能状態となる、一連のデータ値を指す。しかし、ストリームＣにおいては、ストリームは、入力および出力のための枠組を遥かに越える機能を提供する。ストリームは、第１クラスのオブジェクト、すなわち変数の地位にほぼ匹敵する地位へと高められている。これは、ストリームが識別子と結合され得る（すなわち、ストリームに名称が与えられ得る）こと、関数の入力パラメータ（すなわちモジュールの入力パラメータ）と結合され得ること、関数の出力（すなわち、モジュールの入力パラメータ）と結合され得ること、式中のパラメータと結合され得ること、および式の出力と結合され得ることを意味する。
【００６９】
ストリームは、単一のデータ型の値を、１つまたは複数のストリームソースから１つまたは複数のストリームデスティネーションへと伝える。この運搬がどのように遂行されるかについての正確な詳細は、実装に依存し、とりわけ、ストリームが単一の半導体ダイに限定されるかどうか、またはストリームが数メートルまたあるいは数千キロメートルにわたるかどうかに依存する。性能問題に対処する場合を除き、プログラマはこれらの詳細を考慮する必要はなく、４つのストリーム属性、すなわち、ストリーム型、ストリーム名、ストリームソース、およびストリームデスティネーションに関する、ストリームのこれらの側面のみを考慮すればよい。
【００７０】
ストリーム型は、伝えられる値の型を示す。ポインタおよびｔｙｐｅｄｅｆにより定義されるデータ型を含む、Ｃの正当なデータ型であり得るストリーム型は、例えば、モジュール入力または出力パラメータとして現れることにより文脈により暗黙的に特定され得るか、または以下に説明するストリーム宣言を用いて明示的に特定され得る。
【００７１】
ストリームソースは、値がストリームに置かれる位置である。可能なストリームソースは、モジュール定義の入力パラメータ、モジュールインスタンス化の出力、ストリーム式の出力、およびスレッド（以下に説明する）を含む。ストリームデスティネーションは、ストリームがそのポイントへと値を伝える位置である。可能なストリームデスティネーションは、モジュール定義の出力パラメータ、モジュールインスタンス化の入力引数、ストリーム式の入力、およびスレッドを含む。省略可能なストリーム名は、ストリームがモジュール入力またはモジュール出力として現れるときに、またはストリームがストリーム宣言において導入されるときに、ストリームに割り当てられる名称／識別子である。名称を有さないストリームの１つの例は、ストリーム割り当てにより名称が割り当てられていないストリーム式の出力ストリームである。
【００７２】
ストリーム属性の概念は、関数Ｆの宣言およびモジュールＭの部分的な定義を含む、以下の部分的コードにより示される。
ｓｔｒｅａｍ  ｉｎｔ  Ｆ（ｉｎｔ，  ｉｎｔ）；
ｓｔｒｅａｍ  （ｉｎｔ  ｚＳｔｒｍ）  Ｍ（ｉｎｔ  ｘＳｔｒｍ，  ｉｎｔ  ｙＳｔｒｍ）
｛
・
・
・
ｚＳｔｒｍ  ＝  ｘＳｔｒｍ＊ｙＳｔｒｍ＋Ｆ（ｘＳｔｒｍ，  ｙＳｔｒｍ）；
・
・
・
｝
【００７３】
ここでは、名称を有する３つのストリーム、すなわちｘＳｔｒｍ、ｙＳｔｒｍ、およびｚＳｔｒｍが存在し、これらすべては、ｉｎｔのデータ型である。ｘＳｔｒｍおよびｙＳｔｒｍは、それぞれが、モジュールＭの入力パラメータである単一のソースを有する。ｘＳｔｒｍおよびｙＳｔｒｍのデスティネーションは、それぞれ、Ｍの本体における代入式に現れる、ｘＳｔｒｍおよびｙＳｔｒｍの２つのインスタンスにより表される（Ｃにおいては、代入も式であることを想起されたい）。これらのインスタンスは、代入式に対する入力を表す。したがって、ｘＳｔｒｍおよびｙＳｔｒｍは、それぞれが単一のソースおよび２つのデスティネーションを有する。
【００７４】
ストリームの式は、ストリームの式においては変数の代わりに入力ストリームが存在することを除いて、Ｃの式と同じである。ストリーム式は出力ストリームも有し、出力ストリームは、式評価からの結果を伝える。デフォルトにより、出力ストリームは名称を有さないが、ちょうど上記の代入で行ったように、ストリーム代入を用いることにより、名称を割り当てることが可能である。したがって、ストリーム式
ｘＳｔｒｍ＊ｙＳｔｒｍ  ＋  Ｆ（ｘＳｔｒｍ，  ｙＳｔｒｍ）
の出力ストリームは、ストリーム代入
ｚＳｔｒｍ＝ｘＳｔｒｍ＊ｙＳｔｒｍ  ＋  Ｆ（ｘＳｔｒｍ，  ｙＳｔｒｍ）
により、名称ｚＳｔｒｍが割り当てられる。これら２つの式のうちのいずれかが、ｚＳｔｒｍのソースであるとみなされ得る。ｚＳｔｒｍのデスティネーションは、モジュールＭの出力パラメータｚＳｔｒｍにより示されるモジュールＭの出力ストリームである。
ｓｔｒｅａｍ  （ｉｎｔ  ｚＳｔｒｍ）  Ｍ（ｉｎｔ  ｘＳｔｒｍ，  ｉｎｔ  ｙＳｔｒｍ）
したがって、ｘＳｔｒｍは単一のソースおよび単一のデスティネーションを有する。
【００７５】
ストリームの最も重要な属性は、ストリームが値を伝える際に果たす役割に関する。係る属性は４つ存在する。すなわち、ａ）値は、ストリームソースにおけるか、またはｉｎｉｔｉａｌｉｚｅ（）関数を使用するシステム初期化時以外は、ストリームに入らない、ｂ）単一のソースにおいてストリームに入る値は、完全に時間順に整列される、ｃ）ひとたびストリームに入ると、値はやがてはすべてのストリームデスティネーションに送達され、もしデスティネーションが複数存在する場合は、値の別個の複写が各デスティネーションに送達される、およびｄ）単一ソースからの値は、値がストリームに入ったのと同一の順番で（すなわち、ストリームにおいて値の飛び越しは存在しない）、各ストリームデスティネーションにおいて受け取られる。これらの４つの属性が、ストリームが値の運搬について提供する唯一の保証である。これら４つの属性から論理的帰結として従わない任意の他の属性は、一般的なストリーム属性ではない。
【００７６】
ストリームが義務を負うのは、ただ最終的に値を送達することのみである。したがって、ストリームのレイテンシ、すなわち値がストリームソースからストリームデスティネーションへと移動するに要する時間は不定である。事実、レイテンシは、時間に応じて、または同じストリームのソース・デスティネーション対の間で、変動し得る。しかし、レイテンシが一定であるかまたは少なくとも限界を有することは、（プログラミングモデルよりもむしろ）システム実装により提供される保証に依存することにより達成され得る。例えば単一半導体ダイに限定されるソース・デスティネーション対は、通常、そのレイテンシは限界を有する。
【００７７】
また、上述の４つの属性は、ストリームの決定性および非決定性（不確定性）を暗示する。単一ソースを有するストリームに対しては、４つの属性は決定的なストリーム挙動を保証する。それは、値が単一ソースストリームに置かれる順番が、値がすべてのストリームデスティネーションに送達される順番を完全に決定することを意味する。しかし、複数のソースを有するストリームの場合、状況は大きく異なる。複数のストリームソースから生じる問題を示すために、以前のセクションに継続する以下の部分的コードの以下の適用を考慮してみる（ｏｕｔは単一出力モジュールのデフォルト出力ストリームである）。
ｉｎｔ  Ｆ（ｉｎｔ）；
ｓｔｒｅａｍ  ｉｎｔ  Ｍ（ｉｎｔ  ｘＳｔｒｍ，  ｉｎｔ  ｘＳｔｒｍ）
｛
・
・
・
ｏｕｔ  ＝  ｘＳｔｒｍ＊ｘＳｔｒｍ  ＋  Ｆ（ｘＳｔｒｍ）；
・
・
・
｝
【００７８】
モジュールＭの２つの入力パラメータは、同一のｘＳｔｒｍである。４つの属性から、モジュールＭの第１の入力パラメータを通してｘＳｔｒｍに入った値は、ｘＳｔｒｍの３つのデスティネーションのそれぞれにおいて、これらの値がストリームに入ったのと同じ順序で受け取られることとなる。モジュールＭの第２の入力パラメータを通してｘＳｔｒｍに入った値は、ｘＳｔｒｍの３つのデスティネーションのそれぞれにおいて、これらの値がストリームに入ったのと同じ順序で受け取られることとなる。このことは、２つのストリームの値は、ｘＳｔｒｍのそれぞれのデスティネーションに到達する前に、合併または交互配置されることを意味する。
【００７９】
どのように交互配置が実行されるかは、一般に、プログラムの構成により影響される。例えば、上記のプログラムの欠落部分は、パラメータ１とパラメータ２との値の間で正確な交互配置がされるように構成され得る。例えば、モジュールＭの２つの入力パラメータ（ストリーム）ｘＳｔｒｍに到達する整数が、次の順序
【００８０】
【表１】
0005990466.tif 000002
【００８１】
を有する場合、式
ｏｕｔ  ＝  ｘＳｔｒｍ＊ｘＳｔｒｍ  ＋  Ｆ（ｘＳｔｒｍ）
のｘＳｔｒｍの３つのデスティネーションのそれぞれに到着する順序は、
【００８２】
【表２】
0005990466.tif 000003
【００８３】
という形になり得る。しかしながら、このようにプログラムにより課される決定性がいつも成り立つとは限らず、複数のストリームソースからの値が非決定的に交互配置される場合もある。さらに、ターゲットシステムによっては、これらの非決定的交互配置が、ストリームデスティネーション毎に異なり得る。したがって、例えば、モジュールＭの２つの入力パラメータ（ストリーム）上に到達する値が上記の場合と同じである場合、ｘＳｔｒｍの３つのデスティネーションに到達する順序は、
【００８４】
【表３】
0005990466.tif 000004
【００８５】
という形で始まり得る。複数ソースストリームのデスティネーションにおける到着順序の非決定性は、単一ソースストリームのすべてのデスティネーションにわたる到着順序の一定性と対比的である。到着順序が一定である場合、以下の有用な表記を適用することが可能となる。単一ソースストリームｓｓＳｔｒｍおよび非負である整数ｉに対して、
ｓｓＳｔｒｍ  （ｉ）
はｓｓＳｔｒｍのすべてのデスティネーションに現れる第ｉ番目の値を示す。慣例的に、ｓｓＳｔｒｍ（０）はすべてのデスティネーションに現れる第１の値を示す。
【００８６】
値がストリームデスティネーションに到達すると、そのデスティネーションがモジュール定義またはモジュールインスタンス化入力引数である場合、その値は、モジュール境界の他方の側におけるストリームへと渡される。このように値は、移行状態に留まる。デスティネーションがストリーム式の入力またはスレッドである場合、値はＦＩＦＯキューに置かれる。
【００８７】
移行状態に留まることを示すために、以下の部分的コードを示す。
ｓｔｒｅａｍ  ｉｎｔ  ｍｏｄｕｌｅ１  （ｉｎｔ）；
ｓｔｒｅａｍ  ｉｎｔ  ｍｏｄｕｌｅ２  （ｉｎｔ  ｘＳｔｒｍ）
｛
・
・
・
ｏｕｔ  ＝  ｍｏｄｕｌｅｌ１  （ｘＳｔｒｍ）；
・
・
・
｝
この部分的コードは、２つのモジュール、すなわちｍｏｄｕｌｅ１およびｍｏｄｕｌｅ２を含み、これら２つのモジュールのそれぞれは、単一の入力ストリームおよび単一の出力ストリームと、２つの名称を有するストリームｘＳｔｒｍおよびｙＳｔｒｍ（両方がｍｏｄｕｌｅ２の定義（本体）内に存在する）とを有する。ｘＳｔｒｍの唯一のデスティネーションすなわちｍｏｄｕｌｅ１（ｘＳｔｒｍ）は、ｍｏｄｕｌｅ１のインスタンス化の入力引数である。このデスティネーションに到達する値は、ｍｏｄｕｌｅ１の境界を単に通過し、ｍｏｄｕｌｅ１の内部ストリームに達する。この状況は、ｙＳｔｒｍの唯一のデスティネーション
ｓｔｒｅａｍ  （ｉｎｔ  ｙＳｔｒｍ）  ｍｏｄｕｌｅ２  （ｉｎｔ  ｘＳｔｒｍ）
に到達する値に対しても同じである。なぜなら、このデスティネーションはｍｏｄｕｌｅ２の出力パラメータであるため、到達する値はｍｏｄｕｌｅ２の境界を単に通過して、ｍｏｄｕｌｅ２の外部にあるストリームに達する。
【００８８】
他の例は、ストリームデスティネーションが以下の部分的コード等のストリーム式の入力である場合である。
ｓｔｒｅａｍ  ｉｎｔ  Ｆ  （ｉｎｔ，  ｉｎｔ）；
ｓｔｒｅａｍ  ｉｎｔ  Ｍ（ｉｎｔ  ｘＳｔｒｍ，  ｉｎｔ  ｙＳｔｒｍ）
｛
・
・
・
ｏｕｔ  ＝  ｘＳｔｒｍ＊ｙＳｔｒｍ  ＋  Ｆ（ｘＳｔｒｍ，  ｙＳｔｒｍ）；
・
・
・
｝
ストリーム式
ｘＳｔｒｍ＊ｙＳｔｒｍ  ＋  Ｆ（ｘＳｔｒｍ，  ｙＳｔｒｍ）
は、モジュールＭの本体内にあり、このストリーム式は、ｘＳｔｒｍの２つのデスティネーションと、ｙＳｔｒｍの２つのデスティネーションとを含む。このストリーム式は２つの演算子＊および＋と、関数Ｆとを含み、これらは普通のＣ構文である。このことは、この式を評価するためには、２つの演算子および関数Ｆが個々の値を供給されるべきであることを意味する。
【００８９】
キューはストリームＣのリンカ／ローダにより自動的に挿入され、ストリームＣのランタイムにより管理される。ランタイムの任務の中には、キューが空き状態にあるときに信号を発すること、およびどのキューも確実にオーバーフローしないようにすることである。プログラマは特定の量を以下で説明するようにｐｒａｇｍａコマンドにより要求し得るが、各キューは関連するデータ型の少なくとも２つの値の容量を有することが保証される。この例のストリームにおいて、４つのキュー、すなわち４つのストリームデスティネーション（ストリーム式入力）のそれぞれに対して１つのキュー、が存在する。これらのキューは、プログラマに対しては大半が不可視である。
【００９０】
ひとたびストリームＣプログラムが実行（動作）を開始すると、値がストリームに入る唯一の方法は、ストリームソースによる。より多くのストリームのうちの１つは、すでにストリーム中に含まれている値を要求する、有向サイクル（ｄｉｒｅｃｔｅｄ  ｃｙｃｌｅ）を形成し得る。最も簡単な係るサイクルは、
ｘＳｔｒｍ  ＝  ｘＳｔｒｍ＋ｙＳｔｒｍ
と等価である
ｘＳｔｒｍ  ＋＝  ｙＳｔｒｍ
におけるように、ストリーム代入の両辺にストリームが現れるときに生じる。
【００９１】
図９Ａはこの代入の第１の図的表現４００である。なお、この代入においては、有向サイクルが、＋演算子の出力から同一の＋演算子の２つの入力のうちの１つに向かうフィードバック経路からなる。＋演算子が各入力ストリームからの値を消費して出力ストリーム上において値を作成することができないのは、この経路上において値が欠落しているためである。ゆえに、第２の図的表現４０２に示すように、値４０４が、実行の開始前に、フィードバック経路上に置かれない限り、＋演算子は決して始動することがない。
【００９２】
他の問題は、１つの単一ソースストリームの、他の単一ソースストリームに対するオフセットの変化に関する。例えば、ａＳｔｒｍおよびｂＳｔｒｍの両方が、図９Ｂに図的に表現される、
ａＳｔｒｍ＋ｂＳｔｒｍ
等の同一のモジュールまたはストリーム式に対する入力であり、そのモジュールまたは式が、これらのストリームから、対の形で、すなわちａＳｔｒｍから１つの値およびｂＳｔｒｍから１つの値を消費する場合。ａＳｔｒｍ（ｎ）（すなわち、ａＳｔｒｍ上に第ｎ番目に到達する値）が、ｂＳｔｒｍ（ｎ＋２）（すなわち、ｂＳｔｒｍ上に第ｎ＋２番目に到達する値）と対になることが望まれる場合。したがって、ａＳｔｒｍ（０）はｂＳｔｒｍ（２）と、ａＳｔｒｍ（１）はｂＳｔｒｍ（３）と（以下同様）対となる。
【００９３】
両方の問題に対するソリューンは、
＜ストリーム識別子＞．ｉｎｉｔｉａｌｉｚｅ（＜値リスト＞）；
の形を取る、ストリーム初期化文により提供される。
【００９４】
ストリームＣコンパイラ／リンカ／ローダがこの文と遭遇すると、ストリームＣコンパイラ／リンカ／ローダは、ＦＩＦＯキューを＜ストリーム識別子＞の各デスティネーションに挿入すること（デスティネーションがモジュール定義の出力パラメータである場合も、ストリーム文またはスレッドの入力引数である場合も）と、そのキューのサイズを、Ｔデータ型の少なくともｎ＋１個の値を保持することができるよう設定すること（ただし、ｎは＜値リスト＞中の値の個数であり、Ｔは＜ストリーム識別子＞のデータ型である）と、＜値リスト＞中の値を、＜値リスト＞中の第１番目の値がキューの前部（頭部）に置かれる状態の順序でキューに置くこととを指示するものとして、この文を解釈する。
【００９５】
例えば、図９Ａにおいて、デッドロックを防ぐために、図的表現４０２において、値４０４が、フィードバック経路に、および、式出力にも、
ｘＳｔｒｍ．ｉｎｉｔｉａｌｉｚｅ（０）；
により挿入される。この文により、２つのＦＩＦＯキューが、ｘＳｔｒｍの各デスティネーションに対して、作成される（フィードバック経路のデスティネーションにおけるキューは、前のセクションで説明したように、すでに挿入されている）。ｘＳｔｒｍがｉｎｔ型であると仮定すると、各ストリームのサイズは少なくとも２×ｓｉｚｅｏｆ（ｉｎｔ）であり、システム初期化時においてｉｎｔ値０が各キューの先頭に位置する。このことは、図９Ａにおけるフローチャート４０２において図示される。このようにｘＳｔｒｍが初期化され、代入ｘＳｔｒｍ  ＋＝  ｙＳｔｒｍの出力は、
【００９６】
【表４】
0005990466.tif 000005
【００９７】
となる。図９Ｂにおける第２の図的表現４１２におけるｂＳｔｒｍに対するａＳｔｒｍのオフセットを変えることは、同様の方法で対処される。しかし、ここでは、２つの値がａＳｔｒｍのＦＩＦＯキューに挿入される。なぜなら、ｂＳｔｒｍに対して２つの値によりａＳｔｒｍをオフセットすることが望まれ得るためである。このことは、１および２が、システム初期化時にａＳｔｒｍのキューに挿入される２つの値４１４として選択された、以下のストリーム初期化文
ａＳｔｒｍ．ｉｎｉｔｉａｌｉｚｅ（１，  ２）；
を用いることにより達成される。この初期化の結果は図９Ｂにおける表現４１２に図示される。このようにｘＳｔｒｍが初期化され、代入ｘＳｔｒｍ  ＋＝  ｙＳｔｒｍの出力に現れる値は、
【００９８】
【表５】
0005990466.tif 000006
【００９９】
となる。
【０１００】
Ｃ変数の場合と同様に、特定のストリーム宣言は文脈上から、例えば、モジュール入力または出力パラメータとして現れることにより、暗黙的になされるが、すべてのストリームは使用の前に宣言されなければならない。明示的なストリーム宣言のシンタックスは、Ｃ変数宣言のシンタックスに従うが、この場合の宣言はキーワードｓｔｒｅａｍで始まる。
ｓｔｒｅａｍ  ＜記憶クラス識別子＞省略可能＜型＞＜識別子リスト＞；
記憶クラス識別子を有さないストリーム宣言の例を下にいくつか挙げる。
ｓｔｒｅａｍ  ｉｎｔ  ｘＳｔｒｍ，  ｙＳｔｒｍ；
ｓｔｒｅａｍ  ｃｈａｒ  ｃＳｔｒｍ；
ｓｔｒｅａｍ  ｄｏｕｂｌｅ  ｄＳｔｒｍ；
５つのＣにおける記憶クラス識別子、すなわち、ａｕｔｏ、ｒｅｇｉｓｔｅｒ、ｓｔａｔｉｃ、ｅｘｔｅｒｎ、およびｔｙｐｅｄｅｆのうち、ストリーム宣言においては、
ｓｔｒｅａｍ  ｓｔａｔｉｃ  ｉｎｔ  ｘＳｔｒｍ，  ｙＳｔｒｍ；
のように、ｓｔａｔｉｃのみが許可される。
【０１０１】
ｓｔａｔｉｃならびに非ｓｔａｔｉｃのストリーム宣言は、宣言が現れる文脈により決定される。係る文脈は３つ存在し、それぞれが、それぞれのスコープ規則を有する。それぞれの場合において、ストリーム宣言のスコープ規則は、変数宣言の対応物のスコープ規則と同じである。記憶クラス識別子を有さず且つモジュールの内部に現れるストリーム宣言に対しては、宣言スコープは、宣言からモジュールの終わりまで及ぶ。記憶クラス識別子を有さず且つモジュール（および関数）の外部に現れるストリーム宣言に対しては、宣言スコープはグローバルであり、すなわち、プログラム全体に対して可視である。ｓｔａｔｉｃ記憶クラス識別子を有し且つすべてのモジュール（および関数）の外部に現れるストリーム宣言に対しては、その宣言のスコープは、宣言から、宣言が現れるソースファイルの終わりまで及ぶ。
【０１０２】
変数には関係するがストリームには関係しない記憶クラス識別子に関するいくつかの宣言形態は、このリストに現れない。Ｃにおいては、ａｕｔｏ記憶クラス識別子を用いて宣言された、またはまったく識別子を用いないで宣言された、これらの変数は、関数呼び出しの間では値を失う。ストリームはモジュール内においてのみ作用し、モジュールは呼び出されないため（モジュールは常に動作状態にある）、自動ストリームはそもそも無意味な概念である。したがって、ａｕｔｏ記憶クラス識別子はストリーム宣言に適用されない。
【０１０３】
ｓｔａｔｉｃ識別子を用いて宣言され且つ関数の内部に現れる変数宣言は、宣言された変数が関数コール（関数呼び出し）間においてその値を保持することを示す。しかしモジュールの場合には、コールの概念が存在せず、したがって、ｓｔａｔｉｃ識別子はモジュール内部では無意味である。したがって、ｓｔａｔｉｃ識別子はモジュールスコープ内では用いられない。
【０１０４】
変数宣言に対して、ｅｘｔｅｒｎ記憶クラス識別子は、宣言および定義として働くグローバル変数のこれらの宣言と、単に宣言として働くグローバル変数の宣言とを区別することを支援する。しかしストリームの場合には、ストリーム宣言において記憶領域が決して除外されないため、宣言は決して定義にはならない。記憶領域は、以下のストリームＦＩＦＯのセクションにおいて説明されるように、ストリーム定義時においてのみ割り当てられる。ｒｅｇｉｓｔｅｒおよびｔｙｐｅｄｅｆの記憶クラス識別子は、ストリームにおいてはまったく妥当性がなく、ストリーム宣言において現れることがない。
【０１０５】
ストリーム式は、通常のＣ式に対するストリームにおける対応物である。すべての変数に対して入力ストリームが取って代わること、および結果に対して出力ストリームが取って代わることは別として、これら２つの種類の式は極めて類似性が高い。式においては、変数と定数とが組み合わされて新しい値が作成されるが、一方、ストリーム式においては、ストリームと定数とが組み合わされて、新しいストリームが作られる。Ｃ式とストリーム式の構造はほぼ同一である。すべてのＣ演算子は、ストリーム式において有効な演算子である。同じ演算子の優先度が、Ｃ式とストリーム式との両方に当てはまる。Ｃ関数コールは、ちょうどＣ式で認められるのと同様に、ストリーム式においても認められる。単一の出力ストリームを有するモジュールのインスタンス化はストリーム式において認められ、関数コールと同様に取り扱われる。
【０１０６】
Ｃ式とストリーム式との間の相違点は、第１に、評価が行われる時点および方法にある。Ｃ式については、制御のスレッドが、式を含む文に到達した時点で、評価が行われる。その評価は、最初に、各変数をその現時点での値に置き換え、次いで、演算子の優先度にしたがって必要な演算を行うことにより、行われる。次いで、最後の演算により返される値が評価結果として供給される。
【０１０７】
Ｃ式の評価とは異なり、Ｃストリームプログラム言語におけるストリーム式の評価は、制御のスレッドには縛られない。代わって、ストリーム式は、便宜主義的に評価される。従来のように、評価は、演算子の優先度にしたがって必要な演算を行うことにより行われる。変数に対して値を置き換える代わりに、値は、式入力に属する各ＦＩＦＯキューから消費（ポップ）される。ＦＩＦＯキューは、ストリーム式の入力であるすべてのストリームデスティネーションにおいて挿入される。評価は便宜主義的である。なぜなら、式の各入力ＦＩＦＯキューに少なくとも１つの値が存在するときは常に評価が行われるためである。従来のように、評価により作られる結果は、最後の演算により返された値である。しかし結果は、Ｃ式の場合とは異なる方法で対処される。Ｃ式に対しては、結果が代入される用法は、式の文脈により決定される。ストリーム式に対しては、結果は式の出力ストリーム（式が代入かどうかに応じて、名称を持つ場合も持たない場合もある）に単に代入される。
【０１０８】
ストリーム式の１例が、ｘＳｔｒｍ、ｙＳｔｒｍ、およびｚＳｔｒｍがすべてｉｎｔ型のストリームである、以下の式
ｘＳｔｒｍ＊ｙＳｔｒｍ  ＋  ５＊ｚＳｔｒｍ
において示され得る。これら３つのストリームに到達する値は、次のように始まる。
【０１０９】
【表６】
0005990466.tif 000007
【０１１０】
すると、ｘＳｔｒｍ＊ｙＳｔｒｍ  ＋  ５＊ｚＳｔｒｍの（名称を有さない）出力ストリームに代入される最初の３つの値は、以下のようになる。
【０１１１】
【表７】
0005990466.tif 000008
【０１１２】
ストリーム式の中では、特にストリーム代入が注目される。係るストリーム代入には２つの型が存在し、第１の型は
＜ストリーム識別子＞＝＜ストリーム式＞
の形を有する。
【０１１３】
そのＣにおける対応物、すなわち変数への代入と同様に、この型のストリーム代入は副作用を有する。その出力ストリームに値を供給することに加えて、ストリーム代入は、右辺（ＲＨＳ：ｒｉｇｈｔ−ｈａｎｄ−ｓｉｄｅ）式の出力を左辺（ＬＨＳ：ｌｅｆｔ−ｈａｎｄ−ｓｉｄｅ）のソースにし、そのプロセス中に、ＲＨＳ式の出力ストリームを代入の出力ストリームとする。ストリーム代入は、ＲＨＳ式の出力ストリームが名称を有さない場合は、その出力ストリームに名称も与える。より大きい式の部分式の出力ストリームに名称は必要ではないが、名称は、出力ストリームが任意の包含するｓｕｐｅｒ式（ｓｕｐｅｒｅｘｐｒｅｓｓｉｏｎ）の外側のデスティネーションに宛てられなければならない場合は、不可欠となる。
【０１１４】
以下の部分的コートにおける式代入文は１つの例である。ストリーム式は、代入の場合もそれ以外の場合も、セミコロンが後尾に付されるとストリーム文となる。
ｉｎｔ  Ｆ（ｉｎｔ，  ｉｎｔ）；
ｉｎｔ  Ｇ（ｉｎｔ）；
ｓｔｒｅａｍ  ｉｎｔ  Ｍ（ｉｎｔ  ｘＳｔｒｍ，  ｉｎｔ  ｙＳｔｒｍ）
｛
．
．
．
ｏｕｔ  ＝  Ｆ（ｘＳｔｒｍ，  Ｇ（ｙＳｔｒｍ））；
．
．
．
｝
式Ｆ（ｘＳｔｒｍ，  Ｇ（ｙＳｔｒｍ））および部分式Ｇ（ｙＳｔｒｍ）は、それぞれ、ストリーム文をストリーム式として有する。Ｇ（ｙＳｔｒｍ）の場合、出力ストリームは名称を有さない。なぜなら、ストリームのデスティネーションは、式の文脈から明らかであるためである。すなわち、デスティネーションはｓｕｐｅｒ式Ｆ（ｘＳｔｒｍ，  Ｇ（ｙＳｔｒｍ））における関数Ｆの第２の入力引数である。しかし、Ｆ（ｘＳｔｒｍ，  Ｇ（ｙＳｔｒｍ））の出力ストリームの場合は名称が必要となる。なぜなら、デスティネーションが式の外部にあるためである。その名称は、代入式
ｏｕｔ  ＝  Ｆ（ｘＳｔｒｍ，  Ｇ（ｙＳｔｒｍ））
において割り当てられる。この代入により、Ｆ（ｘＳｔｒｍ，  Ｇ（ｙＳｔｒｍ））の出力はｚＳｔｒｍのソースとなり、ｚＳｔｒｍは単一のデスティネーション、モジュールＭの出力パラメータを有する。
【０１１５】
ストリーム代入の第２の型は、
（＜コンマで分割されたストリーム識別子のリスト＞）＝＜モジュールインスタンス化＞
の形を取る。この型は、複数出力モジュールの出力を複数の名称を有するストリームのソースにすることが望まれるときに生じる。例示すると、ｔａｐの第１の出力がｉｎｔストリームｘのソースであり、ｔａｐの第２の出力がｉｎｔストリームｙのソースである場合における、以下の複数出力モジュール
ｓｔｒｅａｍ  （ｉｎｔ，  ｉｎｔ）  ｔａｐ（ｉｎｔ，  ｉｎｔ，  ｉｎｔ）；
である。これは、ストリーム代入
（ｉｎｔ  ｘ，  ｉｎｔ  ｙ）  ＝  ｔａｐ（ａｒｇ１，  ａｒｇ２，  ａｒｇ３）；
を用いて達成される。この代入により、モジュールのｉ番目の出力はｉ番目のストリームのソースとなり、モジュールの出力ストリームが名称を有さない場合には、その出力ストリームに名称が与えられる。
【０１１６】
モジュール本体内の文は、２つのカテゴリー、すなわち、スレッドおよびストリームに分類される。ストリーム文はストリームを取り扱うが、変数は取り扱わない。スレッド文は変数を取り扱い、いくつかの場合においては、ストリームも取り扱う。スレッドドメインにおける文は、大部分がＣ文であり、Ｃ文と同様に、本質的に命令的（手続的）であって、ステップ毎の手順を定義する。制御の逐次フロー（多くの場合、スレッドと称される）は、係る手順と関連付けられ、文が実行される順序を支配する。それに対して、ストリーム文は宣言型である。係る文のそれぞれは、文中に現れるストリームについて宣言する。スレッドドメインにおいて存在するようなステップ毎の手順の概念は存在せず、したがって、モジュール本体内におけるストリーム文の順序は、１つの例外を除いて、重要ではない。変数は使用前に宣言される必要があるように、ストリームもまた使用前に宣言されなければならない。
【０１１７】
ストリームドメインの性質により、制御フローを取り扱うこれらのＣ文、特に、ｉｆ−ｅｌｓｅ、ｅｌｓｅ−ｉｆ、ｓｗｉｔｃｈ、ｆｏｒ、ｗｈｉｌｅ、ｄｏ−ｗｈｉｌｅ、ｂｒｅａｋ、ｃｏｎｔｉｎｕｅ、ｇｏｔｏ、およびｒｅｔｕｒｎに対する対応物は存在しない。事実、ストリームドメインにおける唯一の文型は、Ｃ式文に対するストリームにおける対応物であり、Ｃにおけるように、最も一般的な式文は代入文である。ストリーム式文は、次の２つの形
＜ストリーム式＞；
ｓｔｒｅａｍ  ＜ストリーム式＞；
のうちの１つを有する。一方、ストリーム代入文は、次の２つの形
＜ストリーム識別子＞＝＜ストリーム式＞；
ｓｔｒｅａｍ＜ストリーム識別子＞＝＜ストリーム式＞；
（＜コンマで分割されたストリーム識別子のリスト＞）＝＜モジュールインスタンス化＞
のうちの１つを有する。
【０１１８】
モジュール、ストリームインスタンス化、ストリーム宣言、ストリーム式、およびストリーム文を使用する用途例は、デジタル信号処理において一般に使用される構文である有限インパルス応答（ＦＩＲ：ｆｉｎｉｔｅ−ｉｍｐｕｌｓｅ−ｒｅｓｐｏｎｓｅ）フィルタである。ＦＩＲフィルタは、離散型時間入力信号を離散型時間出力信号へと変換する。図１０は、５タップＦＩＲフィルタ５００の図であり、図中、Ｘ（ｚ）は離散型時間入力５０２を、Ｙ（ｚ）は離散型時間出力５０４を表す。ｚ−１と表記される、一連の単位遅延５０６は、それぞれが、受信する離散型時間信号を１クロックサイクルだけ遅延させる。それぞれが、受信する離散型時間信号に対して一定の係数ｈ（ｉ）を乗算する、一連の乗算器５０８。最後に、それぞれが、２つの受信信号を加算する、Σと表記された一連の加算器５１０。フィルタ５００は５タップフィルタと称される。なぜなら、受信する離散型時間信号の５つの遅延されたバージョンのそれぞれが別個の係数を乗算され、結果として、５つの結果として生じた積が加算されるためである。
【０１１９】
離散型時間信号は、サンプルのストリームとして表される。乗算器５０８および加算器５１０のそれぞれはストリーム式として表される。単位遅延はストリームインスタンス化として表される。１つまたは複数の値を有するストリームを初期化することにより、値は、そのストリーム内で、第２のストリーム内の値に対して、オフセット（遅延）される。これは、ＵｎｉｔＤｅｌａｙモジュールの動作の基礎となる原理である。
ｓｔｒｅａｍ  ｉｎｔ  ＵｎｉｔＤｅｌａｙ（ｉｎｔ  Ｘ）
｛
ｏｕｔ  ＝  Ｘ；
ｏｕｔ．ｉｎｉｔｉａｌｉｚｅ  （０）；
｝
ＵｎｉｔＤｅｌａｙの本体において、ストリーム代入文
ｏｕｔ  ＝  Ｘ；
により、ＵｎｉｔＤｅｌａｙの入力ストリームであるＸは、ＵｎｉｔＤｅｌａｙのデフォルト出力ストリームであるｏｕｔのソースとなる。一方、ストリーム初期化文
ｏｕｔ．ｉｎｉｔｉａｌｉｚｅ（０）；
は、システム初期化時に、値０をｏｕｔに挿入する。ｏｕｔにおけるこの初期値は、ｏｕｔにおける後続のすべての値を１つの値分だけオフセット（遅延）させる効果を有する。
【０１２０】
以下は、１０、２０、３０、４０、および５０を任意に選択された５つのフィルタ係数として含む、図１０におけるフィルタ５００等の５タップＦＩＲフィルタのストリームＣ実装である。
ｓｔｒｅａｍ  ｉｎｔ  ＵｎｉｔＤｅｌａｙ  （ｉｎｔ  Ｘ）
｛
ｏｕｔ  ＝  Ｘ；
ｏｕｔ．ｉｎｉｔｉａｌｉｚｅ（０）；
｝
ｓｔｒｅａｍ  （ｉｎｔ  ｘＯｕｔ，  ｉｎｔ  ｙＯｕｔ）  ｔａｐ  （ｉｎｔ  ｘＩｎ，  ｉｎｔ  ｙＩｎ，  ｉｎｔ  ｈ）
｛
ｘＯｕｔ  ＝  ＵｎｉｔＤｅｌａｙ（ｘＩｎ）；
ｙＯｕｔ  ＝  ｙＩｎ  ＋  ｈ＊ｘＯｕｔ；
｝
ｓｔｒｅａｍ  ｉｎｔ  ＦＩＲ５（ｉｎｔ  Ｘ）
｛
（ｉｎｔ  ｘ２，  ｉｎｔ  ｙ２）  ＝  ｔａｐ  （Ｘ，  １０＊Ｘ，  ２０）；
（ｉｎｔ  ｘ３，  ｉｎｔ  ｙ３）  ＝  ｔａｐ  （ｘ２，  ｙ２，  ３０）；
（ｉｎｔ  ｘ４，  ｉｎｔ  ｙ４）  ＝  ｔａｐ  （ｘ３，  ｙ３，  ４０）；
（ｉｎｔ  ，  ｏｕｔ）  ＝  ｔａｐ（ｘ４，  ｙ４，  ５０）；
｝
【０１２１】
この実装は並列性を示すが、明示的な並列性構文を用いることなく並列性が示されている。複数の名称を有するｔａｐの出力の他は通常の逐次コードと類似するコードから、この並列性は現れたものである。変数に代わって、ここではストリームが存在する。
【０１２２】
ＦＩＲ５の本体内におけるｔａｐの４つインスタンス化のそれぞれは、式
ｙＩｎ  ＋  ｈ＊ｘＯｕｔ
の、それ自身の複写を、３つの他のｔａｐのインスタンス化と並列的に演算する。このことは、ストリーム式の便宜主義的な性質により、および新しい入力値がｔａｐのインスタンス化のそれぞれへと継続的に到着することにより、可能となる。これらの新しい値は、ＦＩＲ５の５つの内部ストリームにより供給される。
Ｘは、ＦＩＲ５の入力から第１のｔａｐの入力へと値を伝える。
ｘ２およびｙ２は、第１のｔａｐの出力から第２のｔａｐの入力へと値を伝える。
ｘ３およびｙ３は、第２のｔａｐの出力から第３のｔａｐの入力へと値を伝える。
ｘ４およびｙ４は、第３のｔａｐの出力から第４のｔａｐの入力へと値を伝える。
ｔａｐの各インスタンス化の入力ｈは、定数により置き換えられる。このことにより、ストリームＣコンパイラはｔａｐインスタンス化内のｈのすべてのインスタンスを定数で置き換える。ｔａｐのインスタンス化により実行されるすべての演算により、ＦＩＲ５入力値はＦＩＲ５出力値へと変換される。
これらの最後の出力値が、ＦＩＲ５のデフォルト出力ストリームにより供給される。
ｏｕｔは、第４のｔａｐの出力からＦＩＲ５の出力へと値を伝える。
この実装は、多数のデジタル信号処理関数がどのようにストリームＣにおいて取り扱われるかの１つの例である。
【０１２３】
上述のＦＩＲフィルタ例においては、５つの係数すなわち１０、２０、３０、４０、５０が、コンパイル時に既知である。しかしＦＩＲ５係数がコンパイル時に未知である場合、または係数が長い期間にわたって一定であるが、随時変更される場合は、他の技術を用いる必要がある。係る場合においては、これらの擬似的定数は、変化するため真の定数ではなく、また、ストリーム式またはスレッドにより消費（ＦＩＦＯキューからポップ）されないため、真のストリームではない。
【０１２４】
擬似的定数ストリームは、いくつかの面において通常のストリームに類似する。擬似的定数ストリームは、型と、１つまたは複数のソースと、１つまたは複数のデスティネーションと、名称とを有する。擬似的定数ストリームは、指定されたソースから指定されたデスティネーションへと指定された型を伝える。しかし、いくつかの点において、擬似的定数ストリームは通常のストリームとは異なる。通常のストリームがＦＩＦＯキューを有するのに対して、擬似的定数ストリームは、指定された型の１つの値のための記憶領域（変数と関連付けられた記憶領域に極めて類似する）を有する。係る記憶領域に存在する値は、ストリーム式またはスレッドによりアクセスされたときに、ポップされることも消費されることもなく、記憶領域内に留まり続ける。記憶された値は、新しい値がストリームソースのうちの１つからストリームに入ると、更新される。係るとき、新しい値は単に古い値を上書きする。この更新は典型的にはシステム動作とは非同期的になされるため、更新がストリームデスティネーションにおいて認識される時点は、一般に、非決定的である。擬似的定数ストリームの宣言は、システム初期化時に各ストリーム記憶位置に記憶される初期値を指定しなければならない。
【０１２５】
擬似的定数ストリームは、単独型の宣言においても、モジュールの入力または出力パラメータリストにおいても、以下のシンタックス
ｃｏｎｓｔ＜ストリーム型＞＜ストリーム識別子＞＝＜初期値＞
を用いて宣言される。通常は変数のみに対して適用される既存のＣキーワードｃｏｎｓｔは、宣言されるストリームが擬似的定数ストリームであることを示す（ｃｏｎｓｔの使用は、新規キーワードの導入の手間を省く）。
【０１２６】
これらの考えは、ＦＩＲ５モジュールの以下の変更例において示される。ここでは、もとの例における５つの係数すなわち１０、２０、３０、４０、および５０は、５つの擬似的定数ストリームｈ０、ｈ１、ｈ２、ｈ３、およびｈ４により置き換えられる。システム初期化時にこれらのストリームに挿入される初期値はもとの係数と同一であるため、新しいＦＩＲ５は、もとと同一の係数で動作を開始する。しかし、新しいＦＩＲ５に関しては、これらの係数は、状況が許可するならば、更新され得る。
ｓｔｒｅａｍ  ｉｎｔ  ＦＩＲ５  （ｉｎｔ  Ｘ，  ｃｏｎｓｔ  ｉｎｔ  ｈ０  ＝  １０，
ｃｏｎｓｔ  ｉｎｔ  ｈ１  ＝  ２０，
ｃｏｎｓｔ  ｉｎｔ  ｈ２  ＝  ３０，
ｃｏｎｓｔ  ｉｎｔ  ｈ３  ＝  ４０，
ｃｏｎｓｔ  ｉｎｔ  ｈ４  ＝  ５０，
｛
（ｉｎｔ  ｘ２，  ｉｎｔ  ｙ２）  ＝  ｔａｐ（Ｘ，  ｈ０＊Ｘ，  ｈ１）；
（ｉｎｔ  ｘ３，  ｉｎｔ  ｙ３）  ＝  ｔａｐ（ｘ２，  ｙ２，  ｈ２）；
（ｉｎｔ  ｘ４，  ｉｎｔ  ｙ４）  ＝  ｔａｐ  （ｘ３，  ｙ３，  ｈ３）；
（ｉｎｔ，  ｏｕｔ）  ＝  ｔａｐ（ｘ４，  ｙ４，  ｈ４）；
｝
【０１２７】
図１１Ａは、入力ストリーム６０４上の一連のＦＩＦＯバッファ６０２が強調されたモジュール６００を示す。図１１ＢおよびＣは、ＦＩＦＯバッファ６０２およびモジュール６００を用いる、２つの追加の代替的な実装を示す。図１１Ｂは、ＦＩＦＯバッファ６０２が一連の出力ストリーム６０６上でのみ使用される状態を示す。図１１Ｃは、ＦＩＦＯバッファ６０２が入力ストリーム６０４および出力ストリーム６０６の両方上で使用される状態を示す。プログラマの観点からは、図１１ＡからＣに示す３つの図は同等である。性能の観点からは、図１１Ｃにおけるように入力および出力にバッファを有することにより、モジュール６００は、ストリームを受け取るモジュール６００上の利用可能スペースを考慮することなく、スケジュールされることが可能となる。これは、追加的なメモリおよび別途のスケジュールステップのコストにより実現される。ＦＩＦＯバッファ６０２は、実装に応じて、仮想メモリスペース上、物理メモリスペース上、および登録ファイルスペース上に存在し得る。
【０１２８】
図１１Ａにおけるような入力ストリームＦＩＦＯに対する高レベルのスケジューリングアルゴリズムの１例を以下に示す。
ａ．次の場合、モジュールを実行するようスケジュールする｛
入力ストリーム（単数または複数）の入力ＦＩＦＯにデータが存在する
且つ
現在のモジュールの出力ストリームに接続されたモジュールの入力ストリームＦＩＦＯに利用可能スペースが存在する
｝
【０１２９】
図１０Ｂにおけるような出力ストリームＦＩＦＯに対する高レベルのスケジューリングアルゴリズムの１例を以下に示す。
ｂ．次の場合、モジュールを実行するようスケジュールする｛
現在のモジュールの入力ストリーム（単数または複数）に接続されたモジュールの出力ストリームＦＩＦＯにデータが存在する
且つ
出力ストリーム（単数または複数）のＦＩＦＯに利用可能スペースが存在する
｝
【０１３０】
図１０Ｃにおけるような入力および出力ストリームＦＩＦＯに対する高レベルのスケジューリングアルゴリズムの１例を以下に示す。
ｃ．次の場合、モジュールを実行するようスケジュールする｛
入力ストリーム（単数または複数）の入力ＦＩＦＯにデータが存在する
且つ
（現在のモジュールの出力ストリームに接続されたモジュールの入力ストリームＦＩＦＯに利用可能スペースが存在する
または
出力ストリーム（単数または複数）のＦＩＦＯに利用可能スペースが存在する）
｝
スレッド
【０１３１】
スレッドは、ストリームＣが完全且つ包括的な言語となるにあたり必要不可欠な能力を提供する。スレッドは、Ｃ関数（すなわち、その入力が個別の値であり、その出力が単一の値である関数）の本体内か、またはモジュール（すなわち、その入力および出力が値のストリームである関数）の本体内に、現れ得る。これら２種類のスレッドは、モジュールの本体内のスレッドがストリームＣストリームにアクセスし得（通常はストリームＣストリームにアクセスする）、その理由のために、通常は終了しないことを除いて、同じである。また、Ｃ関数の本体内のスレッドは、ストリームＣストリームにアクセスせず、すべての（良好な挙動を示す）Ｃスレッドと同様に、終了する。
【０１３２】
ストリームＣスレッドの顕著な特性は、並列問題からの完全な乖離である。並列構文は存在せず、他のスレッドと直接的に相互作用することはなく、および新規スレッドは生成されない。したがって、ストリームＣスレッドに関しては、ストリームＣスレッドが複数スレッド環境で動作中であることを意識する必要がない。したがって、スレッドドメインで作業するプログラマは厳格に逐次的な問題に集中してよい。
【０１３３】
ストリームＣにおける関数宣言および関数定義は、Ｃにおける対応物と同じシンタックスおよびセマンティクスを有する。ストリームＣにおける関数コールに関しては、シンタックスおよびセマンティクスは、コールが（ａ）関数の本体に現れるか、または（ｂ）ストリーム式に現れるかに依存する。同じ関数（再帰関数）の本体、または他の関数の本体におけるストリームＣ関数コールは、通常のＣ関数コールと同じシンタックスおよびセマンティクスを有する。ストリーム式におけるストリームＣ関数コールは、Ｃ関数コールと同じシンタックスを有するが、ただし、ストリームが関数コール引数における変数と置き換わる。係るコールのセマンティクスは同じであるが、通常の関数コールのセマンティクスとは同じでない。相違点は、関数の各評価（コール）がどのように行われるかに関する。さらに詳細には、相違点は、（１）関数コール引数に現れるパラメータ（ストリーム）に対して値がどのように取得されるか、（２）関数コール出力のデスティネーション、および（３）制御がどのように取り扱われるか、に関する。
【０１３４】
Ｃにおいて、関数コールに現れる引数に現れるパラメータはすべて変数であり、係る関数入力変数に代入される値は、その変数の現在値である。ストリームＣにおいては、ストリーム式関数コールの引数に現れるパラメータはすべてストリームであり、係る関数入力ストリームのそれぞれに代入される値は、（ａ）通常のストリームの場合は、ストリームデスティネーションにおけるＦＩＦＯキューからポップ（消費）される値である、または（ｂ）擬似的定数ストリームの場合は、そのストリームデスティネーションにおける現在の値である、のいずれかである。
【０１３５】
Ｃにおいて、関数コールにより変えられる値は、関数コール元に渡される。ストリームＣにおいては、ストリーム式関数コールにより返される値は、関数コール出力ストリーム（名称を有する場合も有さない場合もある）に代入される。ストリーム式そのものであるため、ストリーム式関数コールは常に出力ストリームを有する。出力値のデスティネーションは、ストリームのデスティネーションにより決定される。
【０１３６】
Ｃにおいて、関数は、制御のスレッドがその関数に対するコールと遭遇するとき、コールされる。ストリームＣにおいては、ストリーム式関数コールは、制御スレッドに関わりなく評価される（すなわち、関数がコールされる）。代わって、関数は、関数コールの通常の入力ストリームのそれぞれのＦＩＦＯキューに少なくとも１つの値が存在するときは常に便宜主義的にコールされる。擬似的定数入力ストリームは、値を供給する準備が常に整っており、したがって、関数コールまたはストリーム式の評価を決して妨げることはない。
【０１３７】
これら３つの相違点は別として、通常のＣ関数コールおよびストリーム式関数コールのセマンティクスは同じである。このことは、両方の場合においてスレッドに基づくセマンティクスが関数実行に適用されることを意味する。
【０１３８】
以下の関数ＧＤＣの定義およびモジュールＧＤＣ４を有するＣストリームにおけるスレッドの１例が示され得る。
ｉｎｔ  ＧＣＤ  （ｉｎｔ  ａ，  ｉｎｔ  ｂ）        ／／再帰関数
｛
ｉｆ  （（ａ＞＝ｂ）  ＆＆  （ａ％ｂ）＝＝０）        ／／スレッドの開始
｛
ｒｅｔｕｒｎ  （ｂ）；
｝
ｉｆ  （ａ＜ｂ）
｛
ｒｅｔｕｒｎ  ＧＣＤ（ｂ，ａ）；        ／／関数コール
｝
ｒｅｔｕｒｎ  ＧＣＤ（ｂ，  （ａ％ｂ））；        ／／関数コール
｝
ｓｔｒｅａｍ  ｉｎｔ  ＧＣＤ４（ｉｎｔ  ｗ，  ｉｎｔ  ｘ，  ｉｎｔ  ｙ，  ｉｎｔ  ｚ）        ／／モジュール
｛
ｏｕｔ＝ＧＣＤ（ＧＣＤ（ｗ，ｘ），  ＧＣＤ（ｙ，ｚ））；        ／／３つの関数コールを有するストリーム式
｝
【０１３９】
再帰関数の古典的な例であるＧＣＤは、２つの整数の最大公約数を返す。ＧＣＤは２つの整数入力ａおよびｂを有し、１つの整数結果を返す。ＧＣＤ４は、４つの整数ストリーム入力すなわちｗ、ｘ、ｙ、およびｚを有し、１つの整数ストリーム出力を有する。ストリーム式文
ｏｕｔ  ＝  ＧＣＤ（ＧＣＤ（ｗ，  ｘ），  ＧＣＤ（ｙ，  ｚ））；
が、ＧＣＤ４の本体内に存在し、ストリーム式
ＧＣＤ（ＧＣＤ（ｗ，  ｘ），  ＧＣＤ（ｙ，  ｚ  ））
がこの文の中に存在する。
【０１４０】
この式は、ストリームｗ、ｘ、ｙ、およびｚのデスティネーションを含むため、これら４つのデスティネーションのそれぞれにおいてＦＩＦＯキューが存在する。これらのキューは、関数コールＧＣＤ（ｗ，  ｘ）およびＧＣＤ（ｙ，  ｚ）が上述のように便宜主義的および並列的に評価（実行）されることを許可する。これらの２つのコールのように、ＧＣＤに対する第３のコールは、その２つの入力ストリームのＦＩＦＯキューから取得された入力値を用いて便宜主義的に実行される。これらの入力ストリームは、ＧＣＤに対する２つの他のコールの出力ストリームとして作られたものであり、そのために、これら２つのストリーム上のＦＩＦＯキューは、ＧＣＤに対する第３のコールが最初の２つのコールと並列的に実行されることを可能にする。この第３の関数コールの出力ストリームは、ｏｕｔへのストリーム代入により、ＧＣＤ４の出力ストリームに宛てられる。図１２におけるデータフロー図に表される、ＧＣＤに対する関数コールのこの構成は、４つの入力ストリームすなわちｗ、ｘ、ｙ、およびｚからのデータが、３つの並列動作関数コールを通ってストリームされることを可能にし、その結果、それぞれの出力値が、ｉ＞＝０であるいくつかの整数ｉに対してｗ（ｉ）、ｘ（ｉ）、ｙ（ｉ）、およびｚ（ｉ）の最大公約数である、出力値のストリームが作成される。
【０１４１】
ストリームの観点からすると、どのようにモジュールが入力ストリーム値を出力トリーム値に変換するかは、重要ではない。重要であるのは、入力から出力への変換（単数または複数）（および任意の副作用）のみである。これまで挙げてきた例においては、これらの変換は、ストリーム式、すなわち特定用途用ハードウェア、再設定可能なハードウェア（図１および図２における等の）、逐次コードを実行するプロセッサまたは何らかの他の機構を用いて実装され得る式に関して表されてきた。
【０１４２】
これらの変換は、モジュールの本体内に存在する逐次コードとして明示的に表され得る。係るコードは、記憶されたプログラム逐次プロセッサ（ｓｔｏｒｅｄ−ｐｒｏｇｒａｍ  ｓｅｑｕｅｎｔｉａｌ  ｐｒｏｃｅｓｓｏｒ）上で実行され得、スレッドドメインと称され得るものの中に存在し得る。モジュールの本体は、排他的にストリームドメインまたはスレッドドメインにおける文を典型的には含むであろうが、しかし、このことにより、同じモジュール本体内において両種の文が排除されるわけではない。その場合、２つのドメインは、並んで（すなわち並列的に）動作する。
【０１４３】
スレッドドメインのシンタックスおよびセマンティクスは、Ｂｒｉａｎ  Ｗ．  ＫｅｒｎｉｇｈａｎおよびＤｅｎｎｉｓ  Ｍ．  Ｒｉｔｃｈｉｅ共著「Ｃ  Ｐｒｏｇｒａｍｍｉｎｇ  Ｌａｎｇｕａｇｅ」（１９７８年）により非公式に、およびＩＳＯのＣ規格ＩＳＯ／ＩＥＣ  ９８９９により公式に定義されるＣ言語のスーパーセットである。標準Ｃ言語に対する追加は、スレッドが、モジュール入力ストリーム、モジュール出力ストリーム、モジュール本体に対して内部にあるストリーム、またはグローバルストリームであれ、スレッドに対して可視であるこれらのストリームにアクセスすることを可能にする動作に関するものである。これらのストリームアクセス動作は、２つのカテゴリー、すなわちブロック型および非ブロック型に分類される。これらの動作を理解するために、ストリームにおける値のフローを規制するために用いられる機構、およびタスク（タスクはモジュールインスタンスと等価である）を管理するための機構が、図４におけるノードラッパーを参照して説明したように重要である。
【０１４４】
フロー制御およびタスク管理は、ストリームＣランタイムサポートシステムにより提供される重要なサービスである。フロー制御は、ＦＩＦＯキューのオーバーフロー（すなわち、すでにフル状態であるキューにデータを書き込みこと）およびＦＩＦＯキューのアンダーフロー（すなわち、空き状態のキューからデータを読み込みむこと）を防ぐ。タスク管理は、いつタスクが実行状態に置かれるか、いつかの場合においては、いつタスク実行が終了されるか、を制御する。ストリームＣフロー制御システムおよびタスク管理システムにおいては、３つの重要な要素、すなわち消費側カウント、作成側カウント、およびタスクマネージャが存在する。
【０１４５】
整数消費側カウントは、通常の（擬似的定数ではない）ストリームの各ＦＩＦＯキューと関連付けられる。特定ストリームの特定スレッドによるすべての読み込みは、同一のＦＩＦＯキューをアクセスし、したがって、同一の消費側カウントにアクセスする。消費側カウントの符号ビットは、ＦＩＦＯキューが空き状態であるかどうかを示す。１の符号ビット（消費側カウントは負である）は、キューが空き状態であることを示す。０の符号ビット（消費側カウントは非負である）は、キューが非空き状態であることを示す。
【０１４６】
整数作成側カウントは、各通常（擬似的定数ではない）ストリームの各ソースと関連付けられる。作成側カウントの符号ビットは、このストリームソースに挿入された値を受け取るために下流側ＦＩＦＯキューに利用可能スペースが存在するかどうかを示す。０の符号ビット（作成側カウントは非負である）は、すべての下流側キューが、この出力ストリームにおいて値を受け取るためのスペースを有するとは限らないことを示す。１の符号ビット（作成側カウントは負である）は、すべての下流側キューが、この出力ストリームにおいて値を受け取るためのスペースを有することを示す。
【０１４７】
図２におけるノード１８０等の各プロセシングコアは、実行を開始するに必要な入力データを含むすべてのリソースを有する、タスクの先入れ先出し・実行準備完了キーを有する。各プロセシングコアは、タスクの実行を管理し、且つ必要な調節信号をタスク間に提供するタスクマネージャを有する。タスクマネージャは、データがＦＩＦＯキューにプッシュされた（書き込まれた）ときに消費側カウントをインクリメントすることと、データがＦＩＦＯキューからポップされた（消費された）ときに消費側カウントをデクリメントすることと、スペースがデスティネーションＦＩＦＯキューにおいて利用可能となったことを示すためのバックワードアクノレッジメントを、ストリームソースへと送信すること（デフォルトは、各値が各ＦＩＦＯキューから消費された後にバックワードアクノレッジメントを送信する）と、を自動的に行う。タスクマネージャは、データがモジュールの出力ストリームに書き込まれた場合にそのストリームの作成側カウントをインクリメントすることと、モジュールの出力ストリームに対するバックワードアクノレッジメントが受け取られた場合にその出力ストリームの作成側カウントをデクリメントすることと、タスクが、入力データと、タスクが進行するために必要である任意の他の要求されるリソースと、を有する場合に、プロセシングコアの実行準備完了タスクキューにタスクを置くことと、も実行する。タスクマネージャは、タスクが実行準備完了タスクキューの先頭にあり且つ実行ユニットが利用可能である場合に、タスクを実行状態に置き、タスクが進行するために必要な入力データを有さない場合またはタスクがタイムアウトする場合に、タスクの実行を停止する。
【０１４８】
ブロック型ストリームアクセス演算は、モジュール本体に現れるスレッドが、モジュール入力ストリーム、モジュール出力ストリーム、モジュール本体に対して内部にあるストリーム、およびグローバルストリーム等の、スレッドに対して可視であるストリームにアクセスすることを可能にする。これらはストリームにアクセスするための好適な方法である。なぜなら、非ブロック型ストリームアクセス動作とは異なり、ブロック型ストリームアクセス動作は非決定性を生じさせないためである。係る演算のブロックおよび非ブロックは、プロセシングコアのタスクマネージャにより自動的に対処される。
【０１４９】
係る演算は３つあり、それぞれの演算はＣ＋＋における同様の演算にちなんで作られたものである。演算子＞＞は、ストリームＦＩＦＯキューから単一の値をポップ（消費）し、その値を変数に代入するために用いられる。演算子＞＞は
＜ストリーム識別子＞  ＞＞  ＜変数識別子＞；
の形の文において用いられる。この文により、単一の値が左側のストリームからポップされ、右側の変数に代入される。しかし、ストリームに対するＦＩＦＯキューが、ストリームの消費側カウントの符号ビットにより示されるように空き状態である場合、文はブロック（ストール）され、キューが、ストリームの消費側カウントの符号ビットにより示されるように再び非空き状態となるまで、ブロック状態に保持される。
【０１５０】
演算子＜＜は、変数の現在の値をストリームに代入するために用いられる。演算子＜＜は、
＜ストリーム識別子＞  ＜＜  ＜変数識別子＞；
の形の文において用いられる。この文により、右側の変数の値は左側のストリームに代入される。しかし、１つまたは複数の下流側キューが、ストリームソースにおける作成側カウントの符号ビットにより示されるように、係るデータを受け取るスペースを有さない場合、文は、ブロック（ストール）され、すべての下流側キューが、ストリームの作成側カウントの符号ビットにより示されるように再び値を受け取るスペースを有するようになるまで、ブロック状態に保持される。
【０１５１】
ｐｅｅｋ演算子は、ストリームＦＩＦＯキューの先頭における値を、ポップ（消費）せずに、取得するために用いられる。ｐｅｅｋ演算子は、
＜ストリーム識別子＞．ｐｅｅｋ（）
の形の式において用いられる。この式は、＜ストリーム識別子＞のＦＩＦＯキューの先頭における現在の値を返すが、キューから値をポップ（消費）しない。しかし、ストリームに対するＦＩＦＯキューが、ストリームの消費側カウントの符号ビットにより示されるように空き状態である場合、文は、ブロック（ストール）され、キューが、ストリームの消費側カウントの符号ビットにより示されるように再び非空き状態となるまで、ブロック状態に保持される。
【０１５２】
ブロック型のストリームアクセス演算子と同様に、非ブロック型ストリームアクセス演算は、モジュール本体に現れるスレッドが、モジュール入力ストリーム、モジュール出力ストリーム、モジュール本体に対して内部にあるストリーム、およびグローバルストリーム等の、スレッドに対して可視であるストリームにアクセスすることを可能にする。しかし、ブロック型演算とは異なり、非ブロック型演算は、典型的には、演算の結果に影響する競合状態に関与するものであり、したがって、非決定性を導入する。係る演算は２つある。
【０１５３】
＜ストリーム識別子＞．ｃｏｎｓｕｍｅｒＣｏｕｎｔ（）
の形の式は＜ストリーム識別子＞の消費側カウントを返す。なお、＜ストリーム識別子＞は、＞＞演算またはｐｅｅｋ演算を介して、スレッドにより読み込まれるストリームである。この式は、＜ストリーム識別子＞のＦＩＦＯキューが空き状態である場合に＞＞演算またはｐｅｅｋ演算を回避するために、＜ストリーム識別子＞の消費側カウント符号ビットをテストするために、主に用いられる。
【０１５４】
＜ストリーム識別子＞．ｐｒｏｄｕｃｅｒＣｏｕｔ（）
の形の式は、＜ストリーム識別子＞の作成側カウントを返す。なお、＜ストリーム識別子＞は＜＜演算を介して、スレッドにより書き込まれたストリームである。この式は、１つまたは複数の下流側キューが係る新しい値を受け取るスペースを有さない場合に、＜＜演算を回避するために、＜ストリーム識別子＞の作成側カウント符号ビットをテストするために、主に用いられる。
【０１５５】
モジュール本体内のスレッドが多数の異なる形を取り得るが、多くの変化例が、以下の典型的な形となるであろう。
ｓｔｒｅａｍ  ｉｎｔ  ｍｏｄｕｌｅＡ  （ｉｎｔ  ｓｔｒｍ１，  ．．．，  ｉｎｔ  ｓｔｒｍＮ）
｛
ｉｎｔ  ｖａｒ１，  ．．．，  ｖａｒＮ，  ｒｅｓｕｌｔ；        ／／変数を宣言
ｗｈｉｌｅ  ｔｒｕｅ                ／／永遠にループする
｛
ｓｔｒｍ１  ＞＞  ｖａｒ１；
・        ／／入力ストリームから値を読み込む
・
・
ｓｔｒｍＮ  ＞＞  ｖａｒＮ；
・        ／／ｒｅｓｕｌｔを算出する
・
・
ｏｕｔ  ＜＜  ｒｅｓｕｌｔ；        ／／ｒｅｓｕｌｔを出力ストリームに代入する
｝
｝
ここでｍｏｄｕｌｅＡは、１つまたは複数の入力ストリームと、単一の出力ストリームとを有するモジュールである。入力ストリームおよび出力ストリームのデータ型は、整数型へと任意選択される。ｍｏｄｕｌｅＡの本体内のスレッドが最初に実行することは、各入力ストリームに対する値と、単一の出力ストリームに対する値とを宣言することである。次いでスレッドは、各反復が（ａ）各入力ストリームから値を読み込む（消費する）こと、（ｂ）結果を算出すること、および（ｃ）結果を出力ストリームに代入すること、を含む無限ループに入る。
配列
【０１５６】
他の言語におけるのと同様に、配列は、データ要素の配列ばかりではなく、ストリーム配列およびモジュール配列もまた、ストリームＣにおいて重要な役割を果たす。実際のデータ値の配列（データ値の配列へのポインタではなく）は、複数のストリーム上で並列的に伝えられる。ストリーム配列は、モジュールの配列とともに用いられるとき、特に有用である。
【０１５７】
ストリームＣは、Ｃからデータ配列のためのシンタックスおよびセマンティクスを受け継ぐ。このことは、配列の名称が（関数）引数として用いられる場合、関数に渡される値は、配列の先頭の位置またはアドレスであり、配列要素は複写されないことを意味する。ストリーム入力（引数）およびモジュールの出力に対しても、同じことが成り立つ。例示のために、上述のＧＤＣ４モジュールが用いられ得る。
ｓｔｒｅａｍ  ｉｎｔ  ＧＣＤ４（ｉｎｔ  ｗ，  ｉｎｔ  ｘ，  ｉｎｔ  ｙ，  ｉｎｔ  ｚ）                                        ／／４つの整数引数を有する
｛                                        ／／モジュール
ｏｕｔ  ＝  ＧＣＤ（ＧＣＤ（ｗ，  ｘ），  ＧＣＤ（ｙ，  ｚ））；
｝
ＧＣＤ４に４つの別個の整数ストリーム引数を供給する代わりに、各値が４つの整数の配列である単一のストリーム引数が供給される。ＧＣＤ４は、以下のように変形されるであろう。
ｓｔｒｅａｍ  ｉｎｔ  ＧＣＤ４  （ｉｎｔ＊  ｗｘｙｚ）      ／／１つの配列引数を有するモジュール
｛
ｏｕｔ  ＝  ＧＣＤ（ＧＣＤ（ｗｘｙｚ［０］，  ｗｘｙｚ［１］），  ＧＣＤ（ｗｘｙｚ［２］，  ｗｘｙｚ［３］））；
｝
【０１５８】
Ｃ言語の規則によれば、ＧＣＤ４の単一の引数はｉｎｔ＊型、すなわち整数へのポインタであり、この場合、４つの整数の配列における第１の整数である。ＧＣＤ４の本体内のこれらの４つの整数は、標準的なＣ言語の演算子［］を用いてアクセスされる。Ｃ言語型のデータ配列をモジュールに供給することが、ストリームの文脈において配列を取り扱うための１つの方法である。
【０１５９】
いくつかの用途に関しては、モジュールに配列ポインタのストリームを供給することは、その用途に固有の並列性を十分に利用するためには不十分である。したがって、配列のストリームよりもむしろ、ストリームの配列は、データ値の配列へのポインタではなく、実際のデータ値の配列が、複数のストリーム上で並列的に伝えられることを可能にする。ストリーム配列の宣言は、２つの相違、すなわちキーワードｓｔｒｅａｍが宣言に先行すること、および配列のサイズがコンパイル時に既知でなければならないことを除き、通常のＣ言語の配列の宣言と同じである。この制限は、モジュールと同じくアプリケーション内のすべてのストリームがコンパイル時にインスタンス化されるので、必要である。
【０１６０】
ストリーム配列宣言の例を以下に挙げる。
ｓｔｒｅａｍ  ｉｎｔ  ａｒｒａｙ１Ｄ［４］；
ｓｔｒｅａｍ  ｉｎｔ  ａｒｒａｙ２Ｄ［４］［１６］；
ｓｔｒｅａｍ  ｉｎｔ  ａｒｒａｙ３Ｄ［４］［１６］［９］；
第１の宣言は、ａｒｒａｙ１Ｄが４つの整数ストリームの１次元配列であることを宣言する。同様に、ａｒｒａｙ２Ｄが６４個の整数ストリームの２次元配列であること、およびａｒｒａｙ３Ｄが５７６個の整数ストリームの３次元配列であることが宣言される。ストリーム配列の個々のストリームは、データ配列の個々の要素と同じ方法でアクセスされる。例えば、
ａｒｒａｙ３Ｄ［３］［１５］［７］
は、ａｒｒａｙ３Ｄの５７６個のストリームのうちの１つを示す。
【０１６１】
ひとたびストリーム配列が宣言されると、配列全体、配列内のサブ配列、または配列内の個々のストリームは参照され得る。これらの３つの場合が以下の部分的コードにおいて例示される。
ｓｔｒｅａｍ  ｉｎｔ  ｍｏｄｕｌｅＡ（ｉｎｔ）；        ／／モジュール宣言
ｓｔｒｅａｍ  ｉｎｔ  ｍｏｄｕｌｅＢ（ｉｎｔ［４］）；／／モジュール宣言
ｓｔｒｅａｍ  ｉｎｔ  ｍｏｄｕｌｅＣ（ｉｎｔ［３］［４］）；／／モジュール宣言
ｓｔｒｅａｍ  ｉｎｔ  ｍｏｄｕｌｅＤ（ｉｎｔ  Ｗ［３］［４］）／／モジュール定義
｛
・
・
・
ｓｔｒｅａｍ  ｉｎｔ  Ｘ  ＝  ｍｏｄｕｌｅＡ（Ｗ［２］［０］）；        ／／ストリーム式
ｓｔｒｅａｍ  ｉｎｔ  Ｙ  ＝  ｍｏｄｕｌｅＢ（Ｗ［２］［０］）；        ／／ストリーム式
ｓｔｒｅａｍ  ｉｎｔ  Ｚ  ＝  ｍｏｄｕｌｅＣ（Ｗ）；        ／／ストリーム式
・
・
・
｝
ここでは、ｍｏｄｕｌｅＡ、ｍｏｄｕｌｅＢ、およびｍｏｄｕｌｅＣに対する宣言と、ｍｏｄｕｌｅＤの部分的な定義が示される。これら４つのモジュールの入力型を以下に示す。
【０１６２】
【表８】
0005990466.tif 000009
【０１６３】
ｍｏｄｕｌｅＤの本体内のｍｏｄｕｌｅＡ、ｍｏｄｕｌｅＢ、およびｍｏｄｕｌｅＣのインスタンス化に供給される入力引数を以下に示す。
【０１６４】
【表９】
0005990466.tif 000010
【０１６５】
それぞれの場合において、モジュールインスタンス化引数型は、モジュール入力型と一致し、したがって各モジュールインスタンス化は、ストリームＣの型づけの強い要件を満足する。
【０１６６】
ストリーム式内におけるストリーム配列の個々のストリームをアクセスすることも、この複素数乗算モジュール例に示すように、単純明快である。
ｓｔｒｅａｍ  ｉｎｔ［２］  ｃｏｍｐｌｅｘＭｕｌｔ  （ｉｎｔ  Ｘ［２］，  ｉｎｔ  Ｙ［２］）
｛
ｏｕｔ［０］  ＝  Ｘ［０］＊Ｙ［０］  −  Ｘ［１］＊Ｙ［１］；
ｏｕｔ［１］  ＝  Ｘ［０］＊Ｙ［１］  ＋  Ｘ［１］＊Ｙ［０］；
｝
ストリーム式内の演算子が並列的にアクティブであるため、ストリーム式Ｘ［０］＊Ｙ［０］−Ｘ［１］＊Ｙ［１］およびＸ［０］＊Ｙ［１］＋Ｘ［１］＊Ｙ［０］における４つの乗算、１つの加算、および１つの減算は、並列的に評価される。
【０１６７】
並列処理に対する最も普及している手法の１つであるデータ並列性は、同一のタスクが同一のデータ構造（典型的には配列）の異なる部分上で並列的に（並行して）実行される、並列性の１つの形態である。ストリームＣにおいて、データ並列性は、モジュール配列によりサポートされる。
【０１６８】
モジュール配列は、その名称が暗示するように、モジュールの配列である。モジュール配列は、モジュール名と入力パラメータのリストとの間に角括弧で囲まれた配列次元を挿入することにより宣言される。以下はモジュール配列宣言の２つの例である。
ｓｔｒｅａｍ  ｉｎｔ  ｍｏｄｕｌｅＡ［３］［４］（ｉｎｔ，  ｉｎｔ）；
ｓｔｒｅａｍ（ｉｎｔ，  ｉｎｔ）ｍｏｄｕ１ｅＢ［３］［４］（ｉｎｔ，  ｉｎｔ）；
両方の場合において、配列次元は３×４である。
【０１６９】
通常（単独型）モジュールの定義と同様に、モジュール配列の定義は波括弧（｛および｝）で囲まれた本体を有する。以下は、モジュール配列定義の２つの例である。第１の例は単一（デフォルト）出力ストリームを有し、それに対して、第２の例は名称を有する２つの出力ストリームを有する。
ｓｔｒｅａｍ  ｉｎｔ  ｍｏｄｕｌｅＡ［３］［４］（ｉｎｔ  ａ，  ｉｎｔ  ｂ）
｛
／／モジュール本体
｝
ｓｔｒｅａｍ（ｉｎｔ  ｘ，  ｉｎｔ  ｙ）  ｍｏｄｕｌｅＢ［３］［４］（ｉｎｔ  ａ，  ｉｎｔ  ｂ）
｛
／／モジュール本体
｝
【０１７０】
ひとたびモジュール配列が宣言（宣言または定義）されると、配列全体、配列内のサブ配列、または配列内の個々のモジュールは、データ配列およびストリーム配列と同じ方法で、ストリーム式内でインスタンス化され得る。ｍｏｄｕｌｅＡ［３］［４］に対して、これら３つの場合が示される。
【０１７１】
【表１０】
0005990466.tif 000011
【０１７２】
モジュール配列の重要な属性は、モジュール配列がシステム初期化時にインスタンス化されるとき、顕著なものとなる。モジュール配列の各要素は、別個のモジュールインスタンス化として、インスタンス化される。その結果、すべての配列要素が並列的に動作することが可能となる。ｍｏｄｕｌｅＡ［３］［４］がこの概念の１つの例である。モジュールがインスタンス化されると、ｍｏｄｕｌｅＡの１２（３×４）個の別個のインスタンス化が作られ、それぞれのインスタンス化は、他の１１個のインスタンス化と並列的に動作する。さらに、このインスタンス化の乗算は、ｍｏｄｕｌｅＡ［３］［４］の各インスタンス化に当てはまる。したがって、ｍｏｄｕｌｅＡ［３］［４］の３つのインスタンスが存在する場合、ｍｏｄｕｌｅＡの３６（３×１２）個の別個のインスタンス化が作られる。
【０１７３】
モジュール配列インスタンス化のパーソナル化は、インスタンス化がどのデータ上で演算されるかを決定する。インスタンス化は、各モジュールインスタンス化にそれ自体のユニークなデータをインスタンス化の入力ストリームを通して供給することにより、パーソナル化され得る。各モジュールインスタンス化が、その配列インデックスを、インデックス演算子を用いて特定することが可能となり、それにより、インスタンス化が、グローバル配列のそれ自体のユニークな部分にアクセスすることが可能となることにより、インスタンス化はパーソナル化され得る。
【０１７４】
ストリーム配列がモジュール配列の各要素にユニークなデータを供給するために用いられ得る第１タイプのパーソナル化が以下に示される。第２タイプのパーソナル化は、各配列モジュールの配列インデックスのインスタンス化がコンパイル時に既知であるという事実を利用する。これらのインデックスにアクセスするために、プログラマは、以下のシンタックス
ｉｎｔ  ｉｎｄｅｘ（ｉｎｔ  ｉ）
で演算子を使用する。なお、式中、ｉはコンパイル時に定数へと評価される整数式である。コンパイル時に、ｉｎｄｅｘ（ｉ）はインスタンス化の第１番目のインデックスと置き換えられる。ｉが配列境界外である場合、コンパイル時エラーまたはランタイムエラーが生じる。
【０１７５】
ストリーム配列およびモジュール配列は、ストリームＣの特別な配列結合機能を用いてストリーム配列とモジュール配列とが結合されたときに、最大の有用性を発揮する。結合に対しては、３つの要件、すなわちａ）ストリーム配列およびモジュール配列が同じ次元を有さなければならないこと、ｂ）ストリーム配列がモジュール配列の入力または出力と接続（結合）されていなければならないこと、およびｃ）ストリーム配列型がモジュールの入力／出力型と一致しなければならないこと、が存在する。
【０１７６】
係る結合が生じると、ストリーム配列内の各個別ストリームは、同一のインデックスを有するモジュール配列の個々のモジュールの入力／出力ストリームに接続（結合）される。したがって、０＜＝ｉ１＜Ｄ１，  ０＜＝ｉ２＜Ｄ２．．．０＜＝ｉｎ＜Ｄｎに対して、ストリーム配列Ｓ［Ｄ１］［Ｄ２］．．．［Ｄｎ］がモジュール配列Ｍ［Ｄ１］［Ｄ２］．．．［Ｄｎ］の入力／出力に結合されると、各個別ストリームＳ［ｉ１］［ｉ２］．．．［ｉｎ］は個別モジュールＭ［ｉ１］［ｉ２］．．．［ｉｎ］の入力／出力に接続される。
【０１７７】
以下は、１つのモジュール配列の出力および他のモジュール配列の入力に結合されたストリーム配列の例である。
ｓｔｒｅａｍ  ｉｎｔ  ｍｏｄｕｌｅＡ［３］［２］（）；        ／／第１の結合されたモジュール
ｓｔｒｅａｍ  ｖｏｉｄ  ｍｏｄｕｌｅＢ［３］［２］（ｉｎｔ）；        ／／第２の結合されたモジュール
ｓｔｒｅａｍ  ｖｏｉｄ  ｐａｒｅｎｔＭｏｄｕｌｅ（）
｛
ｓｔｒｅａｍ  ｉｎｔ  ｃＳｔｒｍ［３］［２］；        ／／結合されたストリーム
ｃＳｔｒｍ［］［］  ＝  ｍｏｄｕｌｅＡ［］［］（）；        ／／ｃＳｔｒｍに結合されたｍｏｄｕｌｅＡの出力
ｍｏｄｕｌｅＢ［］［］（ｃＳｔｒｍ［］［］）；        ／／ｍｏｄｕｌｅＢの入力に結合されたｃＳｔｒｍ
｝
ここで、ｍｏｄｕｌｅＡ［３］［２］の出力ストリームはｃＳｔｒｍ［３］［２］に結合され、ｃＳｔｒｍ［３］［２］はｍｏｄｕｌｅＢ［３］［２］の入力ストリームに結合される。これらは正規の結合である。なぜなら、
・ｃＳｔｒｍ［３］［２］、ｍｏｄｕｌｅＡ［３］［２］、およびｍｏｄｕｌｅＢ［３］［２］が、すべて同一の次元を有し、
・ｃＳｔｒｍ［３］［２］が、ｍｏｄｕｌｅＡ［３］［２］の出力およびｍｏｄｕｌｅＢ［３］［２］の入力に接続され、
・ｃＳｔｒｍ［３］［２］の型、ｍｏｄｕｌｅＡ［３］［２］の出力型、およびｍｏｄｕｌｅＢ［３］［２］の入力型がすべてｉｎｔである、
ためである。
【０１７８】
以下の表は、ｃＳｔｒｍ［３］［２］の各個別ストリーム、すなわち（ａ）出力がストリームソースであるモジュール、（ｂ）ｃＳｔｒｍ［３］［２］における個別ストリーム、および（ｃ）インプットがストリームデスティネーションであるモジュールをリストする。
【０１７９】
【表１１】
0005990466.tif 000012
【０１８０】
ＰＩＮＧ
【０１８１】
モジュールが他のモジュールに対して、そのモジュールが実行する特定の演算、副作用が完了したことを通知することが必要となる状況が存在する。例えば、モジュールがグローバルメモリ内のデータ構造に関する演算を実行するとき、おそらく同一のデータ構造に関する演算を実行する多数のモジュールのうちの１つとして、そのモジュールは、典型的には、演算が完了したため下流側の演算またはタスクが開始され得ることを下流側モジュールに通知する必要がある。これらの状況においては、値を返す必要はなく、特定のタスクが完了した信号を返すのみでよい。値ではなく信号が必要とされるこれらの状況に対して、ストリームＣはｐｉｎｇデータ型を提供する。ｐｉｎｇ（ｐｉｎｇ型の値）は、特性を有さず、互いに対して完全に区別がつかない。
【０１８２】
ｐｉｎｇは、３つの演算子、すなわち、（１）タスクの同期を提供するｊｏｉｎ演算子、（２）＞＞ストリームアクセス演算子、および（３）＜＜ストリームアクセス演算子、とともに用いられる。第１の使用法はストリームのみに関するが、第２および第３の使用法は、ストリームおよびスレッドに関する。
【０１８３】
ｐｉｎｇキーワードは、１つまたは複数のｐｉｎｇ型のストリームを宣言するときに用いられる。例えば、以下の式
ｓｔｒｅａｍ  ｐｉｎｇ  ｐＳｔｒｍ０，  ｐＳｔｒｍ１，  ｐＳｔｒｍ２；
は、ｐＳｔｒｍ０、ｐＳｔｒｍ１、およびｐＳｔｒｍ２がｐｉｎｇ型のストリームであることを宣言する。ｐｉｎｇキーワードは、
ｓｔｒｅａｍ  ｐｉｎｇ  ｍｏｄｕｌｅＮａｍｅ（ｉｎｔ，  ｐｉｎｇ）；
のように、モジュール入力または出力がｐｉｎｇ型であることを宣言するモジュールプロトタイプ／定義にも用いられる。
【０１８４】
ｐｉｎｇの第１の使用法はｊｏｉｎ演算子に関し、ｊｏｉｎ演算子は、ｐｉｎｇストリームと他の１つまたは複数のストリームとを繋いで、単一の出力ストリームを作る機能を有する。この演算子は、他のいくつかの演算モデルにおいて見られる接合演算（ｒｅｎｄｅｚｖｏｕｓ  ｏｐｅｒａｔｉｏｎ）と同様である。ｊｏｉｎ演算子を含む式は、２つの形
＜ｐｉｎｇストリーム配列＞．ｊｏｉｎ（）
＜ｐｉｎｇストリーム＞．ｊｏｉｎ（＜ストリーム式＞）
のうちの１つを取る。すべてのストリーム式と同様に、これらの形のうちの１つにおける式の各評価は、各入力ストリームから単一の値／ｐｉｎｇを消費し、式の（名称を有さない）出力ストリーム上に単一の値／ｐｉｎｇを作る。入力ストリームが空き状態（値が存在しない）である場合、すべての入力ストリームが少なくとも１つの値／ｐｉｎｇを有するまで、評価はストール（ブロック）される。非ｐｉｎｇ式に対しては明示的なｊｏｉｎ演算は必要ない。なぜなら、ｊｏｉｎ演算の効果はすでに式評価のセマンティクスにより包含されるためである。
【０１８５】
第１の型の式が評価されると、単一のｐｉｎｇがｐｉｎｇストリームの配列内の各ストリームから消費され、単一のｐｉｎｇが式の出力ストリーム上に発行される。
【０１８６】
第２の形の式が評価されると、＜ｐｉｎｇストリーム＞からの単一のｐｉｎｇと、＜ストリーム式＞の評価とが消費される。このストリーム式＜ストリーム式＞は、ｐｉｎｇを含む任意の型であり得る。＜ストリーム式＞の評価から得られる値は、ｊｏｉｎ演算の出力ストリーム上に発行される。式がｐｉｎｇ型である場合、式は単一のｐｉｎｇに評価される。このように、ｐｉｎｇストリームは、上述の＞＞演算子の場合と同じく、＜ｐｉｎｇストリーム＞にｐｉｎｇが存在する場合にのみ評価の進行を許可する守衛として機能する。
【０１８７】
ｊｏｉｎ演算の２つの形が図１３Ａおよび１３Ｂに図示される。図１３Ａにおいては、サイズがｎである１次元ｐｉｎｇストリーム配列の個別ストリームが繋がれて、単一の（名称を有さない）出力ｐｉｎｇストリームが作られる。図１３Ｂにおいては、単一のｐｉｎｇストリームであるｐｉｎｇＳｔｒｍが式ｅｘｐｒと繋がれて、ｅｘｐｒと同じ型を有する単一の（名称を有さない）出力ストリームが作られる。
【０１８８】
ｊｏｉｎ演算の１例は、データ構造Ｘを含み得る。ただし、データ構造Ｘについて、２つの演算、すなわち演算Ａおよび演算Ｂが行われる。これらの演算は、以下の要件、すなわちａ）ｇｏ信号に応答して実行される以外には、演算Ａも演算Ｂも実行されないこと、ｂ）ｇｏ信号が受信されると、演算Ａおよび演算Ｂが並列的に実行されること、およびｃ）演算Ａまたは演算Ｂのいずれかが開始される前に、直前のｇｏ信号に応答して実行された両方の演算が完了していなければならないこと、を満足する。
【０１８９】
この問題に対する簡単なソリューションは、ｊｏｉｎ演算の２つのインスタンスを用いることである。
ｓｔｒｅａｍ  ｐｉｎｇ  ｍｏｄｕｌｅＡ（ｐｉｎｇ  ｐＳｔｒｍ）
｛
ｗｈｉｌｅ（ｔｒｕｅ）
｛
ｐＳｔｒｍ  ＞＞  ｐｉｎｇ；
／／データ構造Ｘ上で演算Ａを実行する
ｏｕｔ  ＜＜  ｐｉｎｇ；
｝
｝
ｓｔｒｅａｍ  ｐｉｎｇ  ｍｏｄｕｌｅＢ（ｐｉｎｇ  ｐＳｔｒｍ）
｛
ｗｈｉｌｅ（ｔｒｕｅ）
｛
ｐＳｔｒｍ  ＞＞  ｐｉｎｇ；
／／データ構造Ｘに関して演算Ｂを実行する
ｏｕｔ  ＜＜  ｐｉｎｇ；
｝
｝
ｓｔｒｅａｍ  ｐｉｎｇ  ｍｏｄｕｌｅＣ（ｐｉｎｇ  ｇｏＳｔｒｍ）
｛
ｓｔｒｅａｍ  ｐｉｎｇ  ｓｔａｒｔＳｔｒｍ＝ｇｏＳｔｒｍ．ｊｏｉｎ（ｄｏｎｅＳｔｒｍ）；
ｓｔｒｅａｍ  ｐｉｎｇ  ＳｔｒｍＡ＝ｍｏｄｕｌｅＡ（ｓｔａｒｔＳｔｒｍ）；
ｓｔｒｅａｍ  ｐｉｎｇ  ＳｔｒｍＢ＝ｍｏｄｕｌｅＢ（ｓｔａｒｔＳｔｒｍ）；
ｓｔｒｅａｍ  ｐｉｎｇ  ｄｏｎｅＳｔｒｍ＝ＳｔｒｍＡ．ｊｏｉｎ（ＳｔｒｍＢ）；
ｄｏｎｅＳｔｒｍ．ｉｎｉｔｉａｌｉｚｅ（ｐｉｎｇ）；
ｏｕｔ＝ｄｏｎｅＳｔｒｍ；
｝
ｍｏｄｕｌｅＡおよびｍｏｄｕｌｅＢは、それぞれ演算Ａおよび演算Ｂをカプセル化する。それぞれは、ｐｉｎｇ毎に１つの演算を開始する入力ｐｉｎｇストリームと、ｐｉｎｇ毎に１つの演算の完了を確認する出力ｐｉｎｇストリームとを有する。ｍｏｄｕｌｅＣはｍｏｄｕｌｅＡおよびｍｏｄｕｌｅＢの両方の１つのインスタンスを含み、ｇｏＳｔｒｍ入力ｐｉｎｇストリームを介してｇｏ信号を受け取る。
【０１９０】
ｍｏｄｕｌｅＣにおける６つの文は以下の役割を果たす。
ｓｔｒｅａｍ  ｐｉｎｇ  ｓｔａｒｔＳｔｒｍ＝ｇｏＳｔｒｍ．ｊｏｉｎ（ｄｏｎｅＳｔｒｍ）；
は、ｇｏＳｔｒｍとｄｏｎｅＳｔｒｍとを繋いで、ｓｔａｒｔＳｔｒｍを作る。このように、ｇｏＳｔｒｍ（すなわちｇｏ信号）上にｐｉｎｇが存在し、ｄｏｎｅＳｔｒｍ上にｐｉｎｇが存在する（これは、直前のｇｏ信号に応答した演算Ａおよび演算Ｂが完了したことを示す）場合に、ｐｉｎｇがｓｔａｒｔＳｔｒｍに代入される。
ｓｔｒｅａｍ  ｐｉｎｇ  ＳｔｒｍＡ＝ｍｏｄｕｌｅＡ（ｓｔａｒｔＳｔｒｍ）；
は、ｓｔａｒｔＳｔｒｍをｍｏｄｕｌｅＡの入力ｐｉｎｇストリームに接続し、ｍｏｄｕｌｅＡの出力ｐｉｎｇストリームをＳｔｒｍＡに接続する。このことは、演算Ａは、直前のｇｏ信号に関連付けられた両方の演算が完了した後にのみ、ｇｏ信号に応答して行われることを意味する。
ｓｔｒｅａｍ  ｐｉｎｇ  ＳｔｒｍＢ＝ｍｏｄｕｌｅＢ（ｓｔａｒｔＳｔｒｍ）；
は直前の文と同様であり、この文は、直前のｇｏ信号に関連付けられた両方の演算が完了した後にのみ、演算Ｂがｇｏ信号に応答して行われることが確実になされるようにする。しかし、演算Ａおよび演算Ｂが行われる順序に制限はない。換言すれば、演算Ａおよび演算Ｂは並列的に行われる。
ｓｔｒｅａｍ  ｐｉｎｇ  ｄｏｎｅＳｔｒｍ＝ＳｔｒｍＡ．ｊｏｉｎ  （ＳｔｒｍＢ）；
はｍｏｄｕｌｅＡの出力ｐｉｎｇストリームであるＳｔｒｍＡと、ｍｏｄｕｌｅＢの出力ｐｉｎｇストリームであるＳｔｒｍＢとを繋ぐ。このように、直前のｇｏ信号に応答して行われた両方の演算が完了したならば、ｐｉｎｇがｄｏｎｅＳｔｒｍに代入される。
ｄｏｎｅＳｔｒｍ．ｉｎｉｔｉａｌｉｚｅ（ｐｉｎｇ）；
は、システム初期化時に単一のｐｉｎｇをｄｏｎｅＳｔｒｍに代入する。このことは、すべての以前の演算が、まったく存在せず、完了したことを示す。この文がなければ、ｍｏｄｕｌｅＣはデッドロックし、演算はまったく行われないであろう。
ｏｕｔ  ＝  ｄｏｎｅＳｔｒｍ；
は、ｄｏｎｅＳｔｒｍをｍｏｄｕｌｅＣのデフォルト出力ストリームであるｏｕｔに接続する。このストリーム上の各ｐｉｎｇは、ｇｏ信号に応答して行われた演算Ａおよび演算Ｂが完了したことを確認する。ｍｏｄｕｌｅＣの挙動は、ｍｏｄｕｌｅＣの入力ポート上でｇｏ信号（ｐｉｎｇ）として総括され、その結果、以前の演算が完了した後にのみ、演算Ａおよび演算Ｂはデータ構造Ｘに関して並列的に行われ得る。演算Ａおよび演算Ｂの両方が完了すると、モジュールＣはその出力ポート上で確認としてｐｉｎｇを送信する。
【０１９１】
ｐｉｎｇＳｔｒｍ  ＞＞  ｐｉｎｇ；
（式中、ｐｉｎｇＳｔｒｍはｐｉｎｇ型のストリームである）の形の文は、スレッドの実行をｐｉｎｇＳｔｒｍ内のｐｉｎｇと同期する機能を有する。この文がスレッドにおいて遭遇されると、単一のｐｉｎｇがｐｉｎｇＳｔｒｍから読み出される（消費される）。ｐｉｎｇＳｔｒｍが空き状態（すなわち、ｐｉｎｇＳｔｒｍにｐｉｎｇが存在しない）である場合、この文は、ｐｉｎｇが利用可能となるまで、ブロック（ストール）される。したがって、この文は、ｐｉｎｇがｐｉｎｇＳｔｒｍに存在するときにのみスレッドの進行を許可する守衛として機能する。この演算では変数は関与せず、＞＞通常は変数の存在が期待される演算子の右側には、キーワードｐｉｎｇのみが存在する。
【０１９２】
ｐｉｎｇＳｔｒｍ  ＜＜  ｐｉｎｇ；
（式中、ｐｉｎｇＳｔｒｍはｐｉｎｇ型のストリームである）の形の文は、スレッドが、特定の演算（単数または複数）が完了したことを関係者に知らせることを可能にする。この文がスレッドにおいて遭遇されると、単一のｐｉｎｇがｐｉｎｇＳｔｒｍに書き込まれる（代入される）。上述の第１の文とは異なり、この文は決してブロックされない。
【０１９３】
ｐｉｎｇが関与するこれらの２つの形のストリーム／スレッド相互作用が、以下の部分的コードにおいて例示される。
ｓｔｒｅａｍ  ｐｉｎｇ  ｍｏｄｕｌｅＡ（ｐｉｎｇ  ｐＳｔｒｍ）
｛
／／ループに入る前に初期化を行う
ｗｈｉｌｅ（ｔｒｕｅ）
｛
ｐＳｔｒｍ  ＞＞  ｐｉｎｇ；
／／副作用を有する演算を行う
ｏｕｔ  ＜＜  ｐｉｎｇ；
｝
｝
ｍｏｄｕｌｅＡは、単一の入力ポートおよび単一の出力ポートを有し、その両方がｐｉｎｇ型である。ｍｏｄｕｌｅＡ内には無限ループを含むスレッドが存在する。なお、この無限ループの各反復は、以下の文
ｐＳｔｒｍ  ＞＞  ｐｉｎｇ；
により開始される。この文は、ループの反復を、モジュール入力ストリームｐＳｔｒｍにおけるｐｉｎｇと同期させる機能を有する。ｐＳｔｒｍが空き状態であるとき、この文はブロックされ、ｐＳｔｒｍが非空き状態であるとき、この文はｐＳｔｒｍから単一のｐｉｎｇを消費する。その文に続いて、必ず副作用を伴う活動に関連する文がある。副作用がないなら、ｍｏｄｌｅＡは操作不能と等価となるであろう。各反復の末尾には、次の文
ｏｕｔ  ＜＜  ｐｉｎｇ；
が存在する。なお、この文は、ｍｏｄｕｌｅＡの標準出力ポートを通して、他のループ反復が完了したことを知らせる。
【０１９４】
完全にストリームドメイン内で作業するとき、ｊｏｉｎ演算子は有用である。しかし、スレッド内で結合を行うことがより便利となる状況も存在し得る。例えば、スレッド
ｓｔｒｅａｍ  ｐｉｎｇ  ｐｉｎｇＳｔｒｍ［３２］；
内で、個別ストリームを結合することを考えてみる。それは、スレッド内でｆｏｒループを埋め込むことにより達成され得る。
ｆｏｒ  （ｉｎｔ  ｉ  ＝  ０；  ｉ  ＜  ３２；  ＋＋ｉ）
｛
ｐｉｎｇＳｔｒｍ  ［ｉ］  ＞＞  ｐｉｎｇ；
｝
このループは、１つのｐｉｎｇがｐｉｎｇＳｔｒｍ内の３２個のストリームのそれぞれから消費されるまで、ブロックされる。ｐｉｎｇＳｔｒｍ［］．ｊｏｉｎ（）の出力ストリームに対応する出力ストリームは、文
ｊｏｉｎＳｔｒｍ  ＜＜  ｐｉｎｇ；
を有するｆｏｒループに従うことにより、作られる。
【０１９５】
ｐｉｎｇＳｔｒｍ［］．ｊｏｉｎ（）の挙動を模倣するモジュールを作るために、これらの２つの部分的コードがｗｈｉｌｅ（ｔｒｕｅ）ループに埋め込まれ、そのループは、モジュール
ｓｔｒｅａｍ  ｐｉｎｇ  ｊｏｉｎＡｒｒａｙ（ｐｉｎｇ  ｐｉｎｇＳｔｒｍ［３２］）
｛
ｗｈｉｌｅ（ｔｒｕｅ）
｛
ｆｏｒ  （ｉｎｔ  ｉ  ＝  ０；  ｉ  ＜  ３２；  ＋＋ｉ）
{
ｐｉｎｇＳｔｒｍ  ［ｉ］  ＞＞  ｐｉｎｇ；
}
ｏｕｔ  ＜＜  ｐｉｎｇ；
｝
｝
に代入される。
【０１９６】
埋め込まれたスレッドを有するモジュールは、ｐｉｎｇＳｔｒｍ．ｊｏｉｎ（ｅｘｐｒ）（式中、ｅｘｐｒは式である）の挙動を模倣するために用いられ得る。しかし、この場合、モジュールは、ｐｉｎｇＳｔｒｍに対する入力ストリームばかりではなく、ｅｘｐｒの各入力ストリームに対する入力ストリームも必要とする。したがって、例えば、ｅｘｐｒが式Ｘ＊Ｙ＋Ｚ（式中、Ｘ、Ｙ、およびＺは整数である）である場合、ｐｉｎｇＳｔｒｍ．ｊｏｉｎ（ｅｘｐｒ）を実装するモジュールは、
ｓｔｒｅａｍ  ｐｉｎｇ  ｊｏｉｎＥｘｐｒ（ｐｉｎｇ  ｐｉｎｇＳｔｒｍ，  ｉｎｔ  Ｘ，  ｉｎｔ  Ｙ，  ｉｎｔ  Ｚ）
｛
ｗｈｉｌｅ（ｔｒｕｅ）
｛
ｐｉｎｇＳｔｒｍ  ＞＞  ｐｉｎｇ；
ｏｕｔ  ＜＜  Ｘ＊Ｙ＋Ｚ；
｝
｝
のようになるであろう。
【０１９７】
画素処理例は、同一のタスクが配列等の同一のデータ構造の異なる部分上で並列的に（並行して）実行される、並列性の１つの形態であるデータ並列性の実装におけるｐｉｎｇ、ストリーム配列、およびモジュール配列の使用を示す。この例は、モジュール配列およびモジュールからなる。
ｅｘｔｅｒｎ  ｉｎｔ  ｘＳｃａｌｅＦａｃｔｏｒ，  ｙＳｃａｌｅＦａｃｔｏｒ；
ｓｔｒｅａｍ  ｐｉｎｇ  ｄｏＰｉｘｅｌ［６４］［２５６］  （ｉｎｔ＊  ｂａＳｔｒｍ）                                        ／／本体は
｛                                        ／／スレッドドメインにある
ｃｏｎｓｔ  ｉｎｔ  ｘ  ＝  ｘＳｃａｌｅＦａｃｔｏｒ＊ｉｎｄｅｘ（０）；
ｃｏｎｓｔ  ｉｎｔ  ｙ  ＝  ｙＳｃａｌｅＦａｃｔｏｒ＊ｉｎｄｅｘ（１）；
ｉｎｔ＊  ｂａｓｅＡｄｄｒｅｓｓ；
ｗｈｉｌｅ  ｔｒｕｅ
｛
ｂａＳｔｒｍ  ＞＞  ｂａｓｅＡｄｄｒｅｓｓ；
・        ／／演算をｂａＳｔｒｍ［ｘ］［ｙ］および
.        ／／その近傍に関して行う
・
ｏｕｔ  ＜＜  ｐｉｎｇ；
｝
｝
ｓｔｒｅａｍ  ｖｏｉｄ  ｐａｒｅｎｔＭｏｄｕｌｅ（ｉｎｔ＊  ｂａＳｔｒｍ）                                                ／／本体は
｛                                        ／／ストリームドメイン内にある
ｓｔｒｅａｍ  ｐｉｎｇ  ｘＳｔｒｍ［６４］［２５６］；
ｓｔｒｅａｍ  ｐｉｎｇ  ｊＳｔｒｍ；
ｊＳｔｒｍ．ｉｎｉｔｉａｌｉｚｅ（ｐｉｎｇ）；
ｘＳｔｒｍ［］［］  ＝  ｄｏＰｉｘｅｌ［］［］（ｊＳｔｒｍ．ｊｏｉｎ（ｂａＳｔｒｍ））；
ｊＳｔｒｍ  ＝  ｘＳｔｒｍ［］［］．ｊｏｉｎ（）；
｝
【０１９８】
２次元モジュール配列ｄｏＰｉｘｅｌ［６４］［２５６］は、画素の２次元配列のサイズと一致するように作られている。ｄｏＰｉｘｅｌ［６４］［２５６］が動作する画素配列のベースアドレスは、入力ストリームｂａＳｔｒｍにより供給される。個別ｄｏＰｉｘｅｌモジュール上の画素のｘ座標は、個別ｄｏＰｉｘｅｌモジュールのｘインデックスであるｉｎｄｅｘ（０）（セクション５．３参照）にグローバル定数ｘＳｃａｌｅＦａｃｔｏｒを乗算することにより得られる。個別ｄｏＰｉｘｅｌモジュール上の画素のｙ座標は、個別ｄｏＰｉｘｅｌモジュールのｙインデックスであるｉｎｄｅｘ（１）にグローバル定数ｙＳｃａｌｅＦａｃｔｏｒを乗算することにより得られる。各画素の処理は、変数ｂａＳｔｒｍをｂａＳｔｒｍの現在値に設定することにより始まる。次いで、ｂａＳｔｒｍ［ｘ］［ｙ］およびその近傍について演算が行われる。処理が終わると、個別ｄｏＰｉｘｅｌモジュールはｐｉｎｇを発行することにより完了を知らせる。
【０１９９】
ｐａｒｅｎｔＭｏｄｕｌｅは、画素配列のベースアドレスを、ｄｏＰｉｘｅｌ［６４］［２５６］内の個別モジュールにブロードキャストする機能を担当する。このことは、以下の文
ｘＳｔｒｍ［］［］＝ｄｏＰｉｘｅｌ［］［］（ｊＳｔｒｍ．ｊｏｉｎ（ｂａＳｔｒｍ））；
によりなされる。ここで、ｄｏＰｉｘｅｌの入力引数リスト内の式ｊＳｔｒｍ．ｊｏｉｎ（ｂａＳｔｒｍ）は、ｊＳｔｒｍにｐｉｎｇが存在する場合にのみｂａＳｔｒｍ内の値が通過することを許可する守衛として機能する。以下の文
ｊＳｔｒｍ．ｉｎｉｔｉａｌｉｚｅ（ｐｉｎｇ）；
によりｊＳｔｒｍに挿入される初期ｐｉｎｇは、まさに第１のベースアドレスが妨げられずに通過することを許可する。その後、ｐｉｎｇは、以下の文
ｊＳｔｒｍ  ＝  ｘＳｔｒｍ［］［］．ｊｏｉｎ（）；
（ここで、ｘＳｔｒｍ［６４］［２５６］は、ｄｏＰｉｘｅｌ［６４］［２５６］内の個々のモジュールにより作られたｐｉｎｇストリームの配列である）により、ｊＳｔｒｍに挿入される。したがって、新しいｐｉｎｇは、ｄｏＰｉｘｅｌ［６４］［２５６］内のすべのモジュールが、ｐｉｎｇを発行することにより、以前の演算の完了を知らせる場合にのみ、ｊＳｔｒｍに挿入される。これにより、画素配列に関するすべての演算は、次の配列に関する演算が開始される前に、確実に完了することとなる。
【０２００】
標準的Ｃデータ型よりもむしろｐｉｎｇを用いることに、大きい利点が存在する。Ｃデータ型を用いると、先入れ先出し（ＦＩＦＯ）が、Ｃデータ型ストリームのすべてのデスティネーション、すなわち、ストリームが式への入力となるすべての位置において、データ値に必要とされる。しかしｐｉｎｇは互いに対して区別がつかないため、ｐｉｎｇストリームの各デスティネーションにおいて必要とされるものは、キューに入れられたｐｉｎｇの個数を知らせるカウンタのみである。これにより、データ値に対する先入れ先出しキューと比較して、コストが顕著に削減されることとなる。
【０２０１】
ｐｒａｇｍａコマンドはストリームＣコンパイラ／リンカ／ローダに対する指示である。指示＃ｐｒａｇｍａ  ＩｎｉｔｉａｌｉｚｅＣｏｕｎｔ（ｍ，  ｐ，  ｎ）は、モジュールｍからｎの入力／出力ポートｐの消費側／作成側カウントを初期化する。ｐｒａｇｍａコマンドは、モジュール定義＃ｐｒａｇｍａ  ＦｗｒｄｓＡｃｋＶａｌｕｅ（ｍ，  ｓ，  ｎ）の直後に続かなければならない。この定義は、ｎを、モジュールｍの出力ストリームｓから開始されるポイント・トゥ・ポイント接続に対するフォワードアクノレッジメント値として指定する。ｐｒａｇｍａコマンドは、モジュールｍの出力ストリームｓから開始されるポイント・トゥ・ポイント接続に対するバックワードアクノレッジメント値としてｎを指定するモジュール定義
＃ｐｒａｇｍａ  ＢｗｒｄｓＡｃｋＶａｌｕｅ（ｍ，  ｓ，  ｎ）
の直後に続かなければならない。ｐｒａｇｍａコマンドは、モジュール定義の直後に続かなければならない。
【０２０２】
上述の概念のいくつかの利点例は、スレッドおよびマルチスレッド、すなわち、複数スレッドの並列実行のサポートである。また、ＳＩＭＤ、ＭＩＤＭ、命令レベル、タスクレベル、データ並列性、データフロー、およびＳｙｓｔｏｌｉｃ等の並列性のすべての形態が表現可能である。決定的挙動がデフォルトである。非決定性は、プログラムに必要な場合にのみ明示的に追加される。なぜなら、ソフトウェアテストの容易性および信頼性をより効果的なものとするものは、逐次プログラミングの中にあるためである。上述した概念は、明示的な並列的構文を有さない。並列性は、普通の逐次コードに、少なくともシンタックスの面で、類似するストリームドメインにおけるコードから抜け落ちてしまう。したがって、スレッドドメインで作業するプログラマは厳格にシーケンシャル問題に集中してよい。プログラミングモデルは、モデルに基づく設計およびモデルに基づくテストに役立ち、プロセシングコアの任意の個数に応じて拡大縮小する。プログラミングモデルは、プロセシングコアを隔てる距離がナノメートル単位の場合にも千キロメートル単位の場合にも、等しく適用可能である。フォアグラウンドまたはバックグラウンドのタスクは存在せず、ただタスクのみが存在し、割込またはメッセージ引き渡しも存在せず、ただストリームのみが存在する。
【０２０３】
本発明は、本発明に係る特定の実施形態に関して説明してきたが、これらの実施形態は単に例示に過ぎず、本発明を限定するものではない。例えば、任意の種類の処理ユニット、機能的回路または、１つまたは複数のユニットの集合、および／またはメモリ、Ｉ／Ｏ素子、その他等のリソースがノードに含まれ得る。ノードは、簡単なレジスタでもよく、または、デジタル信号処理システム等のように、より複雑なものであってもよい。本明細書に説明したものではなく、他の種類のネットワークまたは相互接続方式も用いられ得る。本発明の特徴または態様は、好適な実施形態に関して本明細書で説明された適合システム以外のシステムにおいても達成され得る。
【図１】
0005990466.tif 000013
【図２】
0005990466.tif 000014
【図３】
0005990466.tif 000015
【図４】
0005990466.tif 000016
【図５】
0005990466.tif 000017
【図６】
0005990466.tif 000018
【図７】
0005990466.tif 000019
【図８Ａ】
0005990466.tif 000020
【図８Ｂ】
0005990466.tif 000021
【図８Ｃ】
0005990466.tif 000022
【図８Ｄ】
0005990466.tif 000023
【図９Ａ】
0005990466.tif 000024
【図９Ｂ】
0005990466.tif 000025
【図１０】
0005990466.tif 000026
【図１１Ａ】
0005990466.tif 000027
【図１１Ｂ】
0005990466.tif 000028
【図１１Ｃ】
0005990466.tif 000029
【図１２】
0005990466.tif 000030
【図１３Ａ】
0005990466.tif 000031
【図１３Ｂ】
0005990466.tif 000032