Please click this logo to help me get on their beta program:

Xobni outlook add-in for your inbox








25/9/2005

Object pooling, part 7 - the first test program

Filed under: General, Programming, .NET — Oliver Sturm @ 3:31 pm - 2 years, 10 months ago

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

As some of you may have noticed, I introduced a bad bug in the last article about growing and shrinking, specifically in the code that would call the ExtendPoolBy and ShrinkPoolBy methods. The amount by which to grow or shrink the pool was calculated as the difference between two percentage figures and passed in to the two methods, which really expected absolute values! That’s what I get for not using test-first development on this :-)

So I fixed this and introduced two simple algorithms instead to handle the calculation of the growing and shrinking amounts. This is quite an important topic for object pooling, because it defines how well the pool scales in various scenarios - sometimes it may be desirable to have a pool that adapts quickly to new requirements, sometimes it’s more important to keep the pool size stable as long as possible. In one of the next posts in the series I’m going to factor out the growing/shrinking behaviour using the strategy pattern, until then the current calculation will have to do - and the new test program shows that it doesn’t do that bad at all!

The test program

Here’s the download of the current code and the sample program. This has been created in Visual Studio 2005 beta 2.

As promised, I have created a test program that uses a few threads to do work with the pool. Each thread runs a loop in which a number of objects is requested from the pool and released after a while. A random values determines if only a single object or a larger number of objects will be requested in each run. Here’s the code:

  class Program : IObjectFactory<PoolableObject> {
    static void Main(string[] args) {
      new Program( ).Run( );
    }
	
    Pool<PoolableObject> pool;
    private const int threadCount = 3;
	
    void Run( ) {
      pool = new Pool<PoolableObject>(this, 0);
      pool.UseTimerBasedResizing = true;
      pool.MaxPoolSize = 100;
	
      Thread[] threads = new Thread[threadCount];
      for (int i = 0; i < threadCount; i++) {
        threads[i] = new Thread(new ThreadStart(ThreadMethod));
        threads[i].Start( );
      }
	
      while (true) {
        Console.WriteLine("Current pool size: " + pool.Size);
        Thread.Sleep(2000);
      }
    }
	
    void ThreadMethod( ) {
      Random random = new Random( );
      while (true) {
        if (random.Next(0, 100) > 50) {
          // boost
          int boostSize = random.Next(5, 20);
          PoolableObject[] objects = new PoolableObject[boostSize];
          for (int i = 0; i < boostSize; i++) {
            objects[i] = pool.GetObject( );
            Console.WriteLine(String.Format("Thread {0} allocated object {1}.",
              Thread.CurrentThread.ManagedThreadId, objects[i].Id));
            objects[i].DoWork( );
          }
          Thread.Sleep(random.Next(0, 5000));
          for (int i = 0; i < boostSize; i++) {
            pool.ReleaseObject(objects[i]);
            Console.WriteLine(String.Format("Thread {0} released object {1}.",
              Thread.CurrentThread.ManagedThreadId, objects[i].Id));
          }
        }
        else {
          // single
          PoolableObject poolableObject = pool.GetObject( );
          Console.WriteLine(String.Format("Thread {0} allocated object {1}.",
            Thread.CurrentThread.ManagedThreadId, poolableObject.Id));
          poolableObject.DoWork( );
          Thread.Sleep(random.Next(0, 5000));
          pool.ReleaseObject(poolableObject);
          Console.WriteLine(String.Format("Thread {0} released object {1}.",
            Thread.CurrentThread.ManagedThreadId, poolableObject.Id));
        }
      }
    }
	
    #region IObjectFactory<PoolableObject> Members
    PoolableObject IObjectFactory<PoolableObject>.CreateObject( ) {
        return new PoolableObject( );
    }
    #endregion
  }
	
  public class PoolableObject {
    public PoolableObject( ) {
      id = Guid.NewGuid( );
      Console.WriteLine(String.Format("Poolable object {0} is being constructed.", id));
    }
	
    ~PoolableObject( ) {
      Console.WriteLine(String.Format("Poolable object {0} is being destructed.", id));
    }
	
    private Guid id;
    public Guid Id {
      get {
        return id;
      }
    }
	
    int useCount;
	
    public void DoWork( ) {
      // Obviously this object should be able to actually do something, but
      // that's not relevant to the test program.
      // So we just count how many times an object has been used.
      Console.WriteLine(String.Format("Poolable object {0} has been used {1} times now.", id, ++useCount));
    }
  }

