Click here to Skip to main content
Click here to Skip to main content

Lazy Alternatives - LazyAndWeak and BackgroundLoader

By , 1 Dec 2011
 

Introduction

This article will present two alternatives to Lazy. One of them is focused on low memory use and the other focused on responsiveness.

Background

.NET 4 added the Lazy class. Its purpose is to allow fast load times and reduce memory consumption.

But, should I say that I consider Lazy class... too lazy? There are two problems that I see when using the Lazy class:

  1. The application loads fast, but then you must wait for each item that you access for the first time.
  2. If you load an item to use it only once, it will be kept in memory forever (ok... until the lazy object itself is collected).

So, to solve those problems, I decided to create my own solution, which I will present here.

Lazy Alternatives

I really wanted to create a solution that is both responsive and that avoids using memory when it is not needed. But unfortunately that's impossible... at least for me and as a generic purpose solution. But I can solve one of those problems at a time.

So I did it, as two different classes:

  • LazyAndWeak - Better memory management
  • BackgroundLoader - Better responsiveness

LazyAndWeak

For human beings, being lazy is usually bad. Being weak too is even worse. But for programming languages, being lazy and weak is good, especially when we want to save memory.

LazyAndWeak only creates its Value when it is first requested, as happens with Lazy class. But it also stores the value as a weak-reference, so it can be collected if memory is needed. Ok, it is a bit more than that. To avoid values to be collected at each garbage collection, I use my GCUtils.KeepAlive method so recently used items are not collected in the next full collection, so constantly used items will always be in memory, even after a full collection.

The big downside here is that after the item is unloaded from memory, if it is needed again, the user will need to wait again and any value that was stored in such item will be lost. So, it is very important that the constructor/creator knows how to get the most up-to-date information.

BackgroundLoader

The BackgroundLoader class goes in the opposite direction of LazyAndWeak. Instead of creating items only when requested and then allowing them to die, it tries to load all items in the background, trying not to affect performance of the application and then keeps them there. So, you load the application in a very fast manner, similar to what happens to any Lazy alternative but then when a CPU is idle, it starts loading the items that you didn't use yet.

I say that it goes in the opposite direction because it will not save any memory, as sooner or later all the items will be loaded and kept in memory. But except from the very first item (in case the user requests it as soon the application is loaded), the user will not need to wait for items to load. It opens the application (in a very fast manner), maybe wait for the very first operation to load its items but, later, any other operation will be done instantly as the items will be already loaded.

When To Use Each One?

In my opinion, never use Lazy. Maybe I am too drastic, but I don't see a real situation were Lazy only will be better. For example, if your application has 1000 menu items, each one with a slow load-time, and your users are probably only using 10 of those 1000 menu items, then it is OK.

But what happens when the user does load those 1000 items? They will be kept in memory forever. So, the user will need to wait 1000 times and then those will be there forever (so new requests will not wait).

But if those 1000 items consume too much memory, I'll say that you should use LazyAndWeak, after all the computer gets slower when running out-of-memory. If you think it is OK to keep all of them in memory, then use BackgroundLoader as the user will wait for the program to load, maybe will wait for the first menu item to load... and if he keeps enough time there, when he uses any other item it will be already in memory, without the need to wait again.

In fact, for very large projects, I usually open the application, go out for a while and then return, expecting everything to be loaded. I consider it frustrating that, in Visual Studio, I end-up waiting again when I try to load an ASP.NET project for the first time or a WPF one.

Implementation

I know CodeProject is for code... but should I really show the code here? I think explaining the basic idea is enough. So, I will only explain the idea.

LazyAndWeak uses a lock when creating the value (so two threads will not create duplicate values). When accessing the Value, a call to GCUtils.KeepAlive is made. I explained that class in one of my first articles, WeakReferences as a Good Caching Mechanism. The main problem of using WeakReferences only is that items are discarded at every collection, even if they were recently used. This class forbids them to be collected at the next collection but allows them to be collected if a second collection happens and they are still unused.

