プログラム悪戦苦闘日記

はてなダイアリーからの移行(遺物)

System.currentTimeMillis()の罠に堕(お)ちる2

 QueryPerformanceCounterがJNIで実装できたので計測をしてみた。
測定プログラムは後述。

CPU:Pentium3 1.2GHz + メモリ512MByte + WindowsXP Pro

    sleepの引数(msec)|   12  |   6  |   3
 --------------------|-------|------|-----
 Thread#sleep        | 12.40 | 6.42 | 3.37
                     |  0.15 | 1.23 | 0.11
 --------------------|-------|------|------
     System.         | 12.07 | 6.02 | 3.01
 currentTimeMillis() |  4.12 | 4.90 | 4.59
 ------------------------------------------
上段:平均
下段:標準偏差

 Thread#sleep()の結果は、標準偏差がほとんど1msec以内なので、問題ないレベルといえる。
System.currentTimeMillis()は標準偏差が4msec以上と、これは全然ダメである。
やはり、msecレベルの分解能が欲しいときは、
System.currentTimeMillis()を使ってはいけないということだ。
 これでようやく、画像エフェクトに戻れる。
 
測定用のプログラム。
分散(標準偏差)は、[tex:\sigma^2 = \frac{1}{N}\Sigma_{i}(x_i - )^2 - \frac{1}{N}\Sigma_{i}(x_i - )]を用いた。
なぜ分散がこんな式をしているかは、Numerical Recipes in CISBN:4874085601を見てくれ。

public class PerformanceCounterTest {

	public static void main(String[] args)
	throws Exception {
		System.out.println("開始...");
		Time t = new Time();
		t.start();
		t.join();
		t.result();
		System.out.println("終了");
	}
}

///
class Time extends Thread {
	final DecimalFormat format = new DecimalFormat("#0.00000");
	final int 	count 	= 5000;
	final int 	sleep 	= 3;
	final long 	freq 	= PerformanceCounter.queryPerformanceFrequency();
	
	double 	cnt1[] = new double[count];
	double 	cnt2[] = new double[count];	
	
	public void run() {
		for(int i=0; i<count; ++i) {
			long s = PerformanceCounter.queryPerformanceCounter();
			try { sleep(sleep); }
			catch(InterruptedException ie) { return; }
			long e = PerformanceCounter.queryPerformanceCounter();
			cnt1[i] = (e - s)*1000.0 / freq;
		}
		
		for(int i=0; i<count; ++i) {
			long s = System.currentTimeMillis();
			try { sleep(sleep); }
			catch(InterruptedException ie) { return; }
			long e = System.currentTimeMillis();
			cnt2[i] = e - s;
		}
	}
	
	void result() {
		double ave1 	= average(cnt1);
		double stderr1 	= stderr(cnt1, ave1);
		double max1		= max(cnt1);
				
		double ave2 	= average(cnt2);
		double stderr2 	= stderr(cnt2, ave2);
		double max2		= max(cnt2);	
		
		hist(cnt1);
		hist(cnt2);	

		
		//
		System.out.println("平均:" + format.format(ave1) + "msec\t標準偏差" + format.format(stderr1) + "\t最大" + format.format(max1));
		System.out.println("平均:" + format.format(ave2) + "msec\t標準偏差" + format.format(stderr2) + "\t最大" + format.format(max2));
	}
	
	double average(double[] x) {
		double ave = 0.0;
		for(int i=0; i<x.length; ++i) ave += x[i];
		return ave / x.length;
	}
	
	double stderr(double[] x, double ave) {
		double mom1 = 0.0, mom2 = 0.0;
		for(int i=0; i<x.length; ++i) {
			mom1 += (x[i] - ave);
			mom2 += (x[i] - ave)*(x[i] - ave);
		}
		double std = mom2/x.length - mom1/x.length;
		return Math.sqrt(std);
	}
	
	double max(double[] x) {
		double m = Double.MIN_VALUE;
		for(int i=0; i<x.length; ++i) if( m < x[i] ) m = x[i];
		return m;
	}
	
	void hist(double[] x) {
		DecimalFormat fmt = new DecimalFormat("00:");
		
		int[] cnt = new int[25];
		for(int i=0; i<x.length; ++i) {
			for(int n=0; n<cnt.length; ++n)
				if( n <= x[i] && x[i] < n+1 ) ++cnt[n];
		}
		
		System.out.println();
		for(int n=0; n<cnt.length; ++n) {
//			cnt[n] /= x.length/100;
//			System.out.print(fmt.format(n));
//			for(int i=0; i<cnt[n]; ++i)
//				System.out.print("*");
//			System.out.println();
			System.out.println(fmt.format(n) + cnt[n]);
		}
		System.out.println();
	}
}