22/9/2005

Object pooling, part 6 - growing and shrinking

Filed under: General, Programming, .NET — Oliver Sturm @ 10:16 am - 2 years, 10 months ago

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

It took me a while to find the time for the next article, but here it is. Now we’re finally going to deal with the topic of growing and shrinking the pool.

One of the first questions we have to ask when it comes to this is, when are we going to do it? We have one mechanism for growing the pool implemented already, which takes place when, during a call to the GetObject method, no unused object can be found in the pool. But is this the right place to think about growing, really? And what about shrinking?

I think it’s a good idea to keep these things out of the way of “normal call flow”. Meaning, if possible, a client’s call to get an object from the pool shouldn’t be delayed by management work. So I have implemented the mechanism with the help of a timer: a regular check is executed to find whether the number of used objects in the pool is too high or too low and growing or shrinking is done accordingly.

To classify the number of used objects as “too high” or “too low”, I’ve introduced two properties “HighWaterMark” and “LowWaterMark”. These are percentage values: if the percentage of used objects in the pool is higher than HighWaterMark, the pool is considered too small and vice versa. To prevent the pool size from being scaled up and down wildly in certain scenarios, I have also introduced threshold values - so the percentage has to be found to be too high at more than one check in a row, for example, for the pool to be grown. Actually, the threshold is more useful in the shrinking than the growing case, but it’s the same principle.

So, here’s the code for these changes. The next article will introduce a sample program to test all the functionality that’s implemented so far and I’ll post the source code for the pool and the sample with it. So if you haven’t been typing in all this code yourself, stay tuned for the next installment in the series!

class Pool<T> {
  . . .
	
  private Pool( ) {
    pool = new List<Slot>( );
    retryWaitTime = 500;
    poolExtensionBatchSize = 10;
    poolExtensionBatchSizeIsPercent = true;
    maxGetObjectTries = 5;
    outOfObjectsBehaviour = OutOfObjectsBehaviour.ExtendPool;
    delayObjectCreation = false;
    maxPoolSize = 100;
    lowWaterMark = 20;
    highWaterMark = 80;
    resizingWaitTime = 2000;
    growThreshold = 0;
    shrinkThreshold = 3;
  }
	
  Timer resizingTimer;
  object resizingTimerLock = new object( );
	
  private bool useTimerBasedResizing;
  /// 
  /// Gets or sets a value indicating whether the pool checks for low and high
  /// water situations automatically and regularly. Also see ResizingWaitTime.
  /// 
  public bool UseTimerBasedResizing {
    get {
      return useTimerBasedResizing;
    }
    set {
      if (useTimerBasedResizing != value) {
        useTimerBasedResizing = value;
        lock (resizingTimerLock) {
          if (useTimerBasedResizing == false && resizingTimer != null)
            resizingTimer.Dispose( );
          else if (useTimerBasedResizing == true)
            resizingTimer = new Timer(new TimerCallback(ResizingTimerCallback),
              null, resizingWaitTime, Timeout.Infinite);
        }
      }
    }
  }
	
  private int resizingWaitTime;
  /// 
  /// Gets or sets a value that indicates the time between to checks for low and
  /// high water situations. Also see UseTimerBasedResizing.
  /// 
  public int ResizingWaitTime {
    get {
      return resizingWaitTime;
    }
    set {
      if (resizingWaitTime != value) {
        resizingWaitTime = value;
        lock (resizingTimerLock) {
          if (resizingTimer != null)
            resizingTimer.Change(resizingWaitTime, Timeout.Infinite);
        }
      }
    }
  }
	
  private int highWaterMark;
  /// 
  /// Gets or sets a value indicating the percentage of used objects in the pool
  /// that must be exceeded for the pool to be grown automatically.
  /// 
  public int HighWaterMark {
    get {
      return highWaterMark;
    }
    set {
      if (highWaterMark != value) {
        highWaterMark = value;
      }
    }
  }
	
  private int lowWaterMark;
  /// 
  /// Gets or sets a value that the percentage of used objects in the pool
  /// must fall below for the pool to be shrunken automatically.
  /// 
  public int LowWaterMark {
    get {
      return lowWaterMark;
    }
    set {
      if (lowWaterMark != value) {
        lowWaterMark = value;
      }
    }
  }
	
  private int shrinkThreshold;
  /// 
  /// Gets or sets a value indicating the number of times the pool must
  /// be judged too big before it is shrunken.
  /// 
  public int ShrinkThreshold {
    get {
      return shrinkThreshold;
    }
    set {
      if (shrinkThreshold != value) {
        shrinkThreshold = value;
      }
    }
  }
	
  private int growThreshold;
  /// 
  /// Gets or sets a value indicating the number of times the pool must
  /// be judged too small before it is grown.
  /// 
  public int GrowThreshold {
    get {
      return growThreshold;
    }
    set {
      if (growThreshold != value) {
        growThreshold = value;
      }
    }
  }
	
  Slot FindUnusedSlot( ) {
    return pool.Find(delegate(Slot slot) {
      return !slot.InUse;
    });
  }
	
  /// 
  /// Shrinks the pool size by count elements, at most. The method removes
  /// only unused pool objects, as long as it finds them.
  /// 
  public void ShrinkPoolBy(int count) {
    if (count < = 0)
      throw new ArgumentOutOfRangeException("Count must be greater than zero.");
    lock (poolLock) {
      count = Math.Max(pool.Count - count, 0);
	
      Slot unusedSlot;
      do {
        unusedSlot = FindUnusedSlot( );
        if (unusedSlot != null)
          pool.Remove(unusedSlot);
        count--;
      } while (count > 0 && unusedSlot != null);
    }
  }
	
  /// 
  /// Returns the number of used objects in the pool.
  /// 
  public int GetInUseCount( ) {
    int count = 0;
    pool.ForEach(delegate(Slot slot) {
      if (slot.InUse)
      count++;
    });
    return count;
  }
	
  /// 
  /// Returns the percentage of used objects in the pool.
  /// 
  public int GetFillLevel( ) {
    return GetInUseCount( ) / pool.Count * 100;
  }
	
  int fillLevelFoundTooLow;
  int fillLevelFoundTooHigh;
	
  void ResizingTimerCallback(object state) {
    int fillLevel = GetFillLevel( );
	
    if (fillLevel < lowWaterMark) {
      fillLevelFoundTooLow++;
      fillLevelFoundTooHigh = 0;
    }
    else if (fillLevel > highWaterMark) {
      fillLevelFoundTooHigh++;
      fillLevelFoundTooLow = 0;
    }
    else
      fillLevelFoundTooHigh = fillLevelFoundTooLow = 0;
	
    if (fillLevelFoundTooLow > shrinkThreshold) {
      ShrinkPoolBy(lowWaterMark - fillLevel);
      fillLevelFoundTooLow = 0;
    }
    else if (fillLevelFoundTooHigh > growThreshold) {
      ExtendPoolBy(fillLevel - highWaterMark);
      fillLevelFoundTooHigh = 0;
    }
	
    lock (resizingTimerLock)
      if (resizingTimer != null)
        resizingTimer.Change(resizingWaitTime, Timeout.Infinite);
  }
	
  . . .
}

14/9/2005

LINQ resources

Filed under: General, Programming, .NET — Oliver Sturm @ 4:41 pm - 2 years, 10 months ago

Just to pass on another piece of news from PDC, here’s a list of information resources about LINQ, which is a fantastic extension to common programming models that we have today. Problem is, it seems to be quite a while away :-)

New Office 12 gui

Filed under: General — Oliver Sturm @ 4:38 pm - 2 years, 10 months ago

Among the news coming from PDC are reports from the first public presentation of Office 12. Lots of gui changes is the most obvious thing to know about that. They replaced the menu bar with the ribbon… hm :-) Go have a look!

Project challenge expo in London

Filed under: General — Oliver Sturm @ 10:32 am - 2 years, 10 months ago

It’s next week and I’m going to be there. Registration is free, so if you’re at all interested in project management or anything that has remotely to do with it, be sure to check out the website and consider coming!

No time for text editor tests

Filed under: General, Best text editor — Oliver Sturm @ 10:14 am - 2 years, 10 months ago

I just thought it might be a good idea to write a post for my text editor category again - the problem is, I don’t currently have the time to do more tests. I haven’t lost interest in this, so rest assured there will be more tests as soon as my time allows it!

Object pooling, part 5

Filed under: General, Programming, .NET — Oliver Sturm @ 10:12 am - 2 years, 10 months ago

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

This part is going to make some modifications to the code in the GetObject() method to implement various alternative behaviours there. Think about it: what do you want to happen if there’s no free object to be had in the pool? I came up with the following possible options:

  1. Return null. In this case, the caller would have to deal with the problem further if it can’t get a pool object.
  2. Throw an exception. Really just a variant of (1), because it also pushes the problem out to the caller.
  3. Try again. Because objects are being used and returned to the pool all the time, it might make sense just to wait a bit and try to allocate an object one more time… or two more times, even. A maximum number of tries would be good, I guess.
  4. Extend the pool. The general growing/shrinking discussion hasn’t taken place yet in these articles, but possibly the case where no more objects are available would be a good place to grow the pool.

Consider this changed code for the GetObject() method:

public T GetObject( ) {
  int tryNo = 1;
	
  while(tryNo <= maxGetObjectTries) {
    lock (poolLock) {
      Slot unusedSlot = pool.Find(delegate(Slot slot) {
        return !slot.InUse;
      });
      if (unusedSlot != null) {
        unusedSlot.Occupy( );
        return unusedSlot.PoolObject;
      }
    }
	
    switch (outOfObjectsBehaviour) {
      case OutOfObjectsBehaviour.ReturnNull:
        return default(T);
      case OutOfObjectsBehaviour.ThrowException:
        throw new OutOfObjectsException( );
      case OutOfObjectsBehaviour.WaitAndRetry:
        Thread.Sleep(retryWaitTime);
        break;
      case OutOfObjectsBehaviour.ExtendPool:
        ExtendPool( );
        break;
    }
  }
	
  throw new OutOfObjectsException( );
}

As you can see, the whole getting-an-object algorithms is potentially repeated multiple times now, possibly sleeping or trying pool extension in between. Finally, when a specific maximum number of retries has run out, an exception is thrown. I’ve specifically decided to do this instead of returning null - I’m assuming there might be no special code to handle null returns on the caller side, otherwise the pool could have been configured to return null in the first place.

The next interesting thing is the ExtendPool() method, which looks like this:

  /// 
  /// Extends the pool by a number of objects that's specified by the PoolExtensionBatchSize
  /// and the PoolExtensionBatchSizeIsPercent properties.
  /// 
  void ExtendPool( ) {
    lock (poolLock) {
      int newObjects = poolExtensionBatchSizeIsPercent ?
        pool.Count * poolExtensionBatchSize / 100 : poolExtensionBatchSize;
      ExtendPoolBy(newObjects);
    }
  }

Several properties have been added to the pool to support the new functionality and there’s also an enum for the out of objects behaviour. If you’ve been following the articles closely, you may have noticed that the maxPoolSize had already creapt in to the ExtendPoolBy() method last time, although I hadn’t introduced it yet :-) Here it is now:

class Pool {
  . . .
  private int maxPoolSize;
  /// 
  /// Gets or sets a value indicating the maximum number of objects that the pool
  /// shall hold. The ExtendPoolBy() method won't extend the pool to a larger
  /// size than this.
  /// 
  public int MaxPoolSize {
    get {
      return maxPoolSize;
    }
    set {
      if (maxPoolSize != value) {
        maxPoolSize = value;
        lock (poolLock) {
          // This might be replaced by a call to the proper shrinking
          // method later.
          if (pool.Count > maxPoolSize)
            pool.RemoveRange(maxPoolSize, pool.Count - maxPoolSize);
        }
      }
    }
  }
	
  private int maxGetObjectTries;
  /// 
  /// Gets or sets a value that defines the maximum tries that are made
  /// to acquire an object from the pool in the GetObject() method.
  /// 
  public int MaxGetObjectTries {
    get {
      return maxGetObjectTries;
    }
    set {
      if (maxGetObjectTries != value) {
        maxGetObjectTries = value;
      }
    }
  }
	
  private int poolExtensionBatchSize;
  /// 
  /// Gets or sets a value that defines the size of the batch when the pool is
  ///  growing. This can be an absolute value or a percentage, which is
  /// defined by the PoolExtensionBatchSizeIsPercent property.
  /// 
  public int PoolExtensionBatchSize {
    get {
      return poolExtensionBatchSize;
    }
    set {
      if (poolExtensionBatchSize != value) {
        poolExtensionBatchSize = value;
      }
    }
  }
	
  private bool poolExtensionBatchSizeIsPercent;
  /// 
  /// Gets or sets a flag that indicates whether the value given by
  /// the PoolExtensionBatchSize property is an absolute value or a percentage.
  /// 
  public bool PoolExtensionBatchSizeIsPercent {
    get {
      return poolExtensionBatchSizeIsPercent;
    }
    set {
      if (poolExtensionBatchSizeIsPercent != value) {
        poolExtensionBatchSizeIsPercent = value;
      }
    }
  }
	
  private int retryWaitTime;
  /// 
  /// Gets or sets a value that defines the time to wait between tries
  /// to acquire a pool object in the GetObject() method. This is only
  /// evaluated for OutOfObjectsBehaviour == WaitAndRetry.
  /// 
  public int RetryWaitTime {
    get {
      return retryWaitTime;
    }
    set {
      if (retryWaitTime != value) {
        retryWaitTime = value;
      }
    }
  }
	
  private OutOfObjectsBehaviour outOfObjectsBehaviour;
  /// 
  /// Gets or sets a value indicating the behaviour that the GetObject()
  /// method shows when a pool object isn't (immediately) available.
  /// 
  public OutOfObjectsBehaviour OutOfObjectsBehaviour {
    get {
      return outOfObjectsBehaviour;
    }
    set {
      if (outOfObjectsBehaviour != value) {
        outOfObjectsBehaviour = value;
      }
    }
  }
  . . .
}
	
/// 
/// Defines the possible behaviours that the GetObject()
/// method can shows when a pool object isn't (immediately) available.
/// 
public enum OutOfObjectsBehaviour {
  /// 
  /// Return null or the default value of the type.
  /// 
  ReturnNull,
  /// 
  /// Wait for a time specified by the Pool.RetryWaitTime property and try again.
  /// A maximum number of tries is specified by the Pool.MaxGetObjectTries
  /// property.
  /// 
  WaitAndRetry,
  /// 
  /// Throw an OutOfObjectsException.
  /// 
  ThrowException,
  /// 
  /// Extend the pool by a default batch as defined by the
  /// Pool.PoolExtensionBatchSize and Pool.PoolExtensionBatchSizeIsPercent
  /// properties, then try again. A maximum number of tries is specified by
  /// the Pool.MaxGetObjectTries property.
  /// 
  ExtendPool
}

Sorry I still didn’t make it to the growing/shrinking, at least not completely. Well, next time!

12/9/2005

Object pooling, part 4

Filed under: General, Programming, .NET — Oliver Sturm @ 11:13 am - 2 years, 11 months ago

This is the fourth article in my mini series about object pooling. Be sure to read part 1, part 2 and part 3 first.

This part will be about automated object creation. To begin with, we need a way for the object pool to have additional objects created at all. One way to do that would be to just call new on the object type - and to use the new constraint on the generic class definition, of course. But that’s an unnecessary restriction, and it would imply that poolable classes always need to have a default constructor. So I decided to go a different way and just define an interface that gets passed in to the Pool class and that can be used when new objects need to be created. This is it:

public interface IObjectFactory<T> {
  T CreateObject( );
}

Now when will the Pool actually have the necessity of creating new objects? This can happen in at least two situations:

  1. When there are not enough free objects left in the pool. I’ll see later how this decision is reached.
  2. When the Pool instance is created and there are no objects in the pool at all.

An additional measure of flexibility that I’d like to include is to make it an option to defer the creation of a poolable object to the point where the object is actually being accessed via the Slot. For pooling situations where the pool size may grow or shrink very rapidly, this could be helpful to distribute load over time, instead of introducing great load peaks when the pool needs to grow.

Here are the changes I made to the classes:

class Slot {
  . . .
  /// 
  /// Constructs a Slot object without a pre-existing pooled type object.
  /// Creation of the pooled type object will be deferred to the point
  /// where the object is actually accessed via the PoolObject property.
  /// 
  public Slot(IObjectFactory<T> objectFactory) {
    if (objectFactory == null)
      throw new ArgumentNullException("objectFactory");
    this.objectFactory = objectFactory;
  }
	
  IObjectFactory<T> objectFactory;
  T poolObject;
	
  /// 
  /// Returns the pooled object itself. If there isn't any object yet,
  /// it'll be created on the fly using the object factory.
  /// 
  public T PoolObject {
    get {
      if (poolObject == null)
        poolObject = objectFactory.CreateObject( );
      return poolObject;
    }
  }
  . . .
}
	
class Pool {
  . . .
  /// 
  /// Creates a new Pool object. The list of objects in the pool is
  /// expanded by a number of additional slots, as given by the
  /// initialPoolSize parameter. Objects are created by a call to the
  /// IObjectFactory<T>.CreateObject() method.
  /// 
  public Pool(IObjectFactory<T> objectFactory, int initialPoolSize) : this( ) {
    this.objectFactory = objectFactory;
    if (initialPoolSize > 0)
      ExtendPoolBy(initialPoolSize);
  }
	
  private IObjectFactory<T> objectFactory;
  /// 
  /// Gets or sets a value defining the object factory that can be used
  /// by the pool to create new poolable objects as necessary.
  /// 
  public IObjectFactory<T> ObjectFactory {
    get {
      return objectFactory;
    }
    set {
      if (objectFactory != value) {
        objectFactory = value;
      }
    }
  }
	
  private bool delayObjectCreation;
  /// 
  /// Gets or sets a flag that defines whether a poolable type object is
  /// immediately created together with a new Slot, or whether the
  /// creation of the poolable type object will be delayed.
  /// 
  public bool DelayObjectCreation {
    get {
      return delayObjectCreation;
    }
    set {
      if (delayObjectCreation != value) {
        delayObjectCreation = value;
      }
    }
  }
	
  /// 
  /// Extends the number of slots in the pool by the number given in
  /// the count parameter.
  /// 
  public void ExtendPoolBy(int count) {
    if (objectFactory == null)
      throw new InvalidOperationException("An ObjectFactory must be configured.");
    if (count <= 0)
      throw new ArgumentOutOfRangeException("Count must be greater than zero.");
    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);
      }
    }
  }
  . . .
}

That’ll be it for now. The next thing I’ll look at is the right place to grow and shrink the pool size.

11/9/2005

Object pooling, part 3

Filed under: General, Programming, .NET — Oliver Sturm @ 12:52 pm - 2 years, 11 months ago

This is the third article in my mini series about object pooling. Be sure to read part 1 and part 2 first.

In this part I’m going to put some of the infrastructure in place that’s needed to handle the list of pooled objects, to make objects available and to allow for them to be put back into the pool.

Initializing the pooled object list

As I don’t want to look at automated object creation just yet, I’ll create a constructor for my pool that allows for a list of pooled objects to be passed in from the outside. The Slot class also needs a constructor that allows me to easily create a Slot for an existing object. These two constructors look like this:

class Slot {
  . . .
  /// 
  /// Constructs a Slot object for a pre-existing pooled type object.
  /// 
  public Slot(T poolObject) {
    if (poolObject == null)
      throw new ArgumentNullException("poolObject");
    this.poolObject = poolObject;
  }
  . . .
}
	
class Pool {
  . . .
  private Pool( ) {
    pool = new List<Slot>( );
  }
	
  /// 
  /// Constructs a pool and fills its list of pooled objects with the objects
  /// passed in to this constructor.
  /// 
  public Pool(IEnumerable<T> objects) : this( ) {
    foreach (T entry in objects)
      pool.Add(new Slot(entry));
  }
  . . .
}

As you can see, I’ve added a private constructor to the Pool class that has the purpose of initializing internal fields - I don’t intend to allow the Pool to be created with no parameters at all, so I made that one private.

Getting and releasing objects

I filled the GetObject() and ReleaseObject() methods with some code that will allow for objects to be allocated and released. To make the Pool safe to use from multiple threads, I’m using a lock object. I also introduced a few other helper methods; everything’s here in the code sample:

class Pool {
  . . .
  object poolLock = new object( );
	
  /// 
  /// Returns a pooled object. After use, the pooled object must be returned to the
  /// pool by calling the ReleaseObject method.
  /// 
  public T GetObject( ) {
    lock (poolLock) {
      Slot unusedSlot = pool.Find(delegate(Slot slot) {
        return !slot.InUse;
      });
      if (unusedSlot != null) {
        unusedSlot.Occupy( );
        return unusedSlot.PoolObject;
      }
    }
    throw new OutOfObjectsException( );
  }
	
  /// 
  /// Releases an object that was previously fetched from the pool
  /// by a call to the GetObject method.
  /// 
  public void ReleaseObject(T poolObject) {
    lock (poolLock) {
      Slot objectSlot = pool.Find(delegate(Slot slot) {
        return EqualityComparer<T>.Default.Equals(slot.Object, poolObject);
      });
      if (objectSlot != null)
        objectSlot.Release( );
      else
        throw new ArgumentException("The given object is not part of this pool.");
    }
  }
  . . .
}
	
class Slot {
  . . .
  /// 
  /// Marks the slot as being in use.
  /// 
  public void Occupy( ) {
    inUse = true;
  }
	
  /// 
  /// Marks the slot as being available.
  /// 
  public void Release( ) {
    inUse = false;
  }
  . . .
}
	
[Serializable]
public class OutOfObjectsException : Exception {
  public OutOfObjectsException( ) { }
  public OutOfObjectsException(string message) : base(message) { }
  public OutOfObjectsException(string message, Exception innerException) : base(message, innerException) { }
  protected OutOfObjectsException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
Next steps

While the current status is complete in itself and could actually be used to borrow and return objects from the pool, the functionality is severely restricted at this point. The pool doesn’t do enough management work itself, but rather leaves object construction to the outside world. So in the next part I’ll show how the system can be extended to do its own object creation, which will make the pool much better scalable.

9/9/2005

DDD Day 2 geek dinner

Filed under: General, Programming, .NET — Oliver Sturm @ 3:30 pm - 2 years, 11 months ago

I’ll be at DeveloperDeveloperDeveloper Day 2 and also at the geek dinner that’ll hopefully take place afterwards. If you can make it to Reading on the 22nd of October, make sure you sign up!

Session voting is now open and there’s a Taxi/Car sharing wiki available for those who don’t know how to get there or want company on the way.

Object pooling, part 2

Filed under: General, Programming, .NET — Oliver Sturm @ 9:55 am - 2 years, 11 months ago

This is the second article in my mini series about object pooling. You can find the first part here.

Now I want to get going by defining an interface for the pool - I don’t mean a C# interface, but rather a framework for the methods and properties that the implemented class is going to have.

I’ve also given some thought to the internal organization. One thing’s clear: the pool needs to have a list of objects that it currently holds. This list will have to be extendable, but there aren’t any other requirements, so a simple List<T> should do just fine. A more interesting question is, what exactly does the list store? It could just be instances of the object type we are pooling, but isn’t there management information that has to be stored as well? Like which objects are in use and which aren’t, to start with?

The Slot

So, my decision is that the list will store instances of another type we need, which is what I call a Slot. A Slot is an encapsulation of the actual type of the pooled object, which adds all the internal per-object management information that the pool needs to do its work. For now, there’s just the object itself and a flag that says whether the object is currently in use or not.

I have decided to make the Slot class a nested class, inside the Pool class. The Slot is really just a management detail of the Pool, this is completely irrelevant to the user of the Pool. It also allows the Slot to share the type parameter with the Pool.

Here’s the code for the Pool and the Slot classes. I have added documentation comments, so it should be self-explanatory.

/// 
/// This is the main Pool class, which implements object pooling.
/// 
public sealed class Pool<T> {
  /// 
  /// The slot class represents an entry in the pool's list. It holds information about
  /// the actual pooled object, plus per-object management information.
  /// 
  sealed class Slot {
    T poolObject;
    /// 
    /// Returns the pooled object itself.
    /// 
    public T PoolObject {
      get {
        return poolObject;
      }
    }
	
    bool inUse;
    /// 
    /// Returns a value indicating whether this slot is currently in use or not, in other words
    /// whether its object has been reserved for use at this time.
    /// 
    public bool InUse {
      get {
        return inUse;
      }
    }
  }
	
  List<Slot> pool;
	
  /// 
  /// Returns a pooled object. After use, the pooled object must be returned to the
  /// pool by calling the ReleaseObject method.
  /// 
  public T GetObject( ) {
  }
	
  /// 
  /// Releases an object that was previously fetched from the pool
  /// by a call to the GetObject method.
  /// 
  public void ReleaseObject(T poolObject) {
  }
}
The source code

A word about the code I’m showing here: This article series is supposed to be about the design process of the whole pooling system. So I’m planning to show parts of the source code in the articles, such as I do here. The parts shown in each article won’t be useful to run on their own, of course - I assume the whole thing will soon be much too long to be posted in a blog post in its entirety. When the sample project comes to the point where it makes sense, I will provide a download with a sample program and the complete classes.

8/9/2005

Object pooling, part 1

Filed under: General, Programming, .NET — Oliver Sturm @ 11:09 am - 2 years, 11 months ago

The other day there was a thread in a newsgroup which ended up discussing object pooling. Jon pointed to Spring.NET - an application framework for enterprise .NET applications. I’m sure there are other implementations out there, at the moment, Spring.NET merely offers a few interfaces and a rather simplistic implementation of a pool. I thought it might be interesting to people to see how pooling functionality can be implemented, so I’m going to do that for this article, which is supposed to be a first in a small series.

What is an object pool?

I’m going to apply my own definition here, which is rather unassuming in nature: An object pool is a object that holds a list of other objects, ready to make them available for use (to yet another object, probably). It does the management work involved, like keeping track of which objects are currently in use, how many objects the pool holds, whether this number should be increased… Where do you use this? Most commonly it’s used in server systems where services are made available to a number of clients: by having a pre-created pool of objects, the server can quickly react to new requests without having to wait for a new object to be constructed; if the pool scales well, the system will adapt easily to different load levels.

Preparations

To start with, there’s got to be some conceptual thinking: what exactly is it that this object pool is supposed to do, which requirements does it have? So, here’s what I’m setting out to create:

  • The object pool should be reusable and type independent.
  • The pool must be thread safe.
  • It should be scaleable. The exact way in which the pool can grow and shrink should be configurable.
  • Work with objects from the pool should be safe. There’s got to be a mechanism to make sure that objects are in a stable state when returned to the pool.
  • Flexibility. I’d like the pool object to support various configurations configurably, instead of creating various pool classes for different detail approaches.

This doesn’t have to be it - I suppose I’ll make up additional features while I go along :-) . But these are the main guidelines for now.

Reusability, type independence

One initial question is this: Do we achieve type independence by letting the pool handle interfaces instead of concrete types, or do we use a generic class for even better type safety? In this case I think the generic class is a better choice, because using interfaces would make the single poolable object more complex. What’s more, we don’t really need anything from the object, so what methods should that interface contain? No, I think I’m going to make the pool a generic class, the generic parameter being the type of the pooled object.

Thread safety

I must assume that objects from the pool are going to be requested from more than one thread, so all management functions must be thread safe. This should be achieved quite easily, because the pool will have only one important central structure: the list of objects in the pool.

Scalability

A pool can have a static size, so scalability is not a problem. But it’s also possible that the pool has to grow when more objects are needed, and to shrink when the number of unused objects is too large. This is a complex topic because there are many different approaches. For example, do we let the pool grow when we find out that no objects are left, or do we try to find out about the need for more objects before we run out? If the latter, how? Also, when more objects have to be created, who does that? We’ll need a factory for that, because the pool itself doesn’t know how to create new objects of the pooled type and we don’t want to impose arbitrary restrictions such as the availability of a default constructor. Lots to think about here and a number of options.

Safety of pool objects

When an object that utilizes the pool gets an object out of it and starts work with it, it must not find out a moment later that old state information has been left in the object (or probably it won’t find out, the results that it gets from the object will simply be wrong). So, depending on the implementation of the pooled objects, it might be necessary to check the validity of the object state when an object is returned to the pool.

Flexibility

With regard to many of the topics discussed above, there may be more than one possible approach. I’d like the pool to implement several alternatives in those cases, the programmer using the pool should be able to switch those behaviours even at runtime. I think this makes more sense than to implement a complete hierarchy of different pool classes, each with various differences in detail implementations.

So, this is it for now, watch this space for part 2, coming soon! And please comment if you have additional ideas!

Commercial services available

Filed under: General, Programming, .NET — Oliver Sturm @ 9:15 am - 2 years, 11 months ago

Some things have been changing around here - I was always largely preoccupied with work for one specific customer of mine. This has now stopped and so I’m looking into other things to do.

To start with, I have put up a few informational pages at www.sturmnet.org - nothing too detailed yet, just on overview. So, if you have project work to be done, or consulting needs, please think of me, look up my resume and contact me!

6/9/2005

Processing

Filed under: General, Programming — Oliver Sturm @ 10:53 am - 2 years, 11 months ago

I just had a look at Processing, which is an environment, based on Java, that lets you create graphics output easily. I’m not remotely doing the thing justice here, better look at the project page for that, and be sure to view the samples!

The idea is great: an editor can be used to create a source code file, which is usually very simple and can be run on the fly, for example rendering some kind of graphics output in a window. When you think you have finished what you were working on, just hit Export and the system will create a Java applet for you, ready for redistribution with a jar file and an HTML page, that does exactly what you programmed in the integrated environment. Nice!

5/9/2005

About using PowerPoint…

Filed under: General — Oliver Sturm @ 9:12 am - 2 years, 11 months ago

No need to point out yet again that Jesper Johansson is finally blogging, but one of his first posts, the aptly named Death by PowerPoint, is definitely worth passing on. Read it, especially if you use PowerPoint occasionally!

Next Page »

Powered by WordPress
© Copyright 2005-2008 Oliver Sturm