Object pooling, part 7 – the first test program

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  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));
    }
  }

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