Caching Regular Data Benefits Regular Programs, Too

We all know what a dramatic increase in speed we can give a Web application if we choose to use some kind of an output cache for a given Web-based application.

For those who are unfamiliar with the term 'Output Cache', what we generally mean is that any output generated by the application, from a source such as a database, is cached (typically in memory). Then, for further requests to the same page, the cached output is returned, rather than a new copy of the page being created each time.

If you have a lot of database queries that are used to create the page, you can often find doing this will give you a massive increase, but at the expense that the data you're working with could easily get stale.

From .NET 4 onwards, however, using output caching is now much easier to use, and also can be used in a traditional application.

Why Would I Want to Use Output Caching in a Regular Desktop App?

I can answer this using the scenario from a project, I'm currently working on...

"Rate Limits"

I'm currently working on an application that has to interface with various rate-limited API calls to Amazon Web services. The rate limit here is so ferocious that using output caching means that I don't hit my rate limit anywhere near as much as I do without it, simply because it reduces the number of live calls I need to make.

Create yourself a simple .NET console mode program. Then, to that project, add yourself a class called "FakeData.cs".

In this new class, add the following code:

using System;
using System.Linq;
using System.Threading;

namespace datacache
{
   public class FakeData
   {
      private string GenerateRandomString()
      {
         var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
         var random = new Random();
         var result = new string(
            Enumerable.Repeat(chars, 20)
               .Select(s => s[random.Next(s.Length)])
               .ToArray());
         return result;
      }

      public string GetData()
      {
         Thread.Sleep(5000);
         return GenerateRandomString();
      }
   }
}

The class is nothing special; it waits 5 seconds each time you call its GetData method, and then returns a random string. The idea here is that it simulates getting data from a Web call, or a database or something similar, and gives the impression of a delay in the process.

In your 'Main' method in Program.cs, make sure you have the following code:

using System;
using System.Diagnostics;

namespace datacache
{
   class Program
   {
      static void Main()
      {
         Stopwatch stopWatch = new Stopwatch();
         stopWatch.Start();

         FakeData myData = new FakeData();
         myData.GetData();

         stopWatch.Stop();
         TimeSpan ts = stopWatch.Elapsed;

         string elapsedTime = String.Format("{0:00}:
               {1:00}:{2:00}.{3:00}",
            ts.Hours, ts.Minutes, ts.Seconds,
            ts.Milliseconds / 10);
            Console.WriteLine("RunTime: " + elapsedTime);
      }

   }
}

If you compile and run this program, you should see something like the following:

Cache1
Figure 1: Our un-cached program always takes about 5 seconds to get its data.

Just to give things a little bit more effect, let's wrap a loop around this, and execute our get data call 10 times.

Alter your main method so it looks like the following:

static void Main()
   {
      Stopwatch stopWatch = new Stopwatch();
      stopWatch.Start();

      for(int count = 0; count < 10; count++)
      {
         FakeData myData = new FakeData();
         string res = myData.GetData();
         Console.WriteLine("Got data: {0}", res);

         TimeSpan ts = stopWatch.Elapsed;

         string elapsedTime = String.Format("{0:00}:
            {1:00}:{2:00}.{3:00}",
         ts.Hours, ts.Minutes, ts.Seconds,
         ts.Milliseconds / 10);
         Console.WriteLine("RunTime: " + elapsedTime);

      }

      stopWatch.Stop();
   }

If you run it now, you should see the following:

Cache2
Figure 2: Our program now gets 10 items of data with approximately 5 seconds between each run.

The data I'm returning is only randomly generated, and if you really did have data that absolutely had to be different on each call, caching is not a strategy you want to employ. However, if your data was fairly static, and changed little, then the case for caching, even in a desktop program, is a valid one.

Use your project's 'Add Reference' right-click menu, to add the

System.Runtime.Caching

namespace to your project.

Cache3
Figure 3: Adding the Caching namespace

With this assembly referenced, update your FakeData class, so that it looks like the following:

using System;
using System.Linq;
using System.Runtime.Caching;
using System.Threading;

namespace datacache
{
   public class FakeData
   {
      ObjectCache _cache = MemoryCache.Default;

      private string GenerateRandomString()
      {
         var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
         var random = new Random();
         var result = new string(
            Enumerable.Repeat(chars, 20)
               .Select(s => s[random.Next(s.Length)])
               .ToArray());
         return result;
      }

      public string GetData()
      {
         string data = (string) _cache.Get("FakeData");
         if(data != null)
         {
            return data;
         }

         CacheItemPolicy policy = new CacheItemPolicy();
         policy.AbsoluteExpiration =
            DateTimeOffset.Now.AddSeconds(10);

         Thread.Sleep(5000);
         string result = GenerateRandomString();

         _cache.Add("FakeData", result, policy);

         return result;
      }

   }
}

The changes we've made to fake data now use the new .NET 4 runtime memory cache, and instead of always going off and taking 5 seconds to re-generate the data each time, every call is now cached for 10 seconds.

If you run your program now, you'll see that it waits only 5 seconds for the first invocation, but every call after that is returned instantaneously:

Cache4
Figure 4: Our program doesn't hang around once Caching is added to it.

You'll notice straight away that it's the initial call that's cached, and subsequent calls will return that object. If you add an artificial delay inside the for loop by using something like a 'Thread.Sleep' call, you'll see that after 10 seconds, your 'GetData' call will once again generate a new string and then return that as the cached copy.

Found a new API, or just want to know if there's an "API for that?" Come hunt me down on Twitter as @shawty_ds or on Linked-in in the Lidnug .NET users group I help run and ask me about it. I'll quite likely do a future post on the subject.



Related Articles

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date