Friend types are slow ... really?

If you followed the post about friend types or the discussion about that topic on the haXe mailing list started by Michael Baczynski, you may believe that friend types can be slow. This is true (we are talking about targeting flash 9 here) if you use a temporary variable to "cast" the friend instance, but there is a workaround for that.

So suppose you have this class:

class Test
{
  var value : Int; // this is the private field we want to expose as friend
  public function new() {
    value = 1;
  }
}

The friend type is:

typedef TestFriend = {
  private var value : Int;
}

So what are your options?

The simple one:

var test = new Test();
var friend : TestFriend = test;
var v = friend.value;

This is not fast because test needs to be converted to a dynamic type to be used like that. Here is the alternative, first create an utility method as such:

class TestUtil {
  public static inline function getValue(t : TestFriend) {
    return t.value;
  }
}

Then use it:

var test = new Test();
var v = TestUtil.getValue(test);

Does it perform better? A lot (even faster that untyped). Is it type safe? Sure ... no need for untyped.

Using "using" makes it even nicer:

using TestUtil;
// ...
var test = new Test();
var v = test.getValue();

About perfomances, I've made a test on 2.000.000 iterations (average on 5 cycles) and here are the results:

  • using a temp var (reassigned on each iteration): reference time (454.0 ms)
  • using a temp var (assigned once): 14% faster (397.8 ms)
  • untyped: 46% faster (310.6 ms)
  • using the static getValue() function: 50% faster (301.8 ms)

Comments

Missing bench

Did you test how fast direct Test access is compared to friend type?

Or did I read over it?

I haven't because it would be

I haven't because it would be a different Test class, but I did the same and strangely enough it is slower than the proposed solution by 4%.

Use case

You can also use it this way:

typedef FooFriend = {
public var x : Int;
}

class Foo {

public var x : Int;
public function new () { x = 5; }

public static inline function friendly (m:FooFriend) : FooFriend {
return m;
}
}

using Main.Foo;

class Main {

public static function main ():Void
{
var f = new Foo();
trace(f.friendly().x);
}
}

I guess I am missing

I guess I am missing something here, why should I do that if I can simply do f.x? x is public no need for friends.

sorry, please replace public

sorry,
please replace public with private.
my purpose was to use one function to access all variables that you need, for example


package;

class Test {
private var a : Int;
private var b : Int;
private var c : Int;
private var d : Int;
public function new () {
a = 1;
b = 2;
c = 3;
d = 4;
}
public static inline function friend (t : TestFriend) : TestFriend {
return t;
}
}

typedef TestFriend = {
private var a : Int;
private var b : Int;
private var c : Int;
private var d : Int;
}

using Main.Test;

class Main {

public static function main ():Void
{
var t : Test = new Test();
trace(t.friend().a);
trace(t.friend().b);
trace(t.friend().c);
trace(t.friend().d);

}
}

cheers
heinz

Makes sense this way ;)

Makes sense this way ;)