Object pooling, part 8 – monitoring the pool’s performance

This is the eighth article in my mini series about object pooling. Be sure to read part 1, part 2, part 3, part 4, part 5, part 6 and part 7 first.

So, although the number of downloads of the sample program hasn’t been exactly great, I’m finally continuing the series. As I said in a previous article, I’d like to factor out the resizing behaviour – but before I do that, I want to integrate functionality that’ll let me evaluate the pool’s performance with regard to resizing, so I can assess the different resizing approaches reliably.

Fortunately performance monitoring is something that needs to be integrated into the pool implementation anyway and Windows has all the tools on board. So I only need to configure a few performance counters in Windows, make sure that these counters are updated with current numbers and the Windows performance monitoring frontend is available to the user to show it all in a nice graph. (I’m sure some of you have never heard of the performance monitor application – it’s available on the Administrative tools menu and it’s called, not surprisingly, Performance.)

Here’s the download for the current version, including these changes: ObjectPooling-2.zip

And these are the changes – at the end of the pool class:

private string performanceCategoryName;
/// <summary>
/// Gets or sets a value indicating the name of the performance counter category.
/// </summary>
public string PerformanceCategoryName {
  get {
    return performanceCategoryName;
  }
  set {
    if (performanceCategoryName != value) {
      performanceCategoryName = value;
      InvalidateCounters( );
    }
  }
}

private string performanceCounterNamePrefix;
/// <summary>
/// Gets or sets a value indicating a prefix that is used for all the performance 
/// counters of this pool instance.
/// </summary>
public string PerformanceCounterNamePrefix {
  get {
    return performanceCounterNamePrefix;
  }
  set {
    if (performanceCounterNamePrefix != value) {
      performanceCounterNamePrefix = value;
      InvalidateCounters( );
    }
  }
}

/// <summary>
/// Set the properties PerformanceCategoryName and PerformanceCounterNamePrefix to the values
/// passed in to the categoryName and counterNamePrefix parameters, then calls InitializeCounters().
/// </summary>
public void InitializeCounters(string categoryName, string counterNamePrefix) {
  performanceCategoryName = categoryName;
  performanceCounterNamePrefix = counterNamePrefix;
  InitializeCounters( );
}

private const string STR_Poolsize = "Pool size";
private const string STR_PoolsizeHelp = "The current number of poolable objects.";
private const string STR_ObjectsInUse = "Objects in use";
private const string STR_ObjectsInUseHelp = 
  "The number of objects that are currently allocated to a pool user.";
    
PerformanceCounter poolSizeCounter;
PerformanceCounter inUseCounter;

/// <summary>
/// Initializes the performance counters according to the settings of the PerformanceCategoryName 
/// and PerformanceCounterNamePrefix properties.
/// </summary>
public void InitializeCounters( ) {
  if (PerformanceCounterCategory.Exists(performanceCategoryName))
    PerformanceCounterCategory.Delete(performanceCategoryName);

  CounterCreationDataCollection counters = new CounterCreationDataCollection( );

  counters.Add(new CounterCreationData(performanceCounterNamePrefix + STR_Poolsize, 
    STR_PoolsizeHelp, PerformanceCounterType.NumberOfItems32));
  counters.Add(new CounterCreationData(performanceCounterNamePrefix + STR_ObjectsInUse, 
    STR_ObjectsInUseHelp, PerformanceCounterType.NumberOfItems32));

  PerformanceCounterCategory.Create(performanceCategoryName, "", 
    PerformanceCounterCategoryType.SingleInstance, counters);

  poolSizeCounter = new PerformanceCounter(performanceCategoryName, 
    performanceCounterNamePrefix + STR_Poolsize, false);
  inUseCounter = new PerformanceCounter(performanceCategoryName, 
    performanceCounterNamePrefix + STR_ObjectsInUse, false);
}

void InvalidateCounter(ref PerformanceCounter counter) {
  if (counter != null) {
    counter.Dispose( );
    counter = null;
  }
}

void InvalidateCounters( ) {
  InvalidateCounter(ref poolSizeCounter);
  InvalidateCounter(ref inUseCounter);
}

In the ExtendPoolBy method:

  . . .
  lock (poolLock) {
    count = Math.Min(maxPoolSize - pool.Count, count);

    for (int i = 0; i < count; i++) {
      Slot slot = delayObjectCreation ? new Slot(objectFactory) :
        new Slot(objectFactory.CreateObject( ));
      pool.Add(slot);
    }

    if (poolSizeCounter != null)
      poolSizeCounter.RawValue = pool.Count;
  }
  . . .

In the ShrinkPoolBy method:

  . . .
  if (poolSizeCounter != null)
    poolSizeCounter.RawValue = pool.Count;
  . . .

… and similar code for the other counter in the GetObject and ReleaseObject methods.

4 Comments on Object pooling, part 8 – monitoring the pool’s performance

  1. Can you provide a sample or partial sample code about how to use this code in ASP.NET environment. Right now, most of the applications are web applications.Thanks a lot.

    Like

  2. Did anybody actually use Object Pooling in their applications? What is the application performance impact after using Object Pooling?Thanks.

    Like

  3. Hi Bruce – what do you mean "most of the applications are web applications"? What applications? In any case, my code is of course "just" a sample of how to implement object pooling, not an out of the box product for all scenarios.As to the ASP.NET environment, I don’t exactly understand what you mean with your question. An object pool can be used in all situations where you want to have a number of objects available for shared (?) use. To use it, you’ll always have to set it up at some point, then make it available to callers. Maybe if you could give an example of the kind of objects that you’re trying to make available from your ASP.NET application, that might help me understand where you see the problem in that case.Finally, I don’t know if anybody ever went and used my source code in their applications. I’m sure that object pooling as a technical approach is used in real world applications all the time. It’s a common technique that is used by many server systems. ADO.NET supports it, as one example, to allow you to get a database connection from a pool instead of creating it all the time. Web servers normally work that way, having a bunch of response threads running in a pool to avoid having to start one up when a request comes in.The idea with object pooling is always that it is somehow a better idea to have an object readily available for a certain task, instead of having to create it and set it up when the task comes along. Now, creating an object is not really something that takes any significant time – it’s the setting up step that can take more time than we would like (opening network connections, database connections, reading metadata, whatever). In the end your application is supposed to work more performantly with object pooling in place. If it doesn’t, it’s not the fault of the object pooling approach, but rather of you using that approach in a situation where it’s not appropriate or useful.

    Like

  4. Hey Oliver,First let me thank you for a through document that is really helpful. I need to implement object pooling and will definitely do some serious copy/paste :)I do have a question.My poolable object is using some "unmanaged" resources such as open files, connections and other external resources. In a normal case I just call MyObject.Dispose() method to make sure all these resources are cleared. I an thinking that if i would use your object pooling this method will never get called. I was thinking of added another method to IObjectFactory, that looks like that:public interface IObjectFactory<T> { T CreateObject( ); void DisposeObject(T objectToDispose);}My object factory’s DisposeObject will call the object’s Dispose method.Then in ShrinkPoolBy i’ll call objectFactory.DisposeObject(unusedSlot.PoolObject) just before removing it from the list.My problem is that if the unusedSlot is empty it will create the object. which is silly.I was wondering you might suggest what is the bast way to handle such issue.Thanks

    Like

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s