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 -
なぜ分散がこんな式をしているかは、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(); } }