2007年5月14日月曜日

5/8分の宿題

1.Take last week's memory copy program, and modify it to fork() to a certain depth, then have each one of the processes time the copy of a certain amount of memory. Your program should take three arguments, the depth, the amount of memory to copy, and the number of times to copy the memory. Ideally, the amount of memory to copy should be large enough to require several seconds, but that's not practical, so have it repeat the copy some number of times. For example, fork five processes, malloc() ten megabytes each, and have the processes copy that memory one hundred times.

program source:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#define TMP_FILE ".tmpfile"
#define LINE_BUFFSIZE (40)
#define COPY_SOURCE (1024 * 1024) /* 1M Bytes */

double cputime();
void add_count();
void print_result();
void usage();
void do_alloc(int, size_t, int);
void die(char*);

int count, depth;
char source[COPY_SOURCE];

typedef struct statistics{
double time;
int process_number;
} statistics_t;

int main(int argc, char **argv){
int i;
if(argc != 4){
usage();
return 0;
}

count = 0;
signal(SIGUSR1, add_count);/* counter signal */
int times;
size_t amount;/* amount may be bigger than int */
depth = atoi(argv[1]);/* number of processes */
amount = atol(argv[2]);/* amount of memory(per process) */
times = atoi(argv[3]);/* times of copy */

if(depth < 1 || amount < 1 || times < 1){
usage();
return -1;
}

char *ptr;
int current_processes = 0;

for(i = 0;i < depth;i++){
pid_t child_pid = fork();
if(child_pid == -1){
die("fork() failed.\n");
}
if(child_pid == 0){
do_alloc(i, amount, times);
return 0;
}
// printf("fork() successed, pid: %d\n", child_pid);
}
wait();
}

void add_count(){
count++;
if(count == depth){
print_result();
}
}

void print_result(){
FILE *fp;
int i, index;
double tmpd;
char buff[LINE_BUFFSIZE];
char *cp;
statistics_t *statistics;

if((statistics = malloc(sizeof(statistics_t) * depth)) == NULL){
die("statistics memory allocation fault.");
}

if((fp = fopen(TMP_FILE, "r")) == NULL){
die("file read error.\n");
}
for(i = 0;i < depth;i++){
if((cp = fgets(buff, LINE_BUFFSIZE, fp)) == NULL){
die("file read error.\n");
}
printf("%s", buff);
/*
sscanf(buff, "%d,%.10f\n", &index, &amp;amp;amp;amp;amp;amp;amp;amp;amp;tmpd);
statistics[index].time = tmpd;
statistics[index].process_number = index;
*/
}
fclose(fp);
remove(TMP_FILE);
/*
tmpd = .0;
for(i = 0;i < depth;i++){
printf("%d -> %.10f ms\n", statistics[i].process_number, statistics[i].t\
ime);
tmpd += statistics[i].time;
}
*/
}

/**
* do_alloc() : allocate memory
*/

void do_alloc(int process, size_t size, int times){
int i, j;
double stime, etime;
char *ptr;
size_t remain;
int current_offset;

/* start memory allocation */
stime = cputime();
for(i = 0;i < times;i++){
/* allocate memory */
if((ptr = malloc(size)) == NULL){
printf("malloc() allocation fault!!\n");
}
current_offset = 0;
for(remain = size;remain > 0;){
if(remain >= COPY_SOURCE){
memcpy(ptr, source, COPY_SOURCE);
remain -= COPY_SOURCE;
}else{
memcpy(ptr, source, remain);
remain = 0;
}
}
free(ptr);
}
etime = cputime();
/* end memory allocation */

FILE *fp;
if((fp = fopen(TMP_FILE, "a")) == NULL){
die("can't open temporary file.");
}
char result[LINE_BUFFSIZE];
sprintf(result, "%d,%.10f\n", process, etime - stime);
if(fputs(result, fp) == EOF){
die("can't write file.");
}
fclose(fp);

kill(getppid(), SIGUSR1);/* termination of process */
return;
}

/**
* usage() : print usage.
*/
void usage(){
printf("Usage: forkmemcopy DEPTH AMOUNT_OF_MEMORY_TO_COPY NUMBER_OF_TIMES\n")\
;
}

/**
* get cputime
*/
double cputime(){
struct timeval t;
// void *v;
gettimeofday(&t, NULL);
return t.tv_sec + (double)t.tv_usec*1e-6;
}

/**
* common error function
*/
void die(char *s){
printf(s);
perror("ERROR(die)");
exit(-1);
}

補足: 最初に立ち上げたプロセスがManagerプロセスとなり,子プロセスをfork()していき,子プロセスがdo_alloc()内でmemcpy()を実行する.
do_alloc()は内部で結果をTMP_FILEへ追記書き込みし,終了時にManagerプロセスにSIGUSR1を送る.
ManagerプロセスはSIGUSR1の数をカウントしており,depthの数だけのSIGUSR1を受け取ったらTMP_FILEの内容を読み込み表示する.
データのやりとりにファイルを使っているが,追記モード("a")でのみ書き込みを行っているため,書き込み時に競合が起こる心配は無いと考えている.

当初,平均値の計算などもTMP_FILEから読み出した値から計算するよう実装しようとしたが,sscanf()によってdoubleの値がうまく読み込めなかったため,急遽結果を出力するだけのプログラムとした.
平均値計算には,5〜6行のPHPスクリプトを用いた.

A. Run the program with a depth of one, and report how long it takes. Repeat this program twenty-five times and report the average and the individual times.

Result:
0.8175361156
0.9691882133
0.7505040169
0.4270858765
0.3950059414
0.4412739277
0.4337899685
0.4545879364
0.3823759556
0.6414270401
0.4668221474
0.3328568935
0.3372659683
0.3725678921
0.3376910686
0.3227369785
0.3254480362
0.4398460388
0.3319430351
0.3406119347
0.6110649109
0.3521721363
0.3620688915
0.3821530342
0.3682980537

Average: 0.45585288

B. Now run the program with a depth of five. Again, repeat at least five times and report the average. Is the average higher than five times the depth one case? Why?
Result:
1.9947190285
2.0967659950
2.1174561977
2.1399757862
2.3340249062
1.6144938469
2.1689970493
2.3224248886
2.5233850479
2.6110658646
0.7902989388
1.9660778046
2.2923190594
2.4991791248
2.4989440441
0.8868319988
1.9140131474
2.2147798538
2.3721489906
2.4381871223
0.7857530117
1.3553609848
2.0533030033
2.3023898602
2.3273780346

Average: 2.024810944
Aに比べて4倍以上の実行時間がかかっている。これは、メモリ確保を同時に行うことで、メモリに対するI/Oの負荷が高まり、動作に時間がかかったことが理由に考えられる。

C. Plot the density function for the execution time. (You should have twenty-five data points here for copying a gigabyte of memory each, for the depth one and depth five cases.) Is there more variability in the depth five case?

グラフの生成にはRを用いた.
> dataA <- read.table("dataA.txt")
> hist(dataA$V1)

> dataB <- read.table("dataB.txt")
> hist(dataB$V1)




2. Run one copy of your program at the normal priority, and at the same time a second copy at lower priority, e.g. by using nice. Does the first one completely monopolize the CPU until it is finished?

以下の様なシェルスクリプトを作り実験した。

#!/bin/bash

./a.out 1 10240000 1000 > nice10.out &
nice -n 5 ./a.out 1 10240000 1000 > nice5.out &

結果、nice5.outは8.2090461254 sec, nice10.outは8.3474671841 secとなった。
なぜこうなったか考えてみると、プログラム中のメモリコピー部分(時間を計測している部分)に入る前の時点でnice10のプロセスがnice5のプロセスに実行を譲っているため、このような結果となったのではないかと考える。
そして、実際にそうであるのかどうか、timeコマンドを間に挟んで以下の様に実行した

#!/bin/bash

time ./a.out 1 10240000 1000 > nice10.out &
time nice -n 0 ./a.out 1 10240000 1000 > nice5.out &

結果、
real 0m7.870s
user 0m3.102s
sys 0m0.769s

real 0m8.384s
user 0m3.499s
sys 0m0.830s

という出力が得られた、これではやはり優先度制御がうまく行われていないのではないかと考える。


3. Find and report the time quantum for your particular system.

# cat /proc/driver/rtc
rtc_time : 15:05:37
rtc_date : 2007-05-14
rtc_epoch : 1900
alarm : 02:17:18
DST_enable : no
BCD : yes
24hr : yes
square_wave : no
alarm_IRQ : no
update_IRQ : no
periodic_IRQ : no
periodic_freq : 1024
batt_status : okay

以上より、1024HzでRTCのクロックが取れていることがわかる。


参考: プログラミング言語C,B.W.カーニハン/D.M.リッチー : http://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9EC-ANSI%E8%A6%8F%E6%A0%BC%E6%BA%96%E6%8B%A0-B-W-%E3%82%AB%E3%83%BC%E3%83%8B%E3%83%8F%E3%83%B3/dp/4320026926/ref=pd_bbs_1/250-6458066-9954607?ie=UTF8&s=books&qid=1179155847&sr=8-1

RjpWiki: http://www.okada.jp.org/RWiki/

1 件のコメント:

rdv さんのコメント...

1. fork depth, size, loop: good
A. depth one, 25 times: good
B. depth 5, 5 times: good
C. plot density function: good!
2. nice: good
3. time quantum: Is the RTC interrupt used for the process scheduling
timer? I'm not sure.

Your graphs are just about the only ones that did the density function
properly. Some additional references:

I wanted either the probability density function or the comulative
distribution function. See, e.g.
http://www.weibull.com/LifeDataWeb/the_probability_density_and_cumulative_distribution_functions.htm
http://www.crcsalinity.com.au/newsletter/SeaNews/images/dpap9903f1.gif
http://en.wikipedia.org/wiki/Probability_density_function
http://en.wikipedia.org/wiki/Cumulative_distribution_function

The PDF and CDF will visually give you an idea whether or not what you
have is a normal distribution.

Score: 10/10