Saturday, March 27, 2010

HaXe iterators vs whiles

I have run some test about haXe iterators. Iterators provide a very nice abstraction but their speed when used in for loops was a big disappointment.

I tested it by creating many different ways to calculate the fibonacci series

Infinite Fibonacci Iterator:

class FibIter1 {
var val : UInt;
var nextVal : UInt;

public function new() {
val = 0;
nextVal = 1;
}

public inline function hasNext() : Bool {
return true;
}

public inline function next() : UInt {
var tmpVal = val;
val = nextVal;
nextVal = tmpVal + val;
return tmpVal;
}
}

Bounded Fibonacci Iterator:

class FibIter2 {
var val : UInt;
var nextVal : UInt;
var count : UInt;

public function new( count : UInt ) {
val = 0;
nextVal = 1;
this.count = count;
}

public inline function hasNext() : Bool {
return count != 0;
}

public inline function next() : UInt {
var tmpVal = val;
val = nextVal;
nextVal += tmpVal;
count--;
return tmpVal;
}
}

Test code:

package ;

import flash.Lib;
import org.flashdevelop.utils.FlashConnect;

class Main
{

inline static var Test = 5;
static function main() {


var startTime = Lib.getTimer();
var acc = 0;
for ( i in 0...99999 ) {
if ( Test == 1 ) {

// ********************************
// Test 1
// Infinite Iterator + Manual Break
// ********************************

var count : UInt = 99;
for ( a in new FibIter1() ) {
if ( count-- == 0 ) break;
acc += a;
}

}else if ( Test == 2 ) {

// ********************************
// Test 2
// Bounded iterator
// ********************************

for ( a in new FibIter2(99) ) {
acc += a;
}

}else if ( Test == 3 ) {

// ********************************
// Test 3
// Manual algorithm using while
// ********************************

var val : UInt = 0;
var nextVal : UInt = 1;
var count : UInt = 99;
while ( count-- != 0 ) {
acc += val;
var tmpVal = val;
val = nextVal;
nextVal += tmpVal;
}

}else if ( Test == 4 ) {

// ********************************
// Test 4
// Manual algorithm using for
// ********************************

var val : UInt = 0;
var nextVal : UInt = 1;
for ( i in 0...99 ) {
acc += val;
var tmpVal = val;
val = nextVal;
nextVal += tmpVal;
}

}else if ( Test == 5 ) {

// ********************************
// Test 5
// Bounded iterator using while
// ********************************

var iter = new FibIter2(99);
while ( iter.hasNext() ) {
acc += iter.next();
}

}

}

FlashConnect.trace(acc);
var endTime = Lib.getTimer();
FlashConnect.trace( "Test " + Test + ": " + (endTime - startTime) );

}
}

Results (In haXe 2.05):

Test 1: 5893ms
Test 2: 5692ms
Test 3: 125ms
Test 4: 131ms
Test 5: 136ms

Conclusions:

After being disappointed by the results of the for expression I decided to check the generated bytecode: apparently the haXe compiler wont inline iterator methods in a for loop. The test 5 shows that the iterator class can be pretty fast if it is correctly inlined.

I can't think of a good reason why the compiler shouldn't be able to inline the iterator methods, which can be a good thing, because if there is no good reason then maybe in future versions of haxe for will inline too.


No comments:

Post a Comment