Announcement

Collapse
No announcement yet.

[CLOSED] [#921] Lift simple anonymous functions into named-functions for performance wins

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

    #16
    We've modified the output of these Lifted Methods.

    I will continue to demonstrate the new output by using the same samples from post #7 above.

    C# Test Case
    using System.Collections.Generic;
    using System.Linq;
    using Bridge.Html5;
    
    namespace BridgeLambdaLiftPreview
    {
        public class Class1
        {
            [Ready]
            public static void Go()
            {
                Console.WriteLine(string.Join(", ", (new Example(123)).GetValues()));
            }
        }
    
        public class Example
        {
            private readonly int _offset;
            public Example(int offset)
            {
                _offset = offset;
            }
    
            public IEnumerable<int> GetValues()
            {
                var localValue = 456;
                return
                    new[] { 1, 2, 3 }
                    .Select(value => value + 1) // Simple lift (single-variable)
                    .Select(value => value + 1) // Simple lift (single-variable) - repeat
                    .Select((value, index) => value + index) // Simple lift (two-variable)
                    .Select(value => value + _offset) // Lift and bind (single-variable)
                    .Select((value, index) => value + index + _offset) // Lift and bind (two-variable)
                    .Select(value => value + localValue); // Non-lift
            }
        }
    }
    Goal #1: Lift anonymous functions into the statics config

    **Achievement Unlocked**

    In the following sample you can see the dynamically created $f1, $f2, etc functions.

    Attempt #1 JavaScript

    (function (globals) {
        "use strict";
    
        Bridge.define('BridgeLambdaLiftPreview.Example', {
            _offset: 0,
            constructor: function (offset) {
                this._offset = offset;
            },
            getValues: function () {
                var localValue = 456;
                return Bridge.Linq.Enumerable.from([1, 2, 3])
                    .select(BridgeLambdaLiftPreview.Example.$f1)
                    .select(BridgeLambdaLiftPreview.Example.$f2)
                    .select(BridgeLambdaLiftPreview.Example.$f3)
                    .select(Bridge.fn.bind(this, BridgeLambdaLiftPreview.Example.$f4))
                    .select(Bridge.fn.bind(this, BridgeLambdaLiftPreview.Example.$f5))
                    .select(function (value) {
                        return value + localValue;
                    }); // Non-lift
            },
            statics: {
                $f1: function (value) {
                    return value + 1;
                },
                $f2: function (value) {
                    return value + 1;
                },
                $f3: function (value, index) {
                    return value + index;
                },
                $f4: function (value) {
                    return value + this._offset;
                },
                $f5: function (value, index) {
                    return value + index + this._offset;
                }
            }
        });
        
        Bridge.define('BridgeLambdaLiftPreview.Class1', {
            statics: {
                config: {
                    init: function () {
                        Bridge.ready(this.go);
                    }
                },
                go: function () {
                    console.log(Bridge.toArray((new BridgeLambdaLiftPreview.Example(123)).getValues()).join(", "));
                }
            }
        });
        
        Bridge.init();
    })(this);
    This change provided a huge improvement, although it bothered me that these dynamically created functions were now polluting the public api of the object. For example, using browser tools, you could see BridgeLambdaLiftPreview.Example.$f1 on the object. Potentially there could be a large amount of these dynamic members added to the object. Wouldn't look good and would cause confusion. We needed to do something about that.

    Goal #2: Lift the anonymous functions into a private object within the closure, thereby hiding from the public api

    **Achievement Unlocked**

    (function (globals) {
        "use strict";
    
        Bridge.define('BridgeLambdaLiftPreview.Example', {
            _offset: 0,
            constructor: function (offset) {
                this._offset = offset;
            },
            getValues: function () {
                var localValue = 456;
                return Bridge.Linq.Enumerable.from([1, 2, 3])
                    .select($_.BridgeLambdaLiftPreview.Example.f1)
                    .select($_.BridgeLambdaLiftPreview.Example.f1)
                    .select($_.BridgeLambdaLiftPreview.Example.f2)
                    .select(Bridge.fn.bind(this, $_.BridgeLambdaLiftPreview.Example.f3))
                    .select(Bridge.fn.bind(this, $_.BridgeLambdaLiftPreview.Example.f4))
                    .select(function (value) {
                        return value + localValue;
                    }); // Non-lift
            }
        });
        
        var $_ = {};
        
        Bridge.ns("BridgeLambdaLiftPreview.Example", $_)
        
        Bridge.apply($_.BridgeLambdaLiftPreview.Example, {
            f1: function (value) {
                return value + 1;
            },
            f2: function (value, index) {
                return value + index;
            },
            f3: function (value) {
                return value + this._offset;
            },
            f4: function (value, index) {
                return value + index + this._offset;
            }
        });
        
        Bridge.define('BridgeLambdaLiftPreview.Class1', {
            statics: {
                config: {
                    init: function () {
                        Bridge.ready(this.go);
                    }
                },
                go: function () {
                    console.log(Bridge.toArray((new BridgeLambdaLiftPreview.Example(123)).getValues()).join(", "));
                }
            }
        });
        
        Bridge.init();
    })(this);
    Now we get lifted functions.

    AND they're private.

    AND no change to the developers C# code.

    This was all handled automatically by Bridge and did not require a single modification or change in technique within your C# app code.

    Any functionality required to enable this enhancement on the client side, such as Bridge.ns, already existed so there were no changes to bridge.js required either. This was a pure compiler level optimization.

    This lifted function scenario may be the single best demonstration of what we're trying to achieve here with Bridge. Having a layer of abstraction can yield big wins. Even very minor refinements made to the compilation process are spread across your apps and all the other apps being developed on Bridge.NET. As a developer, you didn't need to do anything, yet you automagically pick up all these amazing feature and performance gains. All completely transparent to the end user developer.

    These changes will be included in the Bridge 1.11 release.

    Hope this helps.

    [high-five]
    Last edited by geoffrey.mcgill; 2016-02-16 @ 03:01 AM.

    Comment


      #17
      I've only just seen this last comment - I'm glad that you're as excited as I am, I agree that it's a great win and it's all for free when you use Bridge! (And nice catch on the making-private of those functions, while I'm intending to use Bridge to write pure-C# code I'm still very glad that you guys are on the ball with keeping the JavaScript clean and tidy for people who may consume it through JavaScript!).

      Comment

      Working...
      X