BackgroundLoader is harder. A "loader" thread is created. Each time a LazyLoader is created, it registers itself inside that thread and sets an event (ManagedAutoResetEvent) so the loader thread starts to run. If 1000 items are registered, they will be loaded in order (there is not the risk of 1000 threads trying to load their items). This class also uses locking when accessing its Value so if it is not loaded yet, it loads it immediately and removes this BackgroundLoader instance from the loader class, as it will be already loaded.

Also, there is the problem that you may create the background loader and then you discard it (in the sample project, when you change from one directory to another). That's why I implemented the IDisposable interface. If the item is not loaded yet, it is in the list of "to load" items. If you discard it, it simply removes such item from the "to load" list.

How to Use the Classes

Using those classes is very similar to using the .NET Lazy class. You simply create the LazyAndWeak<SomeType> or BackgroundLoader<SomeType> at the very first moment, probably with a declaration like this:

private readonly LazyAndWeak<SomeType> field = new LazyAndWeak<SomeType>();

And when you need to access the SomeType instance, you must access the Value property of the created field.

It is important that SomeType has a default constructor or that you give a creator delegate to it, or you will get an exception. Also, as happens with lazy, those helper objects are only useful to hold slow loading or memory-hungry objects. Don't use them for fast and small objects, as the helper objects themselves consume memory.

Sample

The attached sample is a very simple image visualizer. Its purpose is only to show the differences of the various techniques.

When you enter any directory, all image names are read and some type of lazy-loading will be done. When just opening or scrolling large folders, you will see red lines instead of images. Red lines means such images are not still in memory.

If you force collection, already loaded images will be reloaded using LazyAndWeak (which shows it worked) but will not have any effects on BackgroundLoader or custom Lazy.

I forced a 100 milliseconds delay when loading images. That was on purpose to make the differences more visible as loading images is not that slow.

If you are using BackgroundLoader, when you change directory, it will stop loading the old files. That's why Dispose was needed. If I didn't do that, if I entered directory A, then B... B will only load after loading all (useless) A files.

History

  • 1st December, 2011: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Paulo Zemek
Canada Canada
Member
I started to program computers when I was 11 years old, as a hobbist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.
 
At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do they work easier, faster and with less errors.
 
Want more info or simply want to contact me?
Take a look at: www.paulozemek.com
Or e-mail me at: paulozemek@outlook.com

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
SuggestionLazyAndWeakAndReactiveAndDependent (LWRD)protectorAspDotNetDev1 Dec '11 - 9:02 
Your article presents a neat idea. Some more capabilities you might consider allowing for:
  • Reactive: Only allow for loading once some event has occurred. As an example, this might be useful for loading a variable once the form load event occurs. Maybe something like:
    var allower = new Instigator();
    var var1 = new LWRD<SomeType>(allower, factory);
    /* Some code later... */
    allower.instigate();
  • Dependent: One lazy variable will only load once another lazy variable has completed. As an example, this might be useful for loading a submenu only once the parent menu has loaded. Like this:
    var var1 = new LWRD<SomeType>(allower, factory1);
    // Only create var2 once var1 has been created (or when its value is requested).
    var var2 = new LWRD<SomeType2>(var1, factory2);

 
I'm sure you can think of a more pretty way to design this (e.g., one might consider a dependent the same thing as a reactive variable, and the idle event might also be considered another reactive variable instigated by the processor becoming idle), but thought I'd present the idea. Smile | :)
Somebody in an online forum wrote:
INTJs never really joke. They make a point. The joke is just a gift wrapper.

GeneralRe: LazyAndWeakAndReactiveAndDependent (LWRD)memberPaulo Zemek1 Dec '11 - 9:41 
Well... I think it is not direct, but it is already possible to make it Dependent and Reactive.
In fact, if you have a tree, were each child is composed of Lazy childs, you will need to instantiate the base node, to then request one sub-node, to then request another sub-node.
 
So, instead of:
Lazy A
Lazy B -> Which depends on A.
 
It will be:
Lazy A
Inside A there is Lazy B.
Which in turn can have Lazy C.
So, even if there is a public property B directly, I will access it through a.Value.b.Value, which will resolve all the dependencies.
 
If that's not the case, then I think I need more explanations.
 
---
 
Another option is that B uses a delegate to be constructed. And such delegate uses A.Value. That will make dependency a natural step.
 
And for Reactive only code, you must remember that you must create the Lazy object itself.
So: Before the event, the LazyAndWeak or BackgroundLoader class is null.
In the event, you instantiate it (not its value).
Then the code that depends on the value should first check if the Lazy object is null or not (and generate the appropriate error or result if it is not) and, if it is, then call the Value.
Do you want to create a new programming language?
Do you want to know how to create a virtual machine?
Are at least interested on how they work?
So, see my article: POLAR

GeneralRe: LazyAndWeakAndReactiveAndDependent (LWRD)protectorAspDotNetDev1 Dec '11 - 10:22 
Lazy itself (and your alternatives) is really just a small class to consolidate functionality. My two variations would be the same. Of course you could implement them based on the existing three variations, having two more would provide that functionality out of the box.
 
Paulo Zemek wrote:
In fact, if you have a tree, were each child is composed of Lazy childs, you will need to instantiate the base node, to then request one sub-node, to then request another sub-node.

 
Right. IF you have a tree. That was just one example. The lazy/background dependency might be between objects which are themselves not dependent. For example, one might make printing functionality available to load once a grid has been loaded (because maybe people tend not to print unless there is a grid visible). One might also create many to many relationships for the dependencies. So printing functionality might be made available to load once a grid has been loaded or an image has been loaded, and either of those two things loading might also make available to load exporting to PDF functionality.
 
And this is an important point too: making something "available to load" doesn't necessarily load it. It could then wait for the computer to be idle, for example. And, it being made "available to load" is really just a hint... if some code explicitly asks for the value, it will load immediately even if it was not previously made available to load.
 
Paulo Zemek wrote:
Then the code that depends on the value should first check if the Lazy object is null or not

 
This is basically what the lazy class is there to avoid. You don't want to have to check for null. You want the lazy wrapper itself to exist and a request to the LazyWrapper.Value to either return the already created instance or a new instance. To clarify, the reactive lazy would only make available to load on some event (perhaps controlled by the helper class I demonstrated with). It might then wait for the computer to be idle or a call to blah.Value to instantiate the value.
 
Anyway, just some ideas for you to think about. Your article is already a neat idea and a complete article. Smile | :)
Somebody in an online forum wrote:
INTJs never really joke. They make a point. The joke is just a gift wrapper.

GeneralRe: LazyAndWeakAndReactiveAndDependent (LWRD)memberPaulo Zemek1 Dec '11 - 10:49 
Now I understood a little more about your idea... and still feel a little lost.
OK. I agree that checking for null is against the Lazy class itself.
But by the way my BackgroundLoader works, if you have A and B, it will first load A and then load B, even if they are unrelated.
 
If B depends on A, then my two solutions are the "best" I can see as a generic purpose solution.
Or A instantiates B, so you can create the B property directly, but it will redirect to a.Value.b.Value, or B will have a creator delegate that will use A.
 
---
 
Finally... if I understood your idea... but I am not sure:
About the printing functionality... maybe all this is related to a lazy of lazy.
 
Your program loads. While you don't use a printable grid or a printable image, there is no need to load the printing functionality, which both use.
As soon as you load a grid or an image, the printing functionality is "scheduled" to load (this is the responsability of BackgroundLoader class).
 
But, in the scenario you are telling me, you don't want to load the Printing functionality before you have any printable object.
 
Well... my initial work-around (so, it is not the ultimate solution) is:
Create a PrintingFunctionality class.
Such class has a Get() method, that returns the Lazy (Background loader) instance.
It is a method, not a property, because the callers may simple call it to instantiate the static class, without using it.
Then, the Printable objects call it when they are created... and use it later.
 
But... considering that such PrintingFunctionality is becoming available all the time after their loading... I will not make it that way... I will simple use the BackgroundLoader.
In fact, all "static" functionalities that today are lazy loaded but after are kept in memory will be BackgroundLoader to me. I will even like to be able to use BackgroundLoader over the static classes, as they use the custom Lazy pattern internally.
Do you want to create a new programming language?
Do you want to know how to create a virtual machine?
Are at least interested on how they work?
So, see my article: POLAR

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 1 Dec 2011
Article Copyright 2011 by Paulo Zemek
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid