Styles

Monday, March 10, 2014

Lazy Loading in C# - Performance vs Memory

After a long deliberating discussion with a couple of work colleagues about the pros and cons of lazy loading compared to eager loading, a few interesting conclusions arose.

Many programmers often tend to fall into the trap of instantiating all their objects during the initialization of another parent object (either in the constructor or otherwise).


    public class AccountController : BaseContoller
    {
        UserBusinessFunctions _userFunctions = null;
        RoleBusinessFunctions _roleFunctions = null;
        PermissionBusinessFunctions _permissionsFunctions = null;
        MailoutBusinessFunctions _mailoutFunctions = null;
        public AccountController()
        {
            _userFunctions = new UserBusinessFunctions();
            _roleFunctions = new RoleBusinessFunctions();
            _permissionsFunctions = new PermissionBusinessFunctions();
            _mailoutFunctions = new MailoutBusinessFunctions();
        }
        public ActionResult Index()
        {
            return View();
        }
    }

For windows applications this might be beneficial in that the initial load time may vary, however the rest of the user's experience is very smooth because everything has been loaded already.

However, for much larger projects this can seriously affect the processing of an application in terms of memory and initial loading. Imagine you had to create a new instance of AccountController just to call one function (e.g. Index()) and all that the function did was return an AcionResult object just as MVC does on the initial load of the default page. Moreover, web pages specifically have a decoupling of server side code to the client-side rendered HTML page; so memory is eventually released after a Response is complete. However, that means that the AccountController class will be instantiated every time a request is made to the Index() function. Imagine 15,000 users make a request from their browser to the same function at the same time.
The Garbage Collector may not have enough time to clean up the unused functions.

Some developers could write that off quite easily when they are under the impression that production servers are enormous beasts with infinite amount of RAM. That may be true to an extent, but when you use only one production server to host multiple sites, each of which can have many thousands of users, memory can be easily chewed up. You may have a cloud solution for this, and it could be as simple as "upping" the memory in cloud management, but that could get very expensive very fast.

Lazy loading is the best solution to solve this problem.

    public class AccountController : BaseContoller
    {
        UserBusinessFunctions _userFunctions = null;
        RoleBusinessFunctions _roleFunctions = null;
        PermissionBusinessFunctions _permissionsFunctions = null;
        MailoutBusinessFunctions _mailoutFunctions = null;
        public AccountController()
        {
        }
        public ActionResult Index()
        {
            return View();
        }
        public UserBusinessFunctions UserFunctions
        {
            get
            {
                if (_userFunctions == null)
                {
                    _userFunctions = new UserBusinessFunctions();
                }
                return _userFunctions;
            }
        }
        public RoleBusinessFunctions RoleFunctions
        {
            get
            {
                if (_roleFunctions == null)
                {
                    _roleFunctions = new RoleBusinessFunctions();
                }
                return _roleFunctions;
            }
        }
        public PermissionBusinessFunctions PermissionsFunctions
        {
            get
            {
                if (_permissionsFunctions == null)
                {
                    _permissionsFunctions = new PermissionBusinessFunctions();
                }
                return _permissionsFunctions;
            }
        }
        public MailoutBusinessFunctions MailoutFunctions
        {
            get
            {
                if (_mailoutFunctions == null)
                {
                    _mailoutFunctions = new MailoutBusinessFunctions();
                }
                return _mailoutFunctions;
            }
        }
    }


As shown above, there is no longer any instantiation made in the constructor. This means that for every call to the Index() function, there will no longer be any unnecessary allocations in memory to unused objects just to return an ActionResult object.

What that also means is, if you actually want to use one of the business function objects in the example above, then rather than using the _userFunctions variable, you would have to access the property instead:

User user = this.UserFunctions.GetUser(id);

There is also another page that elaborates on the new C# 4.0 feature of "Lazy Initialization" which is slightly different to the standard "Lazy Loading" pattern mentioned above.


I can see advantages in all three of the scenarios mentioned above:
  • Initializing all in the constructor - good for Silverlight apps where performance is vital over memory usage on the client side. What that means is that there will be more time loading the Silverlight progress bar at the beginning while the application loads the objects in its memory resources, but the rest of the experience is seamless from then on.
  • Lazy loading in the property - good for servers that have multiple web applications/web services/win services running where slight performance trade-offs for valuable memory is important.
  • C#4.0 "Lazy Initializing" - good for examples where you instantiate a bunch of objects before entering a large loop, but want the loop to start as soon as possible.



No comments :