以下に添付図面を参照して、本発明にかかる設計支援装置、および設計支援方法の実施の形態を詳細に説明する。
図1A〜図1Cは、設計支援装置による一動作例を示す説明図である。設計支援装置100は、複数のスレッドが連携して並列動作する際の制御を行う並列動作用のソフトウェアの作成を支援するコンピュータである。
従来、開発者が、データフローやデータフローに基づきガントチャートなどの詳細な仕様を決定した後に、並列動作用のソフトウェアを作成しなければならなかった。そのため、開発者は複数の並列に動作する並列動作処理を行うソフトウェアを作成するのが困難である。データフローとは、処理を表すノード集合と、データ送信元ノードから送信先ノードへの矢印集合で定義される、有向グラフのことである。通例、ノードは並列に動く。並列に動作する処理のことをスレッドとも呼ぶ。本実施の形態では、データフロー制御を例とするが、本質的には、複数ノード、換言すると、複数スレッドの連携制御の一事例として、データフロー制御の例を示している。本発明における、ノードとは、実装上は、スレッドとスレッドに渡す変数値をオブジェクト化したものを指している。このため、以下の説明では、文脈によって、ノードのことを、スレッドとよんでいることがある。
101に、開発者の頭の中のイメージ例を示す。本設計支援装置100は、開発者の直観に近い開発を行える環境を提供するものである。効率的な開発をするためには、直観的に開発できることが必要である。そのため、開発方法において要求する入力が、開発者のイメージに近いことが望ましい。そこで、最初に、開発者が有するイメージを想定したい。開発者は、まず、機能を思い浮かべるはずである。つぎに、この機能を実現するために必要な機能を見つける。例えば、開発者は、すべてコプロセッサで実装し、コプロセッサは、コプロセッサ0〜3を使おう、と決める。つぎに、全体の機能を得るためにはデータのやり取りが必要であるため、開発者はデータフロー図について思考することが推測される。データフロー図は例えばイメージ101中の左側の図である。データフローを実現するために、コプロセッサ間でのデータやり取りが必要になる。そこで、例えば、本実施の形態では、ダブルバッファを用意する。ダブルバッファは、イメージ101中の右側の図である。このようにして、開発者は、ソフトウェアの開発時に大まかな実現イメージを持つことが想定される。複数のスレッドを連携させて1つの機能を実現することを考える場合、複数スレッドの連携全般において、このような発想をすると推測できる。しかし、従来の実現方法では、このイメージをさらに詳細化する必要があった。例えば、実現方法の一例については、図2に示す。
そこで、本実施の形態では、開発者のイメージに近い入力を可能にする枠組みを予め用意する。枠組みでは、部品を用意する。部品は、102abcであり、記憶部102にテンプレートが記憶される。部品102abcは、同期クラス102a、同期ポートクラス102b、基本ノードクラス102cを有する。記憶部102は、例えば、ROM(Read Only Memory)、RAM(Random Access Memory)、ディスクなどである。基本ノードクラス102cは、スレッドを実現するための基本クラスであり、ノードと、ノードに関するデータと、を有する。この基本ノードクラス102cが継承されると、具体的なノードが実現できる。
同期クラス102aは、関係づけられた複数のスレッドの同期をとる同期手段である。同期クラス102aは、同期条件を有し、あるスレッドが他のスレッドと同期をとるための関数sync()を有する。関数sync()が呼ばれると、同期条件の充足を判定する。同期条件とは、例えば、データ転送が破綻しない条件である。同期条件が充足していなければ、関数sync()の中で、実行をスレッドに返す。つまり、関数sync()から抜けない。同期条件が充足していれば、関数sync()を抜けて、そのスレッドの処理を継続する。つまり、すぐに関数sync()から抜ける。さらに、関数sync()の中で、止まっている他のスレッドのうち、同期条件を満たすものがあれば、そのスレッドを起こす。つまり、前記同期条件が整うまで、関数から抜けるタイミングを遅延させる。これにより、上流スレッド(上流ノード)は下流スレッド(下流ノード)に起動をかけ、その起動は、同時期条件を満たしたタイミングで下流スレッドに伝搬したのと同等の効果がある。
同期ポートクラス102bは、同期クラス102aのインスタンスである、同期オブジェクトと、基本ノードクラスの継承クラスのインスタンスである、ノードオブジェクトをつなぐための部品である。
つぎに、図1Bの100に、設計支援装置のフローを示す。この装置では、対話的に、ノードクラスの具体化110と、プログラムコード104を作成する。プログラムコード104は、複数スレッドの連携を定義した記述(ここでは、データフロー記述)である。この記述をする際に、動作可能な部品である、部品102abcを使っているので、コンパイル(112)可能である。プログラムコード104を、コンパイル(112)すると、実行形式105を得ることができる。これを実行すれば、開発者が定義した、複数スレッドが連携した挙動が得られる。したがって、設計支援装置100全体を見ると、本質的には、複数スレッドの連携(データフロー)に関する定義と、同期方法の選択(具体的には、同期クラスの選択)と、ノードの詳細動作記述(ノードクラス)を入力して、並列動作用プログラムを生成する装置、となっている。
図1Bでは、並列動作用のプログラムコード105を、コンパイル112によって生成するとしたが、インタプリタ―で動作するものであってもよい。いずれにせよ、複数のスレッドを並列に動作させる並列動作用のソフトウェアが簡単に得られる。
つぎに、図1Cに、デバッグ用に拡張した設計支援装置のフローを示す。本発明では、プログラムコード104を生成し、実際に、複数スレッドが、同期条件を充たしつつ、並列動作する、実行形式105が生成される。したがって、部品の一部に、重要な出来事を出力する機能を埋め込んでおけば、実行履歴106を生成することができる。実行履歴106を可視化すると、各スレッドの動作状況がわかるデータ、ガントチャート107を表示できる。
実行環境113としては、色々な形態が考えられ、例えば、実機、シミュレーション環境、コンピュータなどが挙げられる。実機とは、開発対象のシステムである。実機の場合には、出力データを取り出す方法が必要になる。ただし、標準出力は取り出せる場合が多いので、ここでは、標準出力として、実行履歴を出力する方法を示す。ただし、この方法に限るものではない。シミュレーション環境も実機と同様である。コンピュータを利用する場合というのは、プログラムコード104を、コンピュータ上で実行できるようにコンパイルして、実行することをいう。本実施の形態では、スレッドプログラミングを用いた実施例を示すが、コンピュータ上のOSがスレッドプログラミングに対応していれば、実行可能である。もちろん、コプロセッサとのやり取りはそのままでは実現できないので、そういった機能が含まれる場合には、対応が必要であるが、複数スレッドの連携動作の再現は可能である。ここでの連携動作とは、データフロー動作である。
したがって、本実施の形態では、実機でのデバッグにも有益であり、仕様検討において複数スレッドの連携動作をコンピュータ上で実行したい、といった用途にも、有益である。また、仕様検討から実機デバッグまで、同一の複数スレッドの連携制御を利用することも可能である。このように利用すれば、主要な部分が同一なので、比較検討が容易になる。
本実施の形態によれば、並列動作用のソフトウェアの作成の容易化を図ることができる。複数スレッドの連携制御に適した手段を提供しているので、ソフトウェア開発コストの削減を図かることができる。連携制御とは、例えば、データフロー制御である。ソフトウェア内で複数スレッドの連携動作が実現されるので、実機上でも複数スレッドの連携が動作することになり、実機におけるデバッグの容易化を図ることができる。連携動作とは、例えば、データフロー動作である。
最終的なソフトウェアと同じ、複数スレッドの連携制御を、コンピュータ上で実行し、直観的に理解することができる。そして、抽象的なシミュレーションとして仕様検討に利用できるので、複数スレッドの連携制御の仕様検討にかかる工数の削減を図かることができる。このため、仕様検討から実機デバッグまでを、同一の、複数スレッドの連携制御をもつ、ソフトウェアで実行できる。そして、開発コスト削減と、結果の比較分析と、の容易化を図ることができる。以上のように、ソフトウェア開発の様々な面で、ソフトウェアの作成の容易化を図ることができる。
スレッドの機能は、コプロセッサで実現されていてもよいし、スレッドの中から呼ばれるソフトウェアで実現されていてもよい。つまり、スレッドではコプロセッサの制御のみを行う。このため、機能がコプロセッサで実装されていても、ソフトウェアで実装されていても、混在していても、複数スレッドの連携制御が可能なのである。また、本実施例でいう、コプロセッサとは、メインのCPUを補助する回路全般をさしている。一般には、GPU(Graphics Processing Unit)のような汎用的なものをさすことが多いが、ここでは、JPEGコーデック回路などのように特定の処理を行う回路もさす。
つぎに、従来との比較をすることにより、どのように、開発者のイメージに近いのか、を示しておきたい。図2〜図4に、従来のソフトウェア開発フローや従来と本発明との違いを示す。以降の説明では、複数コプロセッサの連携動作を実現する並列動作用のプログラムコードである実行形式105の例を示す。
図2は、ソフトウェア開発フローの一例を示す説明図である。まず、図2(1)に示すように、開発者は、所望の動作をするソフトウェアを開発するために、機能要件などのソフトウェアの仕様を決定する。機能要件はいくつかのスレッドなどの処理に分けられる。スレッドは、例えば、コプロセッサに対応付ける。スレッドを組み合わせて、一連のソフトウェアの処理が実現される。スレッドを組み合わせるためには、処理間の関係を定義しないといけない。処理間の関係とは、例えばスレッドAがスレッドBにデータを送るというようなスレッド間の依存関係である。そこで、図2(2)に示すように、例えば、開発者は、仕様に基づいて、スレッド間のデータ転送関係が定義されたデータフローなどを作成する。データフローにおいて、例えば、スレッドAはコプロセッサC0に対応付けられ、スレッドBはコプロセッサC2に対応付けられ、スレッドCはコプロセッサC1に対応付けられ、スレッドDはコプロセッサC3に対応付けられる。
そして、開発者は、データの受け渡しが正しくできる制御を考える。制御されるコプロセッサは、例えば、休止状態から起動、動作状態に遷移、処理、処理終了後の割り込みを上げる動作、休止状態に戻るという一連の動作を行う。
通例、処理の中でデータが生成される。データを正しく受け渡すためには、なんらかの制御が必要になる。例えば、2つのコプロセッサA,Bがあって、Aが一連の処理結果データDを生成し、BはDを入力として一連の処理を実行する、という場合がある。これは、一連の処理を単位として、パイプラインが構成していることに他ならない。このような構造をタスクパイプラインと呼ぶ。一般的なパイプラインは、前段の処理と後段の処理の間にフリップフロップをおくことで、データの受け渡しをしている。フリップフロップは、概念的には、2個のラッチが縦続された構成になっている。前段のラッチは、前段の処理が専有的に利用できる。後段のラッチは、後段の処理に、後段の処理が利用するデータを、1サイクル期間、安定的に供給する。クロックエッジが入ったタイミングで、前段ラッチの値は、後段のラッチにコピーされる。このため、フリップフロップの前段の処理と後段の処理を独立的に考えて設計しても、データが正しく受け渡される。
タスクパイプラインにおいても、通常のパイプラインと同様に、前段の処理と後段の処理を独立的に扱えるようにしたい。このためには、データを正しく受け渡すしくみが必要である。これを実現する方法として、ダブルバッファや、リングバッファがある。ダブルバッファとは、前段の処理から後段の処理に受け渡す最大データサイズよりも大きいメモリ領域を2個用意したメモリのことである。ここでは、各々のメモリ領域を、A面、B面と呼ぶことにする。前段の処理は、処理結果をA面に書く。後段の処理は必要データがないので休んでいる。前段の処理が一連の処理を終えると、A面に、後段の処理が必要なデータが存在することになる。
そこで、システムは、前段の処理の終了を検出して、前段の処理と後段の処理を起動する。A面には後段の処理が必要なデータがあるので、前段の処理は、処理結果をB面に書くようにする。つぎに、システムは、前段の処理の終了と後段の処理の終了とを検出して、前段の処理と後段の処理を起動する。この時、前段はA面に、後段はB面に、アクセスするようにする。このように入れ替えれば、後段の処理が必要なデータを前段の処理が壊してしまうことが起きない。つまり、正しいデータの受け渡しが可能になる。リングバッファとは、ダブルバッファのデータ領域数が2個だったのを、N個に拡張したものである。本実施例では、説明の簡単化のため、ダブルバッファの場合のみを取り上げる。
以上より、タスクパイプラインでは、パイプラインを構成する処理(タスク)が、処理中か否かが重要であることがわかる。処理中か否かを変更するイベントは、起動と、処理終了後の割り込みを上げる動作と、の2つである。そのため、図2(3)に示すように、開発者は、各スレッドの動作期間をバーで示すことができるタスクスケジューリング図を作成してレビューを行う。なお、タスクスケジューリング図は、ガントチャートとも称する。
その後、開発者は、図2(1)〜図2(3)などの分析作業が終了すると、実装仕様を決定する。
図3は、ガントチャートとシーケンス図の一例を示す説明図である。ここでは、1つのコプロセッサの動作が繰り返し実行される例である。ガントチャートでは、コプロセッサは動作中と非動作中とのいずれか一方で示される。しかし、プロセスがコプロセッサに対応付けられた場合、シーケンス図に示すように、制御用のソフトウェアを実行するCPU(Central Processing Unit)と、コプロセッサと、が協調動作することによってプロセスが実現される。CPUとコプロセッサとの間の通信は設定レジスタへの書き込みと割り込みとによって行われる。CPUがコプロセッサに動作指示する際には、CPUがコプロセッサの設定レジスタを起動状態に設定する。コプロセッサがCPUに動作の終了を通知する際には、コプロセッサがCPUに割り込み信号を送る。
図3に示すように、コプロセッサが1つであれば、コプロセッサを制御する制御用のソフトウェアは単純である。また、同様に、制御対象となるスレッドが1つであれば、制御用のソフトウェアは単純である。しかし、スレッドが複数ある場合やコプロセッサが複数ある場合、コプロセッサ間の制御が行われる。図2(3)に示したように、例えば、Aが終わった後に、A,B,Cをスタートさせるなどがある。スレッドが複数ある場合やコプロセッサが複数ある場合、制御用のソフトウェアは複雑となる。例えば、全体を1つの状態としてとらえて、割り込みなどで状態遷移させ、各々の状態で、所定の設定を行うようなFSM(Finite State Machine)を作成しなければならない。
そのため、制御対象が複数ある制御用ソフトウェアには以下(a)〜(c)のような問題点がある。
(a)可読性:悪い
(b)記述量:多い
(c)デバック:高コスト
そこで、本実施の形態では、制御対象が複数ある場合における並列動作用のソフトウェアの開発の効率の向上を図る。
上述したように、タスクパイプラインの場合、データの流れと、データ受け渡しが正しく行える制御が重要である。この制御を実現するためには、複数スレッドを連携させる仕様を作ることと、その使用を実装することが困難である。このため、開発者が、機能を考えるうえで、イメージするものが入力となるような設計環境があれば、格段に生産性が向上すると推測できる。また、図2によると、開発者のイメージは、図1Aの101のようなものであると推測できる。そこで、データフロー(複数スレッドの連携)を入力とする、データ受け渡しが正しく行えることが保証されることを特徴とした、設計支援システムを考案することを目的とする。
さらに、デバッグの場合、ソフトウェア自体が、データフロー(複数スレッドの連携)にそった動作をしているほうがわかりやすいことが推定される。従来の方法で、デバッグをする場合、開発者が、状態と、割り込みに関する情報などを使って、解析しなければならないと推測できる。この推測作業は、開発者に負荷がかかる作業である。このような解析をしなくてよくする方法として、ソフトウェア自体が、データフロー(複数スレッドの連携の1つ)として、動作すれば簡単になる。このようにして、ソフトウェアの上では、仮想的なコプロセッサが連携して、動作している状態になる。このような理由から、ソフトウェア上で、複数スレッドが連携するソフトウェアが生成する方法は、デバッグの面からも好ましいと推測される。
以上より、複数スレッドの連携を入力とし、実行時においても、複数スレッドの同期条件に合わせて、動作する、ソフトウェア(プログラムコード105)を生成するしくみを考案することにした。このしくみから、使用方法を見直すと、スレッドの機能は、コプロセッサで実現する、または、ソフトウェア(スレッド自体)で実現する、の2通りがあることになる。以下、コプロセッサの場合を説明する。最後に、ソフトウェアの場合について、説明する。
図4は、本実施の形態と従来技術との簡単なフローによる処理の比較例を示す説明図である。従来技術では、実装されたソースプログラムがコンパイルされてCPUが実行可能なバイナリが生成される。そして、従来技術では、生成されたバイナリがCPUにロードされる。
本実施の形態では、複数スレッドの連携(例えば、データフロー)を部品102abc(図中では「本発明ライブラリ」)で記述したプログラムコード104を入力とし、入力したプログラムコード104と、コンパイル環境に含まれる、部品102abc(図中では「本発明ライブラリ」)とを組み合わせて、コンパイルする。これにより、実行形式105が生成される。そして、生成されたバイナリがCPUにロードされる。これにより、図中CPUで、複数スレッドの連携動作が実現される。スレッドにコプロセッサC0〜C3が含まれている時には、コプロセッサもスレッドとして、適切に制御される。スレッドにコプロセッサC0〜C3が含まれている時とは、具体的に、あるスレッドに、あるコプロセッサを制御する記述がなされている時である。
(設計支援装置100のハードウェア構成例)
図5は、設計支援装置のハードウェア構成例を示すブロック図である。図5において、設計支援装置100は、CPU501と、ROM502と、RAM503と、ディスクドライブ504と、ディスク505と、を有する。設計支援装置100は、I/F(Inter/Face)506と、キーボード507と、マウス508と、ディスプレイ509と、を有する。また、CPU501と、ROM502と、RAM503と、ディスクドライブ504と、I/F(Inter/Face)506と、キーボード507と、マウス508と、ディスプレイ509とは、バス500によってそれぞれ接続される。
ここで、CPU501は、設計支援装置100の全体の制御を司る。ROM502は、ブートプログラムなどのプログラムを記憶する。RAM503は、CPU501のワークエリアとして使用される。ディスクドライブ504は、CPU501の制御にしたがってディスク505に対するデータのリード/ライトを制御する。ディスク505は、ディスクドライブ504の制御で書き込まれたデータを記憶する。ディスク505としては、磁気ディスク、光ディスクなどが挙げられる。
I/F506は、通信回線を通じてLAN(Local Area Network)、WAN(Wide Area Network)、インターネットなどのネットワーク510に接続され、このネットワーク510を介して他の装置に接続される。そして、I/F506は、ネットワーク510と内部のインターフェースを司り、外部装置からのデータの入出力を制御する。I/F506には、例えばモデムやLANアダプタなどを採用することができる。
キーボード507やマウス508は、開発者などの利用者の操作により、各種データの入力を行うインターフェースである。ディスプレイ509は、CPU501の指示により、データを出力するインターフェースである。
また、図示を省略するが、設計支援装置100には、カメラから画像や動画を取り込む入力装置やマイクから音声を取り込む入力装置が設けられていてもよい。また、図示を省略するが、設計支援装置100には、プリンタなどの出力装置が設けられていてもよい。
また、本実施の形態では、設計支援装置100のハードウェア構成として、パーソナル・コンピュータを例に挙げているが、これに限らず、サーバなどであってもよい。設計支援装置100がサーバである場合、設計支援装置100と利用者の操作可能な装置とがネットワーク510を介して接続されてもよい。
(設計支援装置100の機能的構成例)
図6は、設計支援装置の機能的構成例を示すブロック図である。設計支援装置100は、記憶部102と、第1生成部601と、実行部602と、第2生成部603と、表示部604と、を有する。記憶部102は、例えば、ROM502、RAM503、ディスク505などの記憶装置によって実現される。また、記憶部102は、I/F506を介してアクセス可能な他の装置のRAM、ROM、ディスクなどであってもよい。
第1生成部601から表示部604までの制御部の処理は、例えば、図5に示すCPU501がアクセス可能なROM502、RAM503、ディスク505などの記憶装置に記憶されたプログラムにコーディングされている。そして、CPU501が記憶装置から該プログラムを読み出して、プログラムにコーディングされている処理を実行する。これにより、制御部の処理が実現される。また、制御部の処理結果は、例えば、ROM502、RAM503、ディスク505などの記憶装置に記憶される。
まず、記憶部102には、同期クラス102aと、基本ノードクラス102cが記憶されてある。同期クラス102aは、同期クラスを示す記述情報である。同期クラスは、このクラスのインスタンスである同期オブジェクトに接続されるノードから同期を求める依頼(後述sync())を調停する処理であって、例えば、ノード間のデータ転送で用いられるバッファ構造に合わせた制御を実現する。この場合の同期とは、問い合わせてきたノードの処理が先に進んでも、バッファ上のデータが破壊されない、ことを指す。例えば、ダブルバッファの場合、同期オブジェクトに接続されたノードオブジェクトがすべて、同期オブジェクトに同期を依頼してきた(sync()をコールしている)時、任意のノードオブジェクトは処理を先に進めても、ダブルバッファは破綻しない。このように、同期クラスは、このインスタンスである、同期オブジェクトに対する、同期を求めるメッセージを利用して、同期条件(ここでは、バッファが破たんしない条件)が整わない間、ノードの実行が先に進まないように制御するしくみを提供している。同期クラスと同期オブジェクトについては、図12〜図14を用いて後述する。また、ここでは、同期条件は、バッファが破たんしないことだったが、同期条件は、これに限らない。
基本ノードクラス102cは、ノードクラスの基本クラスを示す記述情報である。基本ノードクラスを継承してノードクラスを作成し、作成したノードクラスがインスタンスされると、ノードオブジェクトとなる。ノードクラスには、スレッドにするメンバー関数run()がある。run()に、このノードでやるべき処理を書く。run()はスレッドとして、実行される。このため、本実施例では、ノードのことを文脈によってはスレッドとよんでいる場合がある。ノードクラスとノードオブジェクトについては、図9〜図11を用いて後述する。
つぎに、第1生成部601は、プログラムコード104を取得する。プログラムコード104には、ノードオブジェクトと同期オブジェクトを所定の方法で接続したコードが含まれている。ノードオブジェクトは、run()の中で、同期したい箇所で、同期オブジェクトのメンバー関数sync()が呼ばれる。より具体的には、run()の中で、同期ポートオブジェクトを介してメンバー関数sync()が呼ばれる。
同期オブジェクトは、sync()が呼ばれると、同期条件が成り立つかを判定する。同期条件が成り立てば、同期条件が成り立つのを待っているスレッドを起こす。より具体的には、スレッドカーネルに、他のスレッドを起こす信号を送る。後述する図13の例はこのようになっている。後述する図29Bでは、sync()に到達した順にsync()から抜けるようにするため、同期条件が成り立ったスレッドは最後にsync()を抜けるように設計されてある。これにより、sync()で、同期条件が成り立たず、実行が停止していたスレッドは、sync()関数を抜けて、先に進む。同期条件が成り立たない場合には、sync()の中で、同期条件が成り立った時に送られてくる信号を待つ。これにより、sync()を呼び出したスレッドは、実行待ちになる。run()では、逐次的に処理を記述するが、同期オブジェクトのsync()をコールした段階で、同期オブジェクトが、同期条件が成り立ったと判定するまで、sync()から抜けないように動作する。そのため、run()では、同期条件が成り立つまで、先の処理には進めない。したがって、同期条件を常に満たしつつ、複数スレッドの連携動作をさせることができるのである。プログラムコード104については、図15を用いて後述する。
図7は、データフロー例を示す説明図である。データフロー700は、ノードと、ノード間のデータの入出力関係を示す矢印と、を含む有向グラフである。データフロー700は、ノードsourceと、ノードPala[0]〜Pala[3]と、ノードsoloと、ノードsinkと、を有する。
ノードPala[0]〜Pala[3]は、それぞれノードsourceからデータを受け付ける。そして、ノードPala[0]〜Pala[3]は、それぞれノードsoloにデータを出力する。ノードsoloは、例えば、ノードPala[0]〜Pala[3]のそれぞれからデータを受け付ける。そして、ノードsoloは、ノードsinkにデータを出力する。
図8は、データフローのモデル化例を示す説明図である。本実施の形態では、2つのオブジェクトによってデータフロー700をモデル化し、モデル化されたデータフロー700を入力とする。ノードは、ノードオブジェクトによってモデル化される。そして、矢印間の依存関係は、同期オブジェクトによってモデル化される。これにより、同期オブジェクトが定める同期条件を充たすべきであることを定義したことになる。このようにして、開発者などの利用者は、データフロー700が示す入出力関係を同期オブジェクトとノードオブジェクトによって表したプログラムコード104を作成する。
図9は、ノードオブジェクト例を示す説明図である。本実施の形態では、ノードオブジェクトは、例えば、コプロセッサの制御を表すオブジェクトである。そのため、ノードオブジェクトは、IN待ち、設定、割り込み待ち、OUT待ちの4つの処理を順に行う。
IN待ちは、上流ノードからの起動を待つ処理である。上流ノードからの起動を待つと書いたが、具体的には、上流ノードと接続している同期オブジェクトのsync()から、復帰するのを待つ。設定は、コプロセッサへの設定を行う処理である。割り込み待ちは、コプロセッサからの処理の終了を通知するための割り込みを待つ処理である。OUT待ちは、下流ノードへの起動を指示する処理である。下流ノードへの起動指示を待つ処理と書いたが、具体的には、下流ノードとつながっている同期オブジェクトのsync()から復帰するのを待つことを指す。このように、実際には、同期オブジェクトのsync()関数で、同期クラスが定める同期条件が成り立つまで遅延する。これは、観念的には、適切なタイミングで、上流ノードから下流ノードに起動を送る機能に相当する。このため、以下の説明でも、観念的な説明をする場合には、同期の代わりに、起動とも呼ぶ。
図10は、ノードクラス例を示す説明図である。本実施の形態では、pthreadプログラミングを利用した例を示す。信号待ちによる同期を実現するためにノードごとに制御用のスレッドを用意する。そのため、ノードクラスは、run()を有する。実際には、後述のとおり、図24のcreate()で、自分自身へのポインタを、クラス関数run_baseに渡して、これをスレッドとして実行、run_baseの中で、引数でもらったポインタが指すオブジェクトのrun関数を呼ぶ。これは、単に、pthreadの仕様に合わせるためのものなので、事実上、メンバー関数run()をスレッドとして実行している。すべてのノードのrun()は、最初に、スレッド関数として呼び出される。具体的には、図33の//createの後の記述のように、すべてのノードに対して、create()を呼ぶ。つぎに、すべてのスレッドが終わったのを検出する。具体的には、//joinの後の記述のように、すべてのノードに対して、join()を呼ぶ。
run()は、上流ノードからの起動を待つ処理と、setup()と、コプロセッサの終了を待つ処理と、下流ノードに起動をかける処理と、を有する。下流ノードに起動をかける処理が終了した後は、上流ノードからの起動を待つ。
図10に示すように、起動をかける処理と起動を待つ処理との間の期間は、おおむね、コプロセッサの休止期間である。また、setup()やコプロセッサの終了を待つ処理の期間は、おおむね、コプロセッサの動作期間である。
setup()では、コプロセッサへの設定を行う。コプロセッサの終了を待つ処理では、コプロセッサからの終了を示す割り込みを待つ。下流ノードに起動をかける処理では、前述のとおり、(下流側の同期オブジェクトの)sync()から抜けるのを待つ。上流ノードからの起動を待つ処理に移行する。上流ノードからの起動を待つ処理は、前述のとおり、(上流側の同期オブジェクトの)sync()から抜けるのを待つ。
図11は、ノードクラスの詳細例を示す説明図である。記述情報1100は、ノードクラスの記述例を示す。ノードクラスでは、図10においても説明したように、run()が定義されてある。点線枠内は、下流ノードへの起動をかける処理から上流ノードからの起動を待つ処理へのループ部分に相当する。
port_in.sync()は、上流ノードからの起動指示を待つための同期処理である。port_inは、同期INポートオブジェクトである。前述のとおり、本実施例では、同期ポートオブジェクトを介して、同期オブジェクトにアクセスする例を示している。実線枠内は、図10で説明したsetup()に相当する部分であり、コプロセッサへの設定を行う処理に相当する。図10の例では、setup()は、設定の代わりに、上流ノードからのデータを印字し、下流ノードにデータを送信する処理を行う。
guard()は、コプロセッサの終了を待つ処理であり、コプロセッサと同期を行う処理である(コプロセッサとguard()の関係については図25のとおり)。port_out.sync()は、下流ノードへ起動をかける処理であり、下流ノードと同期する処理である。port_outは、同期OUTポートオブジェクトである。前述のとおり、本実施例では、同期ポートオブジェクトを介して、同期オブジェクトにアクセスする例を示している。
図12は、同期オブジェクト例を示す説明図である。同期オブジェクトは、ノード間で、同期をとるソフトウェアである。概念的には、上流ノードから下流ノードへの起動の通知を伝搬する処理を行い、同期条件が揃うまで起動の通知を遅延させる、処理をしている。正確には、前述のとおり、同期をとるために、同期オブジェクトに関係するノードは、同期オブジェクトのメンバー関数sync()を呼ぶが、同期オブジェクトは、条件が整うまで、sync()関数からの復帰が遅延される。
同期条件は、例えば、同期オブジェクトに関係するすべてのノードオブジェクトが同期オブジェクトに対してOUT待ち、またはIN待ちになっていることである。ダブルバッファによるデータ転送で、正常なデータ転送が行えるための条件である。このタイミングでバッファを入れ替えれば、必要なデータを破壊することは起きない。
図13は、同期クラスsync_tのメンバー関数sync()を示す説明図である。記述情報1300に、sync()の記述例を示す。sync_tには、sync()に入ったノードの数を記憶するメンバー変数m_reach_numがある。この変数は、sync_tのコンストラクタで0が代入される。また、ここでは図示していないが、sync_tには、メンバー変数m_point_numがある。プログラムコード104で、複数スレッドの連携(例えば、データフロー)を記述した部分が実行されると、接続されるノードの数を計数して、m_point_numに格納する。
sync()は、コールされると、m_reach_numを1増やす。m_reach_numがm_point_numよりも小さかったら、m_condを待つ。m_reach_numがm_point_numと等しかったら、すべてのノードがsync()に入ったことになるので、m_reach_numをクリアして、m_condをブロードキャストする。これにより、m_condを待っていたスレッドは、待ち状態を終了する。したがって、順次、sync()を抜ける。このようにして、すべてのノードが、sync()に入るまで、sync()から抜けないようにすることができるのである。
図14は、同期クラス例を示す説明図である。記述情報1400には、sync_tが記述される。図13のsync()は、sync_tのメンバー関数である。ここでは、上流ノードと下流ノードの間で、パラメータ転送ができる記述例について、説明する。パラメータとは、例えば、一連の処理の終了フラグなどである。
実施例の同期オブジェクトは、同期条件が、ダブルバッファで正しくデータ転送が行えることなので、ノード間のパラメータ転送も、ダブルバッファにすればよい。sync_tがノード中のOUTポートに接続されるごとに、add_out()がコールされ、同期オブジェクトの入力側ノード数分のダブルバッファが用意される。sync()で同期が取れると、update_value()が呼ばれる。これにより、上流ノード用バッファと、下流ノード用バッファが入れ替わる。このようにして、ノード間でのパラメータのやり取りを実現する。なお、update_value()が呼ばれるタイミングは、ダブルバッファを安全に切り替えられるタイミングである。
図15は、データフローの記述例を示す説明図である。記述情報1500は、データフロー700を同期オブジェクトとノードオブジェクトとによって記述したプログラムコード104である。上述したように、データフロー700のモデル化については、開発者によって行われる。本実施の形態では、開発者が、ノードオブジェクトと、同期オブジェクトと、を使ってデータフロー700を定義する。記述情報1500には、ノードの宣言、同期の宣言、ノードと同期の接続、とが記述される。
つぎに、第1生成部601は、部品102abcと、ノードクラス103と、プログラムコード104と、を組み合わせた並列動作用のプログラムコード105を生成する。第1生成部601は、コンパイルを行うことによって実行形式のバイナリコードを生成してもよい。ここでは、並列動作用のプログラムコード105は、コンパイル後のバイナリコードとして説明する。
図16は、実行例を示す説明図である。コプロセッサに対する設定部分に対して標準出力を記載した場合における実行結果例である。
ノードオブジェクトと、同期オブジェクトと、によれば、開発者が、データフロー700を直感的にコーディングすることができる。また、ノードオブジェクトは並列に動作することができる。コプロセッサを制御するプログラムはノードクラスの中に記述される。ノードオブジェクトが割り込みを受理できる。
ここで、同期オブジェクトによる動作の詳細例について、図17から図19を用いて説明する。本実施の形態では、ダブルバッファでデータのやり取りを実現する場合の同期クラスを例に挙げているが、これに限らず、リングバッファに対応した同期クラスを定義してもよい。また、本実施の形態は、より本質的には、同期条件を示すクラスを導入して、そのクラスとスレッドの関係を与えることにより、連携させるしくみである。したがって、上述した例は、コプロセッサの連携の制御であったため、連携のさせ方がデータフローで、同期条件がバッファに関するものである。したがって、一般的には、連携のさせ方は、データフローに限らないし、同期条件は、バッファに関する条件に限らない。
図17は、同期オブジェクトの利用例を示す説明図である。ここでは、ノードオブジェクトは、N0〜N3まである。同期オブジェクトSync_0は、ノードオブジェクトN0とノードオブジェクトN1からの起動指示を同期させてノードオブジェクトN2とノードオブジェクトN3に伝搬させる。換言すると、N0〜N3のすべてが、Sync_0のメンバー関数sync()に入っている状態を検出し、順次、sync()から戻る。
図18A〜図18Eは、同期オブジェクトの動作例を示す説明図である。図19は、同期処理のシーケンス例を示す説明図である。ノードN0〜ノードN3は並列に動作するスレッドとする。ここでは、図19に示すように、2回同期した例を示す。通例、guard()ではコプロセッサの終了を知らせる割り込みを待つため、このタイミングで、スレッドが実行待ちになる。したがって、スレッドカーネルに制御が戻る。しかし、ここでは、動作の説明の簡単化のため、guard()で割り込み待ちをしない例を示す。このようにしても、同期オブジェクトの動作を説明するうえで、差し支えはない。なお、図18A〜E、図19の同期クラスは、図13の定義ではなく、図29A〜Bの定義の動作になっている。
図18Aの(A)は初期状態である。図18Aの(A)のとおり、ノードオブジェクトN0とノードオブジェクトN1とについては、IN側の同期はないが、OUT側の同期がある。
また、図18Aの(A)のとおり、ノードオブジェクトN2とノードオブジェクトN3とについては、IN側の同期があるが、OUT側の同期がない。
図18Aの(B)では、ノードオブジェクトN0は、lock()、setup()、guard()、unlock()を(1)実行する。各ノードオブジェクトのlock()、setup()、guard()、unlock()の詳細例については、<詳細例>に示す。そして、ノードオブジェクトN0は、Sync_0.sync()に入る。同期条件が整っていないので、Sync_0.sync()の中で、(2)同期待ちに入る。
図18Bの(C)では、ノードオブジェクトN1は、lock()、setup()、guard()、unlock()を(3)実行する。そして、ノードオブジェクトN1は、Sync_0.sync()に入る。同期条件が整っていないので、Sync_0.sync()の中で、(4)同期待ちに入る。
そして、図18Bの(D)では、ノードオブジェクトN2が、Sync_0.sync()に入る。同期条件が整っていないので、Sync_0.sync()の中で、(5)同期待ち状態になる。つぎに、図18Cの(E)では、ノードオブジェクトN3が、Sync_0.sync()に入る。ここで、同期条件が整うので、同期待ちをしている、(6)他のスレッドを起こし、m_release_numに1を格納する。ノードオブジェクトN3が最後にSync_0.sync()に入ったスレッドなので、(7)最後のスレッドを起こす信号待ち状態にする。他のスレッドを起こすとは、スレッドカーネルに同期信号を送信させることである。
そして、図18Cの(F)では、スレッドカーネルが(8)ノードオブジェクトN0を起こす。これにより、ノードオブジェクトN0は、同期待ち状態を抜ける。m_release_numをインクリメントする。ノードオブジェクトN0は、lock()、setup()、guard()、unlock()を(9)実行する。そして、ノードオブジェクトN0は、Sync_0.sync()に入る。同期条件が整っていないので、Sync_0.sync()の中で、(10)同期待ち状態になる。
つぎに、図18Dの(G)では、スレッドカーネルが(11)ノードオブジェクトN1を起こす。これにより、ノードオブジェクトN1は同期待ち状態を抜ける。m_release_numをインクリメントする。そして、ノードオブジェクトN1は、lock()、setup()、guard()、unlock()を(12)実行する。ノードオブジェクトN1は、Sync_0.sync()に入る。同期条件が整っていないので、Sync_0.sync()の中で、(13)同期待ち状態になる。
そして、図18Dの(H)では、スレッドカーネルが(14)ノードオブジェクトN2を起こす。これにより、ノードオブジェクトN2は同期待ち状態を抜ける。m_release_numをインクリメントする。ここで、m_release_numがm_point_numと一致する。つまり、1回目の同期で、最後にSync_0.sync()に入ったスレッド以外のスレッドがすべて起きたことになる。そこで、(15)最後のスレッドを起こすよう指示する。つぎに、ノードオブジェクトN2は、lock()、setup()、guard()、unlock()を(16)実行する。ノードオブジェクトN2は、Sync_0.sync()に入る。ノードオブジェクトN2は、同期条件が整っていないので、Sync_0.sync()の中で、(17)同期待ち状態になる。
図18Eの(I)では、スレッドカーネルが、(18)ノードオブジェクトN3を起こす。これにより、ノードオブジェクトN3は同期待ち状態を抜ける。つぎに、ノードオブジェクトN3は、lock()、setup()、guard()、unlock()を(19)実行する。ノードオブジェクトN3は、が、Sync_0.sync()に入る。ここでは、同期条件が整うので、同期待ちをしている、(20)他のスレッドを起こし、m_release_numに1を格納する。ノードオブジェクトN3が最後にSync_0.sync()に入ったスレッドなので、(21)最後のスレッドを起こす信号待ち状態にする。
このようにして、同期オブジェクトは、同期条件が整うまで、sync()から抜けないようにする。これは、ノードオブジェクトN0とノードオブジェクトN1からの起動指示を同期させてノードオブジェクトN2とノードオブジェクトN3とに受け渡す動作を実現していると解釈できる。以上は、ダブルバッファの場合の同期例である。同期条件は、バッファの形態によって変わる。もし、リングバッファであれば、リングバッファのバッファ数を与え、同期オブジェクトへデータを送る側のsync()では、バッファの使用数が、バッファ数―1以下であればsync()を抜け、バッファ数と同じであれば、バッファの使用数がバッファ数―1以下になるのを待つ。一方、同期オブジェクトからデータを受け取る側のsync()では、すべてのリングバッファの使用数が1以上になるのを待つ。すべてのリングバッファの使用数が1以上になったら、sync()を抜ける。再度、sync()に入った時、すべてのリングバッファの使用数を1減らす。このようにすれば、リングバッファにも対応できる。以上のように、プログラムコード105を作成し、図4「本発明」中CPUで実行させると、図1Bに示したフローが実現する。
<ガントチャート作成例>
つぎに、デバッグ機能を追加した、拡張フロー(図1C)を説明する。本発明では、図4のCPUにロードされる実行形式105において、複数スレッドの連携動作(例えば、データフロー動作)そのものを実行しているといってよい。換言すれば、開発者がイメージしていたソフトウェアを、実際に動かしているとも解釈できる。イメージと実際の動作の差が小さいことになる。したがって、不具合の要因に気がつきやすく、デバッグが容易であるといってよい。さらに、開発者が不具合の要因に気がつきやすくなるためには、可視化が有効である。可視化を行うことにより、開発者は、実行形式105の動作を直観的に理解できる。例えば、ガントチャートによれば、制御される対象である、コプロセッサの動きを直観的に理解できる。ガントチャートは、スレッドごとに、状態変化を可視化したものなので、複数スレッドの連携動作(データフロー動作)に近い。
一方、従来の方法によると、開発者が、ガントチャートのようなデータを取得するためには、作成したソースをレビューしてどこに出力文を挿入すれば所望データがとれるかを検討し、再設計とソフトウェア改版をしなければならない。このように、開発者の手間がかかる。本実施の形態では、複数スレッドの連携動作(データフロー動作)をソフトウェアの中で実現しているので、部品の中に、予め、出力文を入れておけばよい。具体的には、ガントチャートを作る場合には、スレッドの実行開始と終了と、同期オブジェクトのsync()関数の前後、または、guard()の割り込み待ちの前後のタイミングで出力すれば、おおむね正しいガントチャートが作れるデータをそろえられる。
本実施の形態例における、同期クラスなどの詳細は後述する図28A〜図28Bの通りである。この中には、同期クラスのインターフェースクラス、同期クラス、同期オブジェクトと接続するための同期ポートクラスがある。さらに、同期ポートクラスには、2つの継承クラス、同期INポートクラスと同期OUTポートクラスがある。同期INポートクラスや同期OUTポートクラスは、ノードオブジェクトの中にインスタンスされる。インスタンス例については、後述する図30〜図32に具体例を示す。
図15の場合、ノードオブジェクトの中の同期ポートオブジェクトと同期オブジェクトの接続は、main()関数の中で行われている。例えば、i_src.port_out(src_pala);が、このような接続を行っている記述である。この接続では、同期OUTポートクラスport_out_tの中のメンバー関数operator()が呼ばれる。この関数の中で、後述する図28Bのように、同期オブジェクトのadd_out()がコールされる。これにより、同期オブジェクトに接続されているノードオブジェクトの情報を、同期オブジェクトに渡している(本実施例では、ノード間で、パラメータを受け渡すために、同期オブジェクト内にパラメータ用のデータ記憶領域を確保するために、使われているだけである)。同期INポートクラスについても同様である。
後述する図28Aによると、同期ポートクラスで、sync()が定義されている。したがって、同期ポートオブジェクトのsync()関数がコールされると、この同期ポートオブジェクトに接続されている同期オブジェクトのsync()がコールされる。このようにして、ノードオブジェクトと同期オブジェクトは通信している。したがって、図21の(1)の同期OUTポートクラスの変更例のように、sync()をオーバーライドして、この関数の中で、同期オブジェクトのsync()をコールする前後に、履歴を出力する記述を追加すれば、所望のデータが取れる。また、guard()に関する履歴をとる場合には、図24の、pthread_cond_wait()の前後に、履歴を出力する記述を追加すればよい。
このようにして出力した、実行履歴データは、後述する図20のように、コンピュータ100により受信され、実行履歴106に出力される。実行履歴106には、スレッドの状態が変化する実行履歴が含まれている。したがって、このデータからガントチャートを作ることができる。実施例では、標準出力を利用して、実行履歴をとる場合を示したが、これ以外の方法でもよい。実行履歴データに、タイマーの値を含めて、時間情報つきにしてもよい。図15や後述する図42のノードオブジェクトと同期オブジェクトについての後述する図44に示す接続関係データを使って、後述する図45のように、依存関係を矢印によって表示してもよい。後述する図44に示す接続関係データを落とす一方法は、記憶部102の中のライブラリをデータ構造出力用に変更したものを用意し、これとプログラムコード104をコンパイルして、接続関係出力用の実行形式を生成し、この実行形式を実行して、後述する図44に示す接続関係データを生成する方法が考えられる。
以上のように、本実施の形態によれば、簡単なしくみで、実行履歴が取れるのである。なぜ簡単に取れるかというと、実行形式105をCPUで実行すると、利用者がイメージする、つまり、開発者が見たいと考える、複数スレッドの連携動作が、CPU上で実現されるからである。したがって、上記のように、複数スレッドの連携を実現しているしくみのしかるべき場所に履歴を出力する記述を加えさえすれば、複数スレッドの連携動作を再現できる実行履歴が得られるのである。開発者が見たいものを動作させることができるという効果がある。単純な構造で所望の機能を実現するほうが、安全性、保守性などの様々な面において向上を図ることができる。したがって、部品102abcの導入による、複数スレッドの連携動作を実現するしくみは、記述が容易になるだけでなく、デバッグ機能を、安全で、保守しやすい形で実現する。さらに、開発者は簡単に利用できる。ソフトウェア開発で重要な、トライアンドエラーの両面、つまり、ソフトウェアを作成することと、デバッグすることとの両面で、生産性の向上が図れる。これは、部品開発視点に立っても、部品利用視点に立っても、しくみが簡単で、利用が簡単であるため、生産性の向上が図れる。
つぎに、以上に説明したことを、図6の構成図にそって説明したい。ガントチャートを利用する場合には、通常の同期用コード102の代わりに、実行履歴を出力するように変更した(例えば、図21のように書き換えた)部品102abcを使い、第1生成部601により、並列実行用のプログラムコード105を生成する。実行部602は、生成された並列動作用のプログラムコード104の実行形式105を実行する。この時、実行部602は、並列動作用のプログラムコード104の実行中に、実行履歴106を記憶部102に記憶させる。
そして、第2生成部603は、記憶部102に記憶された実行履歴106をもとに、スレッドごとに状態変化を図示する。表示のしかたとしては、例えば、ガントチャートやシーケンス図がある。そして、表示部604は、生成された図を表示する。表示部604は、例えば、ディスプレイ509に図を表示する。
図20は、ガントチャートの表示例を示す説明図である。図20には、実際に開発対象のシステム2000(実機ともいう)に含まれるCPU2001に並列動作用のプログラムコード104の実行形式105をロードさせた場合の例を示す。システム2000は、例えば、CPU2001と、コプロセッサC0〜コプロセッサC3と、を有する。そして、CPU2001と、コプロセッサC0〜コプロセッサC3とはバスを介して接続される。
システム2000は、例えば、設計支援装置100とインターフェースを介して接続される。そのため、設計支援装置100は、CPU2001に並列動作用のプログラムコード104の実行形式105をロードさせることができ、実行履歴106を取得することができる。
実行部602は、開発対象となるシステム2000に含まれるCPU2001にバイナリである並列動作用のプログラムコード104の実行形式105をロードさせ、CPU2001に並列動作用のプログラムコード104を実行させる。つぎに、設計支援装置100は、CPU2001が並列動作用のプログラムコード104を実行中に、実行履歴データを取得して実行履歴106を生成する。
そして、設計支援装置100は、実行履歴106に基づいてガントチャート2004を作成し、作成したガントチャート2004を画面2003に表示する。ガントチャート2004は、例えば、スレッドの各々について、実行履歴2002に基づいて、実行期間の各タイミングが、入力待ち、処理中、出力待ち、などによって分類される。
そして、表示部604は、例えば、画面2003をディスプレイ509に表示する。これにより、コプロセッサの動作状況を可視化することができる。
図21は、実行履歴を記憶するための記述例を示す説明図である。図21(1)には、syncが実行される前後で、syncを実行するノードオブジェクトの名称を出力するための記述例を示す。図21(2)には、runが実行される前後で、runを実行するノードオブジェクトの名称を出力する記述例を示す。これにより、開発対象のシステムの内部の動作を可視化することができる。
また、ここでは、実際の開発対象のシステムにロードさせる例を示したが、システムをプログラミング言語などによってモデル化したシステムモデルにロードさせる場合も同様に、内部の動作を可視化することができる。また、以前にも書いた通り、実行形式にかぎらない。CPU上で、並列動作用のプログラムコード104をインタプリタで処理するとしてもよい。また、CPU上のVM(Virtual Machine)で実行するとしてもよい。
図22は、分析作業の支援例を示す説明図である。ここでは、開発中の並列実行用のプログラムコード104を、コンピュータ上で実行する実行形式105を生成し、実行部602として、コンピュータを用いる利用例を示す。このようにすると、開発システム(実機ともいう)2000で利用するプログラムコード104と、基本的に同じプログラムコードをコンピュータ上で実行できるようになる。実機とコンピュータで異なるものもある。具体例としては、スレッドがコプロセッサによる機能実現した場合がある。この場合、ソフトウェア上のスレッドにはコプロセッサの制御(setup())と割り込みの受理(guard())が記述され、ソフトウェア上のスレッドでコプロセッサの制御が行われる。実際のコプロセッサが通信することにより、ソフトウェア上のスレッドのコプロセッサの制御と、実際のコプロセッサが協調して、1つのスレッドとして動作する(参考:図9、図11)。しかし、コンピュータ上には、実際のコプロセッサはないので、このようなスレッドの実現はできない。したがって、何らかの対策が必要である。対策の1案は、コプロセッサの制御記述を空関数にして実行することである。制御記述とは、setup()またはguard()である。図9に示した設定と割り込み待ちとが行われないため、プログラムコード104がコプロセッサに依存させないようにすることができる。
同期オブジェクトはスレッドライブラリの機能を使って、同期を実現するしくみなので、スレッドライブラリが提供されているOS上のコンパイル環境であれば、コンパイル可能であり、実行できる。このようにした場合、コプロセッサの具体的な制御はしないものの、複数スレッドの連携動作(例えば、データフロー動作)は実行していることになる。このような実行は、上述した図1A、図2などで示した通り、複数スレッドの連携(例えば、データフロー)であるため、開発初期時の検討をシミュレーションしていることに相当する。さらに、部品は共通であり、実機で動作させうるプログラムコード104を抽象化したものを、コンピュータ上で実行している。すなわち、抽象度の高いレベルから具象化した、実機で動作するプログラムコードの実行形式105まで、同一の制御構造(アーキテクチャ)を利用した開発が行えるということを意味する。同一のアーキテクチャを使っているので、抽象度の高いシミュレーションで得た知見は、実機におけるデバッグでも利用可能である。つまり、実機におけるデバッグまでに習熟した内容が、実機におけるデバッグで効果がでる。したがって、習熟した内容が効率的に利用される開発方法になっているといいうる。開発方法論的側面については、図40を用いて、後述する。
さらに、先に示した実行履歴データの取得は、部品102abcを拡張して、実現するものだった。この部分は、抽象化して、コンピュータ上で実行する場合にも利用する。したがって、実行履歴を落とすことができ、ガントチャートを出力することができる。したがって、利用者は、複数スレッドの連携動作(例えば、データフロー動作)を、直観的に、観察することができる。さらに、setup()とguard()にかかる予想時間を入れると、コプロセッサの時間負荷を考慮した動作も実現できる。簡単な実施形態としては、setup();guard()の代わりに、sleep()やnanosleep()をコールして、遅延させる方法がある。
また、遅延を管理するスレッドを追加してもよい。例えば、シミュレーション全体の時間を管理する時計を用意しておき、スレッドごとに、状態を保持する変数と、「時間待ちで停止している」スレッドが起きるべき時間を保存する変数を用意する。状態とは、同期で停止している状態、時間待ちで停止している状態、どちらでもない状態のいずれかである。「どちらでもない」になっているスレッドが0個の時に時刻の更新を行う。「時間待ちで停止している」スレッドが、起きたい時刻をしらべて、直近の時刻まで時計を進め、進めた時刻で起きるべきスレッドを起こす。
これにより、スレッド、つまり、ノードオブジェクトのrun()の実行が再開される。この後の実行により、同期が起きることがある。同期が起きると、「同期で停止している」スレッドが、「どちらでもない」スレッドになるが、さらに、実行が進む。この結果、いずれ、「同期で停止する」か「時間待ちで停止する」のどちらかになる。つまり、「どちらでもない」スレッドが0個になる。これを繰り返すことにより、時間管理ができるのである。このようにすると、複数スレッドの連携記述(例えば、データフロー記述)を、基本的には、変えることなく、時間の概念を含めた動作を、コンピュータ上で、実行できるようになる。詳述はしないが、割り込みも、部品そのものは、割り込み信号を直接参照しているわけではないので、図37、図38のように、疑似的な割り込み動作を実現することはでき、この場合、guard()こみの動作も実現可能になる。
本発明には、後述するが、スレッドごとの機能と、複数スレッドの連携制御を分ける働きがあるといいうる。この働きは、抽象的なシミュレーションにも、効果がある。すなわち、コプロセッサと同等の動作をする機能を書いて、この機能を、setup()とguard()の代わりに、実装すれば、複数スレッドの連携制御は同じものを利用しつつ、ソフトウェアによる機能実装による動作実現が可能になる。これにより、実機でも利用しているのと同じ、複数スレッドの連携制御を利用した、機能シミュレーションが可能になる。抽象度が高いレベルから、段階的に、実装・実行を繰り返しながら、ソフトウェアを実現する場合、しばしば、異なる段階の結果同士を比較して、問題点を推測する解析が行われる。このような解析を効率化する方法には、問題点を探す探索空間を減らすことがある。本発明は、異なる段階間で、バグが入りやすいと推測される制御部分(つまり、早期にバグがない状態にしたい部分)を共通化し、しかも早期に動作可能にしているので、後期のデバッグにおいて、重要部分が安全であり、かつ、探索空間を削減されていて効率的になっていると推測しうる。したがって、段階的なソフトウェア開発における生産性の向上を生んでいるといいうる。さらに、後述するが、LSI(Large Scale Integrated)のラインナップ上、同一の機能を、ソフトウェア実現とコプロセッサ実現との2つの実装を用意しなければならない場合がある。上記は、ソフトウェア実現とコプロセッサ実現の両方を統一的に開発しているともいえるので、生産性が向上していると推測しうる。さらに、コプロセッサ機能の新規開発を行い、その機能を高位合成で開発する場合には、高位合成に入力する記述を実行していることになり、機能シミュレーションをしている効果があるといいうる。したがって、高位合成を用いた統一的な開発方法論においても、利点があるといいうる。
このような機能があると、コンピュータ上で実行でき、ガントチャート2004を作成して画面2003をディスプレイ509に表示することもでき、開発早期に、開発システム上でプログラムコード104を動作させた場合の直観を得ることができるのである。そのため、利用者による、複数スレッドの連携(例えば、データフロー)の分析作業を支援することができる。このため、利用者によるソフトウェアの仕様作成の容易化を図ることができる。また、上記のように、重要な部分(ソフトウェアアーキテクチャといってもよい)から立ち上げて、段階的にソフトウェア開発・デバッグをしながら実現していくことが可能になっているので、ソフトウェアを早く安定化させられる可能性も出てくることが考えられる。したがって、このような開発方法論を了すれば、更なる生産性の向上が見込める。
本実施の形態では、複数のコプロセッサの制御を行う並列動作用のソフトウェアプログラムを作成する例を示したが、これに限らず、機能をソフトウェアで実現したスレッドの実行を行う並列動作用のソフトウェアプログラム104を作成してもよい。例えば、setup()やguard()の代わりに、コプロセッサが行う処理そのものをソフトウェアで記述した場合、並列スレッドの制御を行う並列動作用のソフトウェアプログラムを作成したことになる。つまり、マルチスレッドのソフトウェア開発においても、本発明は、利用できる場合がある。さらに、コプロセッサで機能を実現したスレッドとソフトウェアで機能を実現したスレッドが、混在しても、複数スレッドの連携のしくみは同じものが利用できる。つまり、実装形態に依存する部分と、複数スレッドの連携制御が分離されていることになる。したがって、制御部分は共通化されているのである。このような性質も、前述のとおり、本実施の形態による利点である。例えば、同一の機能なのに、低価格LSIではソフトウェアで機能実現だが、高価格LSIではコプロセッサで機能実現、といったシリーズのLSIもありうる。このような場合、制御部分は共通化されているので、機能実現の形態が違う部分だけを、個別管理すればよいため、ソフトウェア開発の容易化を図ることができる。
(設計支援装置100が行う処理手順例)
図23は、設計支援装置が行う処理手順例を示すフローチャートである。ここでは、実際に運用する対象のプロセッサに生成される並列動作用のプログラムコードの実行形式105がロードされた場合の処理手順例を示す。
まず、設計支援装置100は、例えば、プログラムコード104を取得する(ステップS2301)。つぎに、設計支援装置100は、プログラムコード104と部品102abcとを組み合わせた並列動作用のプログラムコードの実行形式105を生成する(ステップS2302)。ここでは、並列動作用のプログラムコードの実行形式105はコンパイルされた後のデータであるとする。前述のとおり、CPU上でインタプリタが動作し、並列動作用のプログラムコード104が、インタプリタ上で動作する場合、並列動作用のプログラムコード104をインタプリタにロードするとしてもよい。このようにしても、本質的には同じなので、コンパイル後の実行形式105を利用したフローで説明する。
そして、設計支援装置100は、並列動作用のプログラムコードの実行形式105を実行する(ステップS2303)。これにより、実装すべきソフトウェアが実行される。ステップS3302で、実行時にデバッグ情報をとりたい場合には、デバッグ用の実行履歴を出力する部品102abcを使ってプログラムコードを生成する。この場合、S2303と並行して、ステップS2304が実行される。これにより、設計支援装置100は、並列動作用のプログラムコード105を対象のプロセッサにロードさせて実行させた場合の実行履歴106を取得する(ステップS2304)。
つづいて、設計支援装置100は、実行履歴106に基づいてガントチャート2004を作成する(ステップS2305)。そして、設計支援装置100は、作成したガントチャート2004を表示し(ステップS2306)、一連の処理を終了する。
<詳細例>
以降、図24〜図49に基づいて、本実施の形態にかかる各クラスや実行例などの詳細例を示す。まず、データフロー700が示すノード集合とノード間の入出力関係とが、ノードクラスと、割り込みとノードとの対応表のクラスと、同期クラスと、によって記述される。そのため、ノードの基本クラスと、対応表のクラスと、同期クラスと、について詳細に説明する。
図24は、ノードの基本クラスの記述例を示す説明図である。記述情報2400には、ノードの基本クラスが記述される。図24に示すノードクラスは、ノードの基本クラスである。そのため、ノードクラスでは、このクラスを継承して、runをオーバーライド定義することにより、スレッド実行する関数を定義する。また、ノードの基本クラスでは、メンバー関数create,join,lock,unlockが定義されてある。ノードの基本クラスでは、m_mutexが利用されてある。
ノードの基本クラスには、m_actionというpthread_cond_tも定義されてある。m_actionは、action()が呼ばれると、guard()でブロックされている状態から抜ける動作を行う。
図25は、ノードクラスと割り込み例を示す説明図である。ノードクラスでは、lock()の記述とunlock()の記述との間に、コプロセッサへの設定とguard()とが記述される。
スレッドカーネルは、割り込み信号とノードオブジェクトとを割り込みとノードとの対応表に記録しておき、割り込みハンドラによって割り込みIDを取得し、割り込みIDによって対象のノードオブジェクトの参照を得る。そして、スレッドカーネルは、参照を得たノードオブジェクトのaction()をコールする。これにより、Node_tはアクションから抜ける。したがって、guard()は、対応するコプロセッサが正常に終了するのを待機することになる。createによってスレッドが生成されると、run_baseクラス関数を経由して、run関数がスレッド上で実行される。
図26は、割り込みとノードとの対応表例を示す説明図である。記述情報2600には、割り込みとノードとの対応表が記述される。対応表によれば、割り込みハンドラで、終了の割り込みを検出した際に、対応表オブジェクトのメンバー関数operator[]()の引数として、割り込みIDを渡すと、割り込みに関係するノードオブジェクトの参照が得られる。operatorは、C++の演算のオーバーロード定義をするための予約語である。operator[]()は、演算[]を定義している。この定義により、Int_to_sig_tのインスタンスをint_tableとした時、int_table[100]と書くと、引数を100として関数operator[]が呼ばれ、結果、100に関係づけられたノードオブジェクトへの参照が得られる。
図27は、同期例を示す説明図である。同期クラスは同期のしかたごとに用意される。同期のしかたとは、例えば、ダブルバッファやリングバッファなどである。ここでは、ダブルバッファが利用される。そして、ノードオブジェクトN0とノードオブジェクトN1とノードオブジェクトN2とのすべてが同期に入った時に、同期がとれたとみなされる。そして、ノードオブジェクトN0とノードオブジェクトN1とノードオブジェクトN2とはいずれも同期の部分を抜けてつぎの処理を開始することができる。また、「同期がとれたとみなす」条件が、前述の説明で、同期条件と呼んでいたものである。
ノードオブジェクトN0、ノードオブジェクトN1に関しては、処理を実行する際に、sync_0の中のノードオブジェクトN0とノードオブジェクトN1との出力のデータフィールドに値を書き込むことができる。一方、ノードオブジェクトN2は、同期を抜けた後の処理によって、ノードオブジェクトN0とノードオブジェクトN1とからの出力を受け取ることができる。これにより、上流ノード(スレッド)から下流ノード(スレッド)に伝えたいパラメータを渡すことができる。後述の記述例では、イタレーション回数と一連の処理の最後を示すブール値を渡している。このように、複数スレッドの連携制御(例えば、データフロー制御)に関する情報を、複数スレッドの連携の中で、渡すことができるのである。
図28A〜図29Bは、同期クラスと同期ポートクラスの詳細例を示す説明図である。図28Aと図28Bとの記述情報2800には、同期クラスのインターフェースSync_if_t、同期ポートクラスSync_port_t、同期INポートクラスSync_in_t、同期OUTポートクラスSync_out_tが記述されてある。図29Aと図29Bとの記述情報2900には、同期クラスsync_tが記述されてある。
Sync_in_t、Sync_out_tは、データの方向を部品に教える役割ももっている。Sync_in_tはsync_tからデータをもらうポートであることを示す。Sync_out_tはsync_tにデータを送るポートであることを示す。これにより、sync_tの中に、2つのDATA_t型を有する記憶領域をSync_out_tの個数分用意される。一方の記憶領域はSync_out_tからの書き込み用として使用される。他方の記憶領域は、Sync_in_tからの参照用として使用される。Sync_tに繋がるポートがすべてメンバー変数sync()に入った時に、Sync_out_t用と、Sync_in_t用と、を入れ替える処理が行われる。入れ替える処理とはupdate()である。これにより、sync_tへデータを送る側と、sync_tからデータを受け付ける側が並列に動作しても動作が破綻しないようにしてある。
図29Aおよび図29Bに示す例では、broadcastによって同期待ち状態を起こしている。また、最後にsync()をコールしたスレッドが最後にsync()を抜けるようにしている。これは実行順序制御の一例である。さらに、スレッドを起こす際に優先順位をつけ、優先順位によって起こしてもよい。例えば、同期オブジェクトに先に入ったものほど、優先順位が高いとしてもよい。例えば、同期オブジェクトは、各々のスレッドに対応したイベントを用意し、優先順位順に起こす。これにより、同期した際の関連するノードの実行順序のスケジューリングが可能になる。例えば、スレッドカーネルは、コプロセッサに割り当てられる処理の処理時間に基づいて、ノードからコプロセッサに設定する処理の順を決定してもよい。同期クラスは、同期条件を有するとしたが、より正確には、同期条件とスレッドの実行順序の決定方法を有する。
つぎに、データフロー700の記述例を示す。ここでは、データフロー700の制御について示すため、図24に示すINT_MODELディレクティブが指定されていない場合を示す。図24に示すINT_MODELディレクティブが指定されると、図24や図25に示すguard()が有効化される。つぎに、ノードの記述例を図30〜図32に示す。
図30は、source_tの記述例を示す説明図である。記述情報3000には、ノードsourceに応じたノードクラスsource_tが記述されてある。
図31は、pala_tおよびsolo_tの記述例を示す説明図である。記述情報3100には、ノードPalaおよびノードsoloに応じたノードクラスpala_tおよびノードクラスsolo_tが記述されるが、説明の簡単化のために、pala_tとsolo_tの記述例を共通化してある。
図32は、sink_tの記述例を示す説明図である。記述情報3200には、ノードsinkに応じたノードクラスsink_tが記述されてある。
ここでは、図30〜図32に示す各ノードの記述の簡単化のため、記述情報3000〜記述情報3200には、コプロセッサの制御の記述が省略され、当該制御の記述の代わりにプリント文が記述されてある。また、このプリント文による標準出力結果を使って、動作内容を説明する。
図33は、ノードの接続記述例を示す説明図である。記述情報3300は、プログラムコード104である。記述情報3300には、ノードクラスのインスタンス、すなわち、ノードオブジェクトと、同期クラスのインスタンス、すなわち、同期オブジェクトと、ノードオブジェクトと同期オブジェクトの接続が記述される。
接続記述では、Sync_tがインスタンスされる。詳細例で示すSync_tはテンプレートクラスになっており、テンプレートの引数としては、パラメータの受け渡しに使われるデータ領域の型をとっている。図33の例では、図34に示すdata_tを指定している。
図34は、data_tの記述例を示す説明図である。記述情報3400には、出力データのデータ型が記述される。data_tにはデータフィールドが2つある。1つめのデータフィールドにはイタレーションの回数値が保持される。もう1つのデータフィールドには、一連の処理の最後の場合に真になるフラグが保持される。
図30〜図32の説明に戻る。図30のように、source_tは、20回繰り返しをしたら、終了フラグを送信し、処理を終了している。図31のように、データフローの中間ノードでは、もらってきたパラメータを、そのまま、下流ノードに渡している。また、一連の処理の最後を示すフラグを参照して、最後だったら終了する。図32は、他のノードを起動しない例である。このノードでは、受け取ったデータを参照して、一連の処理の最後を示すフラグが真だったら、処理を終わるようにしてある。このように、ノード間の制御を同期させることができるのである。
図33の説明に戻る。図33の接続記述では、ノードオブジェクトのインスタンス、同期オブジェクトのインスタンス、ノードオブジェクトと同期オブジェクトとの接続、ノードオブジェクトをcreate、ノードオブジェクトをjoin、が定義されてある。ノードオブジェクトをcreateとは、スレッドを実行することを示す。ノードオブジェクトをjoinとは、スレッドの終了により同期することを示す。このように、接続記述は、データフロー700を形式的に記述したものである。
設計支援装置100は、記述情報3300をコンパイルして実行すると、標準出力として以下の図35A〜図35Cに示す結果を得る。
図35A〜図35Cは、実行によって得られる標準出力例を示す説明図である。この標準出力結果3500によれば、同期条件を充たしつつ、複数のスレッドによる並列動作が行えていることがわかる。
図36は、source_tの他の記述例を示す説明図である。図28A〜図35Cで、複数スレッドの連携動作の一例としてデータフロー動作を説明した。図36〜図39で、割り込みに関する、コプロセッサとノードの連携について説明する。図36にノードクラス例を示す。記述情報3600は、ノードsourceに対応する図30に示すノードオブジェクトsource_tと異なるノードオブジェクトの記述例である。記述情報3600は、guardを利用した記述例である。なお、図24に示すINT_MODELディレクティブが指定された状態、つまり、割り込みがノードに届く形態であるとする。
図37は、ノードの接続記述の他の例を示す説明図である。記述情報3700では、ノードオブジェクトsource_tと割り込みを模擬するノードオブジェクトinterrupt_tとがインスタンス化されている。ノードオブジェクトinterrupt_tは、guard()を説明するために便宜的に導入したもので、HDL(Hardware Description Language)記述におけるテストベンチに相当するものである。interrupt_tの記述例を図38に示す。
run()記述では、1秒ごとに、割り込みID100に対する割り込みを受け取って、割り込みID100に対応するノードオブジェクトに割り込みを通知する。このようにして、割り込みと割り込みハンドラの連携で実現する動作を模擬している。実際の利用では、図38の(2)の記述を、割り込みハンドラの中で識別した割り込みIDを使って、図38と同様、g_int_to_sig[int_id]で、ノードの参照を得て、そのメンバー関数action()を呼ぶ。したがって、実際と模擬の差は、割り込みハンドラの中でaction()が呼ばれるか、ダミーのスレッドの中で、適当な間隔で、action()が呼ばれるかの差だけなので、割り込みがノードに伝わるしくみ自体は同じである。このようにして、複数スレッドの連携を構成するスレッドに対して、割り込みがかかる。ノード(スレッド)がコプロセッサの代理人(例えば、図9)として、コプロセッサの動作状況をまねながら動いている状態を作ることができる。したがって、利用者は、複数コプロセッサの連携制御を、コプロセッサの代理人である複数ノードを、複数スレッドの連携制御(例えば、データフロー制御)で結合することで、ソフトウェア開発ができるのである。
そして、記述情報3700には、ノードオブジェクトsource_tと割り込み用のノードオブジェクトinterrupt_tとが、並列のスレッドとして、生成されることが記述されてある。また、記述情報3700には、ノードオブジェクトsource_tのスレッドとinterrupt_tのスレッドとが、ともに終了すると、全体の処理を終了するように記述されている。ただし、図38の場合、スレッドが終了しない。この記述例は、コンピュータ上で実行するものだったので、所望の応答が得られたら、プロセスをkillすることで処理を終了させていた。
図38は、interrupt_tの記述例を示す説明図である。run()の動作は前記の通りである。
図39は、実行によって得られる標準出力の他の例を示す説明図である。標準出力結果3900では、コプロセッサとノードとが関連付けられたことを示している。図36の記述情報3600では、図25に示したようにguard()を利用してノードオブジェクトsource_tが定義されている。そのため、上述したように、割り込みハンドラで、コプロセッサの終了を検出して、「g_int_to_sig[int_id].action()」が呼ばれると、action()の中で、スレッドプログラミングにおける信号が呼ばれ、スレッドはguard()の中でこの信号待ちをしているので、guard()を抜け、処理を再開するのである。
これにより、起動から終了の間、ノード(スレッド)は休止していることになる。つまり、簡便な記述ながら、ノード(スレッド)は、コプロセッサの動作を模擬しているのである。したがって、guard()を抜けたところに、下流ノードと同期をとると記述しておけば、同期条件にあったコプロセッサ間のデータ転送が実現されるのである。また、コプロセッサが実行中、スレッドが休止していることも重要で、この間、他のスレッドの動作を妨げない。つまり、おおむね、制御にかかわる判断が必要な時と、コプロセッサの設定が必要な時とに、動作するものができているのである。まとめると、複数スレッドの連携を実現する同期クラスと、ノードとコプロセッサを連携させる技術(ノードクラスの一機能)とを使うと、ノード(スレッド)はコプロセッサの状態を模擬することになるので、複数ノードを、複数スレッドの連携制御でつなぐだけで、複数コプロセッサの連携制御ができるようになるのである。冒頭で書いたように、従来は、この制御を実現するために、仕様検討、詳細化が必要であった。しかし、本発明によれば、複数コプロセッサと対応付けられたノードを、直観的に、つなぐだけで、所望の機能が得られるのである。したがって、安全に、わかりやすい形で、機能実現が可能になった、といってよい。
本実施の形態では、利用者は、データフロー700の定義を形式的に記述するだけである。そのため、利用者は、データフロー700の作成以外に新たな構築作業を行わずに、並列動作用のプログラムコード105を簡単に得ることができる。
また、図37に示したように、コプロセッサとノードとを関連付ける記述例は、コプロセッサ単体の制御を行う記述である。図36に示したように、ノードはguard()でなされる、コプロセッサからの割り込み待ちから抜けるのを待つという記述になる。そのため、利用者はFSMを考えることなく、ソフトウェアの実装を行うことができるのである。
したがって、複数のコプロセッサの連携を行う並列動作用のプログラムコードの作成の簡単化を図ることができる。
図40は、段階的な設計フロー例を示す説明図である。利用者が、制御記述のないスケルトン記述を作成する(ステップS4001)。制御記述とは、コプロセッサを制御するための記述である。そして、設計支援装置100は、同期クラスと、ノードクラスと、データフロー700が示す入出力関係を同期クラスおよびノードクラスによって記述したプログラムコードと、によってコンパイルして実行する(ステップS4002)。これにより、標準出力結果によってガントチャート2004を作成することができる。このように、コプロセッサ間の制御の関連性部分の記述を開発して簡単に検証することができる。また、前述のような時間管理クラスや、割り込み模擬クラスを導入して、制御記述の一部も利用可能にして、実行するとしてもよい。
また、利用者が、スレッドについての制御記述を追加して、並列動作用のプログラムコード105を作成する(ステップS4003)。制御記述とは、コプロセッサに対する設定とコプロセッサからの割り込み待ちなどの制御である。そして、設計支援装置100は、並列動作用のプログラムコード105をコンパイルして実行する(ステップS4004)。これにより、ガントチャート2004を作成することができる。
つぎに、設計支援装置100は、コンパイル後の並列動作用のプログラムコード105を実機にロードして実行する(ステップS4005)。実機は、例えば、上述したシステム2000やシステム2000をプログラミング言語によってモデル化したものである。そして、設計支援装置100は、実行履歴2002に基づいてガントチャート2004を作成する(ステップS4006)。
ここで、ステップS4002やステップS4004のように、プログラムをコンパイルして実行するだけで、ガントチャート2004を得られるようにするためには、以下図41のような記述が追加される。
図41は、ガントチャート作成のための追加記述例を示す説明図である。記述情報4100に示すように、図28Aおよび図28B、図29Aおよび図29Bなどに示す記述情報2800や記述情報2900には、イベントの発生前後においてイベントの発生起因をダンプできる記述が追加される。
図42は、ガントチャート作成のための接続記述例を示す説明図である。記述情報4200には、ノードオブジェクトと同期オブジェクトとのインスタンスの際に、下線の箇所のように、オブジェクトを示す名称を受け渡すように記述される。また、記述情報4200には、Port{out,in}にノードの名称を渡すために、ノードそのものを渡すように記述される。
図43A〜図43Eは、図41および図42に示した記述情報に基づく実行結果例を示す説明図である。設計支援装置100は、図41に示した記述情報4100と図42に示した記述情報4200とをコンパイルして実行することによって実行結果4300を得る。
実行結果4300には、スレッドの実行開始のタイミングと、Sync()に入ったタイミングまたは出たタイミングと、スレッドの実行終了のタイミングと、が含まれる。
図44は、接続関係の記述例を示す説明図である。接続関係情報4400は、ノードオブジェクトと同期オブジェクトとの接続関係が記述される。この情報は、プログラムコード104を解析して、出力してもよいし、部品102bcを、この記述を出力するように拡張した部品を使って、プログラムコードの実行形式105を生成し、実行形式を実行すると、図44が出力されるようにしてもよい。
図45は、作成されるガントチャート例を示す説明図である。設計支援装置100は、接続関係情報4400と、実行結果4300と、に基づいてガントチャート2004を作成してディスプレイなどに表示する。ガントチャート2004では、ノードオブジェクトと同期オブジェクトの各々について、入力待ち、処理中、出力待ち、などの状態と、各イベントの発生タイミングと、が可視化される。
設計支援装置100は、キーボード507やマウス508などの入力装置を介して利用者によって開始ノードオブジェクトの指定を受け付ける。または、図44より、out=””のみに出現するノードオブジェクトを選択するとしてもよい。このようなノードは入力側に同期オブジェクトがいないので、開始ノードオブジェクトである。設計支援装置100は、開始ノードオブジェクトが指定された場合、開始ノードオブジェクトは、ガントチャートの先頭が「処理中」になるようにガントチャート2004を作成する。また、設計支援装置100は、入力待ちがある場合には、入力待ちを抜けた場合に「処理中」の状態となるようにガントチャート2004を作成する。入力待ちがない場合、出力待ちを抜けた場合に「処理中」の状態となるようにガントチャート2004を作成する(例えば、図中、Src)。また、設計支援装置100は、ガントチャート2004を90度回転させたシーケンス図を作成してもよい。
つぎに、ソフトウェアで機能実装したスレッドの例を示す。図46A〜図46Cは、印字回数を増やした記述例を示す説明図である。ここでは、guard()は空関数(図24の記述例でディレクティブINT_MODELが設定されていない状態)で、コメントアウトされているのと同等の状態である。この時、ノードオブジェクトにおいて、lock()〜unlock()は、連続実行される。記述情報4600では、矢印で示した部分のように所定回数分の印字が、連続して、行われる。INT_MODELが設定されている場合には、lock()〜guard()間が連続実行され、コプロセッサが割り込み信号を上げるまで、guard()の中で、スレッドは休止する。ここでは複数スレッドの連携動作を、コンピュータ上で可視化したいので、guard()を空関数にしている。
図47は、図46A〜図46Cの記述情報に基づく実行結果例を示す説明図である。図47に示す実行結果4700によれば、lock()〜unlock()が連続実行されている。コプロセッサ制御の場合は、前述のとおり、lock()〜guard()が連続実行される。このような動作になるのは、unlock()と同様、guard()でも、クリティカルセクションを抜けるためである。これにより、設定が連続していることが望まれるコプロセッサに適した動作となる。
図48A〜図48Cは、印字回数を増やした他の記述例を示す説明図である。記述情報4800では、lock()、unlock()、guard()が削除されてある。そのため、ノードオブジェクトにおいて、lock()とunlock()がないので、連続実行されない。
図49は、図48A〜図48Cの記述情報に基づく実行結果例を示す説明図である。図49の実行結果4900によれば、同期条件は満たすものの、実行は、複数のスレッドの実行がインターリーブされていることがわかる。つまり、ノードオブジェクトの機能が並列実行されているのである。例えば、ノードsourceの処理の4番目、ノードpara_0,para_1,para_2,para_3の各々の3回目、ノードsoloの処理の2回目、ノードsinkの処理の1回目が並列に動作していることを判別できる。このように、同期条件を満たしつつ、スレッドで実現した機能は、インターリーブして動作される。機能をソフトウェア実現した場合にこのような動作が好まるので、機能をソフトウェア実現した場合に適した動作ができていることがわかる。
このように、データフロー700の制約にあった形で、複数のスレッドが並列動作させられ、機能をコプロセッサ実現した場合に好まれる実行も、ソフトウェア実現した場合に好まれる実行も実現しうることがわかる。
以上説明したように、設計支援装置100は、複数スレッドを、同期オブジェクトが定める同期条件で、同期した動作をする複数スレッドで構成された、並列動作用のプログラムコードを生成する。実施の形態で説明したように、ダブルバッファに対応した同期オブジェクトで、スレッドを連携させると、ダブルバッファで破綻しないデータフロー制御ができる並列動作用プログラムが生成できる。このように、並列動作用のプログラムコードの作成の容易化を図ることができる。
また、設計支援装置100は、並列動作用のプログラムコードの実行中に、同期をとる関数に入ったタイミングと出たタイミングとを動作履歴として記憶し、各タイミングに基づいてスレッドの各々についての動作の流れを示す図、例えば、ガントチャートやシーケンス図などが、簡単に表示しうる。したがって、利用者は、データフローなどの仕様作成中に、ガントチャートなどを確認することができるため、仕様などの分析作業の容易化を図ることができる。
また、設計支援装置100は、さらに、データフローが示すスレッドをノードクラスで記述したプログラムコードによって並列動作用のプログラムコードを生成する。これにより、スレッドを簡単にコードに表すことができるため、読みやすいプログラムコードを簡単に作成することができる。
また、並列動作用のプログラムコードが複数のコプロセッサを連携して動作させるための制御プログラムである場合、ノードクラスには、コプロセッサに対する設定を行う処理と、コプロセッサからの割り込みを受け付ける処理と、が定義される。これにより、複数のコプロセッサを連携して動作させるための並列動作用のプログラムコードを簡単に得ることができる。また、スレッドの機能を一部共通化させることができるため、読みやすいプログラムコードを簡単に作成することができる。
また、設計支援装置100は、並列動作用のプログラムコードを実行中に、同期をとる関数に入ったタイミングと出たタイミングとを、または、割り込みのタイミングとを動作履歴として記憶し、記憶した各タイミングに基づき、複数のコプロセッサの各々についての制御動作の流れを示す図を表示する。これにより、コプロセッサの動作の流れを簡単に可視化することができる。したがって、ソフトウェアのデバッグ作業の容易化を図ることができ、ソフトウェアの開発期間の短縮化を図ることができる。
なお、本実施の形態で説明した設計支援方法は、予め用意された設計支援プログラムをパーソナル・コンピュータやワークステーション等のコンピュータで実行することにより実現することができる。本設計支援プログラムは、磁気ディスク、光ディスク、USB(Universal Serial Bus)フラッシュメモリなどのコンピュータで読み取り可能な記録媒体に記録され、コンピュータによって記録媒体から読み出されることによって実行される。また、設計支援プログラムは、インターネットなどのネットワーク510を介して配布してもよい。
上述した実施の形態に関し、さらに以下の付記を開示する。
(付記1)入力される複数のスレッドを同期する際の同期条件を記憶する記憶部と、
前記複数のスレッドに対して、前記同期条件に応じた制御を行う同期手段と、
利用者が作成する前記複数のスレッドと、前記複数のスレッドのそれぞれと前記同期手段を関係づける記述情報と、に基づいてソフトウェアを生成する生成部と、
を有することを特徴とする設計支援装置。
(付記2)前記複数のスレッドを同期した実行履歴を出力する出力手段を有することを特徴とする付記1に記載の設計支援装置。
(付記3)前記スレッドがコプロセッサを制御する場合であって、
前記記憶部は、
前記コプロセッサが送信した割り込みを前記スレッドに出力する手段を有し、
前記生成部は、
割り込み識別番号と前記スレッドを関係づける情報に基づいて前記コプロセッサが送信した割り込みを前記スレッドに出力することを特徴とする付記1に記載の設計支援装置。
(付記4)前記割り込み、または、前記複数のスレッドを同期した実行履歴を出力する出力手段を有することを特徴とする付記3に記載の設計支援装置。
(付記5)出力した前記実行履歴を解析して、前記スレッドの変化を示す図を表示する表示手段を有することを特徴とする付記2または付記4に記載の設計支援装置。
(付記6)入力される複数のスレッドを同期する際の同期条件を記憶し、
前記複数のスレッドに対して、前記同期条件に応じた制御を同期手段が行い、
利用者が作成する前記複数のスレッドと、前記複数のスレッドのそれぞれと前記同期手段を関係づける記述情報と、に基づいてソフトウェアを生成することを特徴とする設計支援方法。
(付記7)入力される複数のスレッドを同期する際の同期条件を記憶し、
前記複数のスレッドに対して、前記同期条件に応じた制御を同期手段が行い、
利用者が作成する前記複数のスレッドと、前記複数のスレッドのそれぞれと前記同期手段を関係づける記述情報と、に基づいてソフトウェアを生成することを特徴とする設計支援プログラム。