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

objectpooling-performance.png

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.