レベル: 中級 梶 信也 (skaji@jp.ibm.com), Editor, Multicore acceleration, IBM Systems & Technology Group, AKD48
2009年 02月 06日 Cell Broadband Engine (Cell/B.E.) が持つ高い演算能力には魅力があるが、その演算能力を必要とするアプリケーションがWindowsプラットフォームでしか動作しないために、諦めたりしていませんか。IBM Dynamic Application Virtualization (IBM DAV) は、そんな悩みを解決してくれるツールです。
はじめに
第 1 回では IBM DAV を使って Windows アプリケーションの一部分を Cell/B.E. などのコンピュータノード上に関数オフロードする流れを紹介しました。実際に試された方は IBM DAV を使えばネットワーク越しのコンピュータノードへの関数オフロードをどの程度簡単に実現できるか感じていただけたかと思います。第 2 回では IBM DAV をより良く使う上で知っているととても便利な Tips や IBM DAV の制約事項など、本格的に IBM DAV を使って Cell/B.E. を搭載したコンピュータノードを利用した Windows アプリケーション開発を行う際に役立つヒントや情報を紹介します。
尚、昨年 12 月に最新の IBM DAV がリリースされ Cell SDK 3.1 と System z Linux プラットフォームがサポートされるようになりましたので、是非最新版を入手して下さい。
問題が起こったときにはログを見る
IBM DAV を使えば Windows などのプラットフォームで動作するアプリケーションの一部の処理をネットワーク越しのコンピュータノードにオフロードする一連の作業を大幅に削減することができます。またその手順も簡略化されており容易に活用することができるといえます。
しかしながら、いざ IBM DAV を使ってみると、思った通りにうまくいかないといったこともあると思います。例えばクライアントからコンピュータノードにオフロードした関数を呼び出してもコンピュータノードで実行されずにどこかでトラブルが発生しているようなときです。原因は IP アドレスやポート番号の不一致であったり、ファイアウォールによるブロックといったネットワーク設定に関連するものかもしれませんし、コンピュータノード側でリクエストを受け付けた後に何かしらの問題が発生しているのかもしれません。
IBM DAV を正しい手順に従って使っているのに問題が発生するといった場合には、ログを利用することが有用といえます。そこでログの便利な使い方を 2 つほどご紹介します。
サービスブローカーのログをコンソールに出力する
1 つ目はクライアントからコンピュータノードへの関数実行要求を仲介する役割を持つサービスブローカーのログ出力をコンソール上に出す方法についてです。通常サービスブローカーはログをログファイルに出力していますが、コンソール上に直接出力することも可能です。
次のようにサービスブローカーを起動する際に "-d" オプションをつけて実行して下さい。
$ cd /usr/ibmdav/server/bin
$ ./davServiceBroker -d
|
このように起動すると 図 1 のようにエラーが発生した場合にログがコンソール上に出力されて問題解決に役立てることができます。図 1 を例にすると、このエラーメッセージは「何かしらのノードが取得できない」ということを意図しているわけですが、実はコンピュータノード上のデーモンが起動していなかったために発生していたエラーでした。単純なミスによるものですが、こういったメッセージがあると原因を突き止める際に非常に役立つと思います。
図 1. コンソールへのログ出力
サービスブローカーのログレベルを変更する
2 つ目はサービスブローカーが出力するログのレベルを変更する方法についてです。通常サービスブローカーはエラー発生時にのみログを出力しますが、ログレベルを変更することで動作中の状態を詳細に把握することが可能となり、問題解決の手がかりになる可能性があります。
ログレベルを変更するにはクライアント側もしくはサーバ側の設定ファイル (IBM_DAV.conf) 内に dav.log.level を指定します。
...
#Log level
dav.log.level=2
...
|
ここで指定するログレベルの値とログ出力される内容の関係を以下に示します。
- ログレベル 1 : エラー発生時の内容がログとして出力されます (デフォルト)
- ログレベル 2 : エラー発生時の内容に加えて実行時設定などがログ出力されます
- ログレベル 3 : エラー発生時の内容および実行時設定に加えて IBM DAV のデバッグメッセージが出力されます
ユーザレベルでの問題解決に役立てるという点ではログレベルを 2 に設定することが最も有用かと思います。図 2 はログレベルを 2 に設定してサービスブローカーを起動し、図 1 のときと同様のエラーが発生したときに出力されるログメッセージの様子です。
図 2. ログレベルを変更した場合のログ出力
是非目的に合ったログレベルを設定して問題解決に役立てて下さい。
IBM DAV の設定ファイル (IBM_DAV.conf) の理解を深める
クライアント側の設定ファイル (IBM_DAV.conf) については特に取り上げて説明することはありません。既に第 1 回で言及したようにサーバ (正確にはサービスブローカーノード) と通信するための IP アドレスとポート番号を正しく記述していれば設定としては十分です。
一方でサーバ側の設定ファイル (IBM_DAV.conf) については少し補足しておきたいと思います。とても重要なことですが、サービスブローカーノードとコンピュータノードは物理的に異なるマシンから構成することが可能です。サービスブローカーノードとコンピュータノードを物理的に分ける場合にはコンピュータノード上の設定ファイルの dav.broker.listen.services.ip を変更します。この項目はサービスブローカーの IP アドレスを指定するものです。
サービスブローカーノード上の設定ファイルでは自分自身を指定するので特に値をデフォルト (127.0.0.1) から変更する必要がありませんが、コンピュータノード上の設定ファイルではサービスブローカーノードの IP アドレスに変更します。
IBM DAV にも苦手なことはある
IBM DAV を使えば Windows アプリケーションの色々な処理を Cell/B.E. を搭載したコンピュータノードにオフロードすることで劇的な高速化が図れる可能性があります。では IBM DAV を使えばどのような関数でもコンピュータノード上にオフロードすることができるのでしょうか。残念ながら現時点での答えは "ノー" です。
以下に IBM DAV を活用する上で一筋縄ではいかないようなケースをご紹介し、それらの回避策もあわせてご紹介します。
マルチスレッドからの非同期呼び出しには注意が必要
昨今のアプリケーションはプロセッサのパワーを引き出すためにマルチスレッドで動作するものが主流になってきています。おそらく IBM DAV を使って関数オフロードを実現する際に、オフロードされる関数が複数スレッドから非同期的に実行されるようなものがあるでしょう。
では IBM DAV を使ってオフロードした関数はスレッドセーフに実行することができるでしょうか。筆者がリスト 1 からリスト 3 に示すプログラムで試した限りでは、スレッドセーフではなく、例外が発生してプロセスが終了してしまいました。ではマルチスレッドから非同期的に呼び出す関数を IBM DAV によってオフロードするにはどうすれば良いでしょうか。
1 つはリスト 4 のように非同期呼び出ししないようにクライアントアプリケーション側で排他制御した上で IBM DAV によってオフロードした関数を呼び出すという方法が挙げられます。しかしこの方法ではスレッドの待ち時間が増えてしまい、コンピュータノードの演算能力を十分に発揮できない可能性もありますし、結果としてパフォーマンスの劣化にもつながりかねません。
もう 1 つの方法はサービスを分ける、すなわちサーバ側のライブラリを分けて作成するということが挙げられます。マルチスレッドから非同期的に呼び出される関数を複数のサービスとして稼動することで、マルチスレッドからの非同期呼び出しに応えることができます。スマートな解決策とは言い難いですが、クライアントから関数を非同期的に呼び出すスレッド数が少数かつ限られているような場合には検討してみても良いかもしれません。
リスト 1. マルチスレッドによる非同期呼び出しを行うクライアントプログラム (DavTestDriver.c)
#include <stdio.h>
#include <Windows.h>
#include "davtest.h"
#define DAV_THREAD_NUM 2
static DWORD WINAPI davThreadProc(LPVOID lpParameter)
{
int davThreadId = 0;
davThreadId = *((int *) lpParameter);
printf("+++ dav_sleep() +++\n");
dav_sleep(davThreadId * 10);
printf("--- dav_sleep() ---\n");
return 0;
}
int main(int argc, char *argv[])
{
int i;
int davThreadId[DAV_THREAD_NUM];
HANDLE hThreadObj[DAV_THREAD_NUM];
for (i = 0; i < DAV_THREAD_NUM; i++)
{
davThreadId[i] = i + 1;
hThreadObj[i] = CreateThread(NULL, 0, davThreadProc, &davThreadId[i], 0, NULL);
}
for (i = 0; i < DAV_THREAD_NUM; i++)
{
WaitForSingleObject(hThreadObj[i], INFINITE);
}
for (i = 0; i < DAV_THREAD_NUM; i++)
{
CloseHandle(hThreadObj[i]);
}
return 0;
}
|
リスト 2. 関数 (dav_sleep) をオフロードするためのサーバプログラム (davtest.c)
#include <stdio.h>
#include <unistd.h>
int dav_sleep(unsigned int seconds)
{
sleep(seconds);
return (0);
}
|
リスト 3. セマンティクスを付加したヘッダーファイル (davtest.h)
#if defined(__cplusplus)
extern "C" {
#endif
/**IBMDAV* @function dav_sleep
*/
int dav_sleep(unsigned int seconds);
#if defined(__cplusplus)
}
#endif
|
リスト 4. 排他制御を加えたクライアントプログラム (DavTestDriver.c)
#include <stdio.h>
#include <Windows.h>
#include "davtest.h"
#define DAV_THREAD_NUM 2
static HANDLE hMutexObj;
static DWORD WINAPI davThreadProc(LPVOID lpParameter)
{
int davThreadId = 0;
davThreadId = *((int *) lpParameter);
WaitForSingleObject(hMutexObj, INFINITE);
printf("+++ dav_sleep() +++\n");
dav_sleep(davThreadId * 10);
printf("--- dav_sleep() ---\n");
ReleaseMutex(hMutexObj);
return 0;
}
int main(int argc, char *argv[])
{
int i;
int davThreadId[DAV_THREAD_NUM];
HANDLE hThreadObj[DAV_THREAD_NUM];
hMutexObj = CreateMutex(0, FALSE, 0);
for (i = 0; i < DAV_THREAD_NUM; i++)
{
davThreadId[i] = i + 1;
hThreadObj[i] = CreateThread(NULL, 0, davThreadProc, &davThreadId[i], 0, NULL);
}
for (i = 0; i < DAV_THREAD_NUM; i++)
{
WaitForSingleObject(hThreadObj[i], INFINITE);
}
for (i = 0; i < DAV_THREAD_NUM; i++)
{
CloseHandle(hThreadObj[i]);
}
CloseHandle(hMutexObj);
return 0;
}
|
IBM DAV サーバデーモンが生成するスレッドとプロセスに注意が必要
IBM DAV のサーバデーモンはクライアントからの要求を受け取り、コンピュータノード上のライブラリに実装された関数を実行して、結果をクライアントに返します。では IBM DAV はどのようにして動作しているのでしょうか。
リスト 5 に示すプログラムをコンピュータノード上のライブラリとして IBM DAV を使ったクライアントから呼び出してみます。
リスト 5. IBM DAV のプロセス生成を調べるためのサーバプログラム (davtest.c)
#include <sys/types.h>
#include <unistd.h>
int dav_get_proc_id(void)
{
return ((int) getpid());
}
|
結果ですが、クライアントから dav_get_proc_id() 関数を複数回呼び出したところ、常に同じプロセス ID を返すことが分かりました。また pthread_self を利用してスレッド ID を取得するような同様の関数を作成して試してみましたが、常に同じスレッド ID を返すことが分かりました。すなわち、サービスとして登録したライブラリに対しては同じプロセス、同じスレッドから呼び出されるということになります。
一方でリスト 6 に示すリスト 5 のプログラムをコピーしたものを異なるサービスとして登録し、クライアントアプリケーションから呼び出してみます。
リスト 6. 異なるサービスに対する IBM DAV のプロセス生成を調べるためのサーバプログラム (davtestclone.c)
#include <sys/types.h>
#include <unistd.h>
int dav_get_proc_id_clone(void)
{
return ((int) getpid());
}
|
上記のプログラムはリスト 5 のプログラムと同じ動作をするが名前が異なるライブラリとなります。クライアントから dav_get_proc_id() 関数と dav_get_proc_id_clone() 関数を複数回呼び出したところ、図 3 にあるように両者の結果は異なるものでした。すなわち異なるプロセスから呼び出されているということになります。
図 3. 異なるサービス間のプロセス ID
ここで注意しなければならないことは、ライブラリ間 (= IBM DAV に登録するサービス間) ではプロセスが異なる、すなわちアドレス空間が異なるという点です。例えばコンピュータノード上に 2 つのライブラリを作成してクライアントから IBM DAV を使って呼び出すときに、一方のライブラリの関数を使って作成されたリソースをもう一方のライブラリの関数で使うようなことを考えた場合には対応が必要になります。
IBM DAV のオーバーヘッドを理解する
IBM DAV はネットワーク越しに存在するコンピュータノードへの関数オフロードを容易に実現しますが、ネットワーク通信を伴うため関数オフロードを行う演算処理の大小によってはオーバーヘッドが無視できないことになりえます。例えば数秒、数分、数時間もかかるような関数をオフロードするのであれば、ネットワーク通信のオーバーヘッドを考慮しても高速化の効果が得られる可能性が極めて高いですが、数ミリ秒、数マイクロ秒の処理になってくると演算時間よりもネットワーク通信時間の方が大きくなってしまうことがあります。
そこで IBM DAV を使った関数オフロードがどの程度のオーバーヘッドを伴うかを調べてみました。尚測定環境は Windows XP が動作している ThinkPad と Cell/B.E. を搭載した PLAYSTATION 3 とをギガビットイーサネットで直接接続した環境です。入出力のバッファサイズを変更して各 10000 回ずつ測定し、その際に通信に要した平均時間、最大時間、最小時間、および標準偏差を表 1 にまとめました。
表 1. IBM DAV の通信性能
| | 平均時間 (msec) | 最大時間 (msec) | 最小時間 (msec) | 標準偏差 |
|---|
| 16 Byte のデータ転送を伴う関数呼び出し | 1.874 | 14.928 | 1.199 | 0.531 | | 64 Byte のデータ転送を伴う関数呼び出し | 2.081 | 66.534 | 1.210 | 1.353 | | 128 Byte のデータ転送を伴う関数呼び出し | 2.018 | 57.374 | 1.204 | 1.098 | | 256 Byte のデータ転送を伴う関数呼び出し | 2.097 | 302.681 | 1.331 | 3.404 | | 512 Byte のデータ転送を伴う関数呼び出し | 2.165 | 47.722 | 1.345 | 1.409 | | 1 KByte のデータ転送を伴う関数呼び出し | 2.209 | 260.488 | 1.331 | 2.862 |
この測定結果から分かるように、関数の入出力データが小さい場合でも 2 ミリ秒程度の通信時間がかかってしまいます。もちろんマシン構成の違いであったり、ネットワークアダプタの違いなどにより多少異なる測定結果が得られるとは思いますが、大切なことは関数オフロードを行う際のネットワーク通信のコストとしてオーバーヘッドが少なからず存在するということです。
この通信コストに見合った高速化がコンピュータノードへの関数オフロードによって得られなければならないということを理解する必要があります。そのため処理時間が数百ミリ秒、数秒、数分、数時間程度かかってしまうような関数をコンピュータノードにオフロードすることが適しており、数ミリ秒から数十ミリ秒程度もしくはマイクロ秒オーダーの処理時間しかかからないような関数をコンピュータノードにオフロードする場合には注意が必要となります。特に、アプリケーション内で数マイクロ秒程度の極めて短い時間で処理が行われるが膨大な数の呼び出しが行われるような関数には気をつけて下さい。
IBM DAV による関数オフロードの効果が極めて高いケースをしっかりと見極める必要があります。すなわち Windows アプリケーション内で処理時間の大半を占めている箇所を IBM DAV によって関数オフロードし高速化を図ることをまず検討するわけですが、同時にオフロードする関数の特性をしっかりと把握し、関数オフロードにより得られる効果を見極めた上で IBM DAV を適用しましょう。
まとめ
本記事では IBM DAV を使って Windows アプリケーション開発を進める際の便利な小技や IBM DAV の特性を知った上でどのように使うかを見極めるためのヒントを紹介しました。
IBM DAV を使えば Windows アプリケーションへの変更を最小限に抑えつつも、コンピュータノードへの関数オフロードによってパフォーマンス改善が得られることはあります。全てのケースに適用できるわけではないこともご紹介はしましたが、それでも IBM DAV の特性を正しく理解し、使用することでアプリケーション開発の幅を大きく広げ、また強力なコンピュータパワーを Windows アプリケーションに適用することができるのです。繰り返しになりますが IBM DAV は正しく使えば極めて有用なツールです。それ故正しい使い方を理解し、最大の恩恵を受けながらアプリケーション開発に役立てていただければと思います。
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | 
|  | 梶 信也 (Shinya KAJI) は、日本IBMのソフトウェアエンジニアとして、Cell/B.E. 向けソフトウェアの開発や、組み込みの世界でドライバやミドルウェアの設計/開発を行ってきました。現在は主にソフトウェアアーキテクトとしてソフトウェアのあるべき姿を日々探求しています。 |
記事の評価
|