カーネル内での特定処理にかかる時間を測定

NetBSD 1.6でカーネル内での特定処理にかかる時間を測定しようと思ったのだが、そういう仕組みは入ってなさそうだったので、Quick & Dirtyに書いてみた。

コード

#define MEASURE_TIMES 10000

typedef struct {
	int id;
	int time;
	unsigned start[MEASURE_TIMES];
	unsigned end[MEASURE_TIMES];
} measure_record_t;

static void measure_get_diff(measure_record_t *r, unsigned *diff);
static void measure_dump(measure_record_t *r);
static unsigned measure_diff_min(unsigned *array);
static unsigned measure_diff_max(unsigned *array);
static unsigned measure_diff_median(unsigned *array);
static unsigned measure_diff_average(unsigned *array);
static void qsort(void *a, size_t n, size_t es,
		  int (*cmp) __P((const void *, const void *)));

static inline int measure_start(measure_record_t *r)
{
	if(r->time < MEASURE_TIMES) {
		r->start[r->time] = cpu_counter32();
		return r->time++;
	}
	return -1;
}

static inline void measure_end(measure_record_t *r, int time)
{
	KASSERT(r->time != -1);
	r->end[time] = cpu_counter32();
	if(time == MEASURE_TIMES - 1) {
		measure_dump(r);
		r->time = 0;
	}
}

static void
measure_dump(measure_record_t *r)
{
	unsigned diff[MEASURE_TIMES] = {0};
	measure_get_diff(r, diff);
	printf("id:%d min:%u max:%u median:%u average:%u\n",
	       r->id,
	       measure_diff_min(diff),
	       measure_diff_max(diff),
	       measure_diff_median(diff),
	       measure_diff_average(diff));
}

static void 
measure_get_diff(measure_record_t *r, unsigned *diff)
{
	int i;
	for(i = 0; i < MEASURE_TIMES; i++) 
		diff[i] = r->end[i] - r->start[i];
}

static unsigned 
measure_diff_min(unsigned *array)
{
	unsigned res = UINT_MAX;
	int i;
	for(i = 0; i < MEASURE_TIMES; i++)
		if(array[i] < res)
			res = array[i];
	return res;
}

static unsigned 
measure_diff_max(unsigned *array)
{
	unsigned res = 0;
	int i;
	for(i = 0; i < MEASURE_TIMES; i++)
		if(array[i] > res)
			res = array[i];
	return res;
}

static int 
unsigned_cmp(const void *_a, const void *_b)
{
	unsigned a = *(unsigned *)_a;
	unsigned b = *(unsigned *)_b;
	if(a < b)
		return -1;
	else if(a > b)
		return 1;
	return 0;		
}

static unsigned 
measure_diff_median(unsigned *array)
{
	qsort(array, MEASURE_TIMES, sizeof(unsigned), unsigned_cmp);
	return array[MEASURE_TIMES / 2];
}

static unsigned
measure_diff_average(unsigned *array)
{
	unsigned long long sum = 0;
	int i;
	for(i = 0; i < MEASURE_TIMES; i++)
		sum += array[i];
	return (unsigned)(sum / (unsigned long long)MEASURE_TIMES);
}

使い方

measure_record_t mr0 = {0,}; /* 変数準備 0としているのは識別用ID */
int mt0 = measure_start(&mr0); /* 測定開始 */
hoge(); /* 測定対象 */
measure_end(&mr0, mt0); /* 測定終了 */

ものすごく汚いが、mbufにm->m_pkthdr.measure_timeとかでっち上げ

m->m_pkthdr.measure_time = measure_start(&mr0);

として、別のコンテキストから

measure_end(&mr0, m->m_pkthdr.measure_time);

とかやるとどこからどこへ流れてる間にどの程度時間がかかったのかパケット毎に追跡出来る。
はず。

netisr(softint)を跨ぐ追跡がやりたかったのでこんな風にしてみましたが何か。

ちなみにqsortはカーネルにリンクされてる筈がないので、libcからぶんどってきた。