OmniRpcModuleInitによるモジュールの自動初期化

OmniRpcModuleInitによって、モジュールのリモート実行プログラムが起動さ れた時に自動的に初期化の関数を呼び出すようにすることができます。これを 使うことによって、workerの初期化が必要なmaster-workerのプログラムを効 率的にプログラミングすることができます。

モジュール自動初期化とは
OmniRpcModuleInitを使ったプログラム例

モジュール自動初期化とは

モジュールの自動初期化とは、モジュールのリモート実行プログラムが起動さ れた時に自動的に初期化の関数を呼び出す機能です。

OmniRpcHandleを用いることによって、リモート実行プログラム側にデータを 保持できるため効率的なプログラムができる半面、プログラマがどのリモート 実行プログラムを使うかのプログラミングをしなくてはなりません。これに対 し、最初で説明したOmniRpcCallでは指定されたリモート関数を指定するだけ で、システムが適当なリモート実行プログラムを必要に応じて起動して実行し ますが、半面、どのリモート実行プログラムに割り当てられるかが不定になる ため、データをあらかじめセットして置くことができません。

OmniRpcのターゲットとするmaster-worker型のプログラムでは、共通なデータ をmasterとworkerが持ち、workerがそのデータに対してmasterから与えられた 異なる様々なパラメータで計算を行うという場合がよくあります。このような 場合に便利なのが、モジュールの自動初期化です。OmniRpcでは、モジュール 内にInitializeという関数があるとその他の関数の関数呼び出しに際して新た なリモート実行プログラムが起動された時にInitializeを自動的に呼び出しま す。このInitializeの時に共通のデータをセットアップし、それを実際のパラ メータを変えて呼び出す関数呼び出しで再利用することにより効率化すること ができます。セットアップするコストが大きい程、その効果は大きくなります。

OmniRpcModuleInitを使ったプログラム例

例として、サイズ10000の文字列から10種類の文字列(最大10文字)が何回現れ るかを計算するプログラムを考えてみましょう。逐次プログラムは、以下のよ うになるでしょう。

#include <stdio.h>
#include <string.h>

char data[10000];  /* data to be searched */
char str[10][10];  /* string to be compared */
int occurence[10];  /* array to record occurence */

/* prototype */
void count_occurence(char *data,char *str, int *r);

int main(int argc, char *argv[])
{
    FILE *fp;
    int i;

    if((fp = fopen("data","r")) == NULL){
	fprintf(stderr,"cannot open data file\n");
	exit(1);
    }
    fread(data,10000,1,fp);
    fclose(fp);
    
    if((fp = fopen("strings","r")) == NULL){
	fprintf(stderr,"cannot open strings file\n");
	exit(1);
    }
    for(i = 0; i < 10; i++)
	fscanf(fp,"%s",str[i]);
    fclose(fp);

    for(i = 0; i < 10; i++)
	count_occurence(data,str[i],&occurence[i]);
    
    for(i = 0; i < 10; i++)
	printf("string(%i,'%s') occuerence=%d\n",i,
	       str[i],occurence[i]);
    
    exit(0);
}

void count_occurence(char *data,char *str, int *r)
{
    int i,len,count;
    len = strlen(str);
    count = 0;
    for(i = 0; i < 10000-len; i++){
	if(strncmp(&data[i],str,len) == 0) count++;
    }
    *r = count;
}

これを、OmniRpcCallAsyncで並列化する場合には、count_occurenceをリモー ト実行プログラムにします。IDLファイルは次のようになるでしょう。

Module count_occerence;

Define count_occurence(char IN data[10000],char IN str[10], int OUT
r[]) Calls "C" count_occurence(data,str,r);

これを、上のcount_occurence関数にリンクして、登録します。 クライアントのプログラムは、count_occurenceの呼び出しをOmniRpcAsyncに かえます。

   
int main(int argc, char *argv[])
{
    FILE *fp;
    int i;
    OmniRpcReqeust reqs[10];

    OmniRpcInit(&argc,&argv);

    ... /* input data */

    for(i = 0; i < 10; i++)
    	reqs[i] = OmniRpcCallAsync("count_occurence",
	                      data,str[i],&occurence[i]);
   OmniRpcWaitAll(10,reqs);
			          
    ... 
   OmniRpcFinalize();
   exit(0);
}

この場合の問題は、リモートモジュールの関数を呼び出すたびに、data を送られてしまうことです。dataは不変ですので、送られたデータが リモート実行プログラム側で再利用することによって効率化できます。

OmniRpcModuleInitを使って、リモート実行プログラムを初期化する時点で dataをおくっておき、それぞれのOmniRpcCallでは検索する文字列だけを送り ます。まず、IDLファイルを以下のように定義します。

Module count_occerence;

Globals {
#include <string.h>
char data[10000];
}

Define Initialize(char IN input_data[10000])
{
     memcpy(data,input_data,10000);
}

Define count_occurence_each(char IN str[10], int OUT r[]){
    count_occurence(data,str,r);
}

クライアントのプログラムでは初期化を指定しておきます。

int main(int argc, char *argv[])
{
    FILE *fp;
    int i;
    OmniRpcReqeust reqs[10];

    OmniRpcInit(&argc,&argv);

    ... /* input data */

    OmniRpcModuleInit("count_occerence",data);

    for(i = 0; i < 10; i++)
    	reqs[i] = OmniRpcCallAsync("count_occurence_each",
	                      str[i],&occurence[i]);
   OmniRpcWaitAll(10,reqs);
    ... 
   OmniRpcFinalize();
   exit(0);
}

OmniRpcModuleInitは初期化に必要なデータを指定するだけで、 実際の初期化は必要なリモート実行モジュールが起動された 時に行われます。したがって、初期化に指定したポインタ引数の 領域は書き換えてないように注意する必要があります。