hxcpp: lambda using local var creates Array

75 views
Skip to first unread message

Jeff Ward

unread,
Sep 9, 2015, 12:19:29 PM9/9/15
to Haxe
Investigating with hxScout, we found this interesting behavior of lambda functions in hxcpp -- that using a local variable inside a lambda function changes the variable to an Array under the hood:

That is, with this code:

  static function local() {
    var sum:Int = 0;
    sum++;
    trace(sum);
  }

  static function lambda() {
    var sum:Int = 0;
    function increment() { sum++; }
    increment();
    trace(sum);
  }

The local() compiles to cpp:

  int sum = (int)0;
  (sum)++;


And lambda() compiles to cpp:

  Array< int > sum = Array_obj< int >::__new().Add((int)0);

  HX_BEGIN_LOCAL_FUNC_S1(hx::LocalFunc,_Function_1_1,Array< int >,sum)
  int __ArgCount() const { return 0; }
  Void run(){
    {
      (sum[(int)0])++;
    }
    return null();
  }
  HX_END_LOCAL_FUNC0((void))


There's surely a reason for this Array, perhaps to do with scope and C++ and whatnot, but since the lambda is intentionally modifying the outer variable sum, it seems like the Array shouldn't be necessary (in this case, anyway.)

This could imply that, in critical code sections, lambda usage is not ideal.

Hugh or anyone else know the reason?

Thanks!
-Jeff

Jeff Ward

unread,
Sep 9, 2015, 12:27:37 PM9/9/15
to Haxe
Ah, the HX_BEGIN_LOCAL_FUNC_S1 is obviously a clue. From Macros.h:

#define HX_BEGIN_LOCAL_FUNC_S1(SUPER,name,t0,v0) \
   struct name : public SUPER { \
   t0 v0; \
   void __Mark(hx::MarkContext *__inCtx) { DoMarkThis(__inCtx); HX_MARK_MEMBER(v0); } \
   HX_LOCAL_VISIT_0(v0) \
   name(t0 __0) : v0(__0) {}

Apparently sneaking in the mark and visits for GC reference counting? Hmm, I wonder if we can (or whether it's worth) avoiding this for int (and/or other) types.

Robert Konrad

unread,
Sep 9, 2015, 12:43:23 PM9/9/15
to Haxe
Using a local variable in a closure promotes it to a garbage collected heap variable. That's the very principle of a closure (and by the way was one of the primary reasons for not adding them to Java until Java 8). In this special case a super fancy optimizer could theoretically realize that the closure is only used locally and avoid all allocations and maybe some JavaScript implementations actually do that, but generally you should just avoid creating closures in performance critical code.

Jeff Ward

unread,
Sep 9, 2015, 12:50:08 PM9/9/15
to Haxe
Interesting. Well, at least inline can help:

  static function lambda() {
    var sum:Int = 0;
    inline function increment() { sum++; }

    increment();
    trace(sum);
  }

Produces the same cpp as local(). Unfortunately if you absolutely need a var reference to your function, you can't inline. But good to know. Thanks, Robert!

Lars Doucet

unread,
Sep 9, 2015, 12:56:42 PM9/9/15
to Haxe
I just tested this on some of OpenFL's text rendering functions that use lambdas, and it works! inline removes the array allocations and keeps the intended behavior.
Reply all
Reply to author
Forward
0 new messages