現時点のTraceMonkeyを検証してみる

Firefox3.1の開発版に実装された現時点のTraceMonkeyを検証してみる。

なお、使用したFirefox3.1は開発中のバージョン (Gecko/20080825031951 Minefield/3.1a2pre) でありリリース版では全く異なる結果になるかもしれない。また、テストの実施は木俣個人のPC環境で行ったため、これについても異なる環境では異なる結果になるかも知れないので、あくまで参考程度のものとしてご理解ください。

また、木俣はJITを含め、compilerの最適化などについて特に詳しいわけではないので、解釈、解説には誤りがあるかもしれません。この点についてもご了承くださいというか、より詳しい人の突っ込みと解説希望。

検証1: フィボナッチ数を求めてみる (その1)

TraceMonkeyはJust-In-Time (JIT) Compilerというcodeを動的に最適化する機能によりscriptの実行速度を高速化するらしい。ということで、再帰処理によるフィボナッチ数の計算速度を検証してみた (なお、検証には関係ないが、負の項数のフィボナッチ数には対応してない)。

検証code1
function Fibonacci(n){
	if(n <= 0){
		return 0;
	}
	if(n === 1){
		return n;
	}
	return arguments.callee(n-1) + arguments.callee(n-2);
}
var n = 30;
var testStartTime = new Date().getTime();
temp = Fibonacci(n);
var testEndTime = new Date().getTime();
alert("Fibonacci(" + n.toString() + ") is " + temp.toString() + ".\nTIME:" + (testEndTime - testStartTime).toString());
検証code1の結果 (10回試行し、最大、最小を除いた8回の平均)
javascript.options.jit.content = false
5664 [msec]
javascript.options.jit.content = true
5601 [msec]

ほぼ効果なし。

検証2: フィボナッチ数を求めてみる (その2)

arguments.calleeでは駄目なのかも知れない。ということで、関数名で再帰するようにして検証してみた。

検証code2
function Fibonacci(n){
	if(n <= 0){
		return 0;
	}
	if(n === 1){
		return n;
	}
	return Fibonacci(n-1) + Fibonacci(n-2);
}
var n = 30;
var testStartTime = new Date().getTime();
temp = Fibonacci(n);
var testEndTime = new Date().getTime();
alert("Fibonacci(" + n.toString() + ") is " + temp.toString() + ".\nTIME:" + (testEndTime - testStartTime).toString());
検証code2の結果 (10回試行し、最大、最小を除いた8回の平均)
javascript.options.jit.content = false
675 [msec]
javascript.options.jit.content = true
675 [msec]

まったく効果なし。

検証3: フィボナッチ数を求めてみる (その3)

再帰では駄目なのかもしれない。ということで、for文でフィボナッチ数を求めるようにして検証してみた。

検証code3
function Fibonacci(n){
	var temp = 0, temp1 = 0, temp2 = 0;
	for(var i = 0; i <= n; i++){
		if(i <= 1){
			temp = i;
		}else{
			temp = temp1 + temp2;
		}
		temp2 = temp1;
		temp1 = temp;
	}

	return temp;
}

var n = 30;
var testStartTime = new Date().getTime();
for(var i = 0; i < 100000; i++){
	temp = Fibonacci(n);
}
var testEndTime = new Date().getTime();
alert("Fibonacci(" + n.toString() + ") is " + temp.toString() + ".\nTIME:" + (testEndTime - testStartTime).toString());

なお、for文でFibonacci(30)を求めた場合、1msec以下で完了してしまうため、10万回実行するようにした。

検証code3の結果 (10回試行し、最大、最小を除いた8回の平均)
javascript.options.jit.content = false
460 [msec]
javascript.options.jit.content = true
48 [msec]

約9.6倍の高速化。

検証4: 最適化が困難だと思うオブジェクトの操作の場合

今度は逆に、最適化が困難だと思うオブジェクトの操作について、JITの評価が裏で動くことによりどれくらい遅くなるか検証してみる。

検証code4
function Point(x, y){
	this.x = x;
	this.y = y;
};
Point.prototype.setX = function(x){
	this.x = x;
};
Point.prototype.setY = function(y){
	this.y = y;
};

var n = 100000;
var testPoint = new Point(0, 0);
var testStartTime = new Date().getTime();
for(var i = 0; i < n; i++){
	testPoint.setX(i);
	testPoint.setY(i);
}
var testEndTime = new Date().getTime();
alert("TIME:" + (testEndTime - testStartTime).toString());
検証code4の結果 (10回試行し、最大、最小を除いた8回の平均)
javascript.options.jit.content = false
67 [msec]
javascript.options.jit.content = true
111 [msec]

約1.7倍の低速化 (約0.6倍の高速化)。

結論

  • for文などによる単純な計算の繰り返しは、JITによりかなり速くなる
  • 再帰処理については、(今回の検証コードでは) JITの負荷と最適化がつりあい、ほぼ効果がない
  • オブジェクトの操作など、最適化が困難な場合、JITによりむしろ遅くなる場合がある

あと、JITと関係ないが。

  • 再帰処理は遅い
  • arguments.calleeによる再帰処理はすごく遅い