Skip to content
ikopylov edited this page Apr 12, 2017 · 13 revisions

Qoollo Object Pool

Object Pool is a container of already initialized objects kept ready to use. A client of the pool will request an object from the pool and perform operations on the returned object. When the client has finished, it returns the object to the pool rather than destroying it.

The library contains 4 different types of object pools:

  1. Static object pool - a simple and fast container of elements, where user should manually add all required elements;
  2. Dynamic object pool - object pool, that can dynamically create and destroy elements according to current workload;
  3. Balancing static object pool - static object pool, where every element has priority, so the pool returns the best of currently available;
  4. Balancing dynamic object pool - dynamic object pool, where every element has priority, so the pool returns the best of currently available;

All implementations are thread safe and highly optimized for concurrent access.

Static object pool

Static object pool is implemented by class 'StaticPoolManager'. Here the sample of usage:

// Creation of the pool
var pool = new StaticPoolManager<SqlConnection>(name: "name");

// filling the pool
for (int i = 0; i < 10; i++)
{
    var con = new SqlConnection("connection string");
    con.Open();
    pool.AddElement(con);
}

// using the pool
using (var element = pool.Rent())
{
    var cmd = element.Element.CreateCommand();
    cmd.ExecuteNonQuery();
}

// remove invalid elements from pool
using (var element = pool.Rent())
{
    element.Element.Dispose();
    pool.RemoveElement(element);
}

// Dispose the pool (by default it call dispose on every element inside)
pool.Dispose();

Dynamic object pool

Dynamic object pool is implemented by class 'DynamicPoolManager'. Here the sample of usage:

// Create own subclass of DynamicPoolManager
public class SqlConnectionPool: DynamicPoolManager<SqlConnection>
{
    public SqlConnectionPool(int maxElementCount)
        : base(minElementCount: 0, maxElementCount: maxElementCount, name: "name")
    {
    }


    // Implement element creation logic
    protected override bool CreateElement(out SqlConnection elem, int timeout, CancellationToken token)
    {
        try
        {
            elem = new SqlConnection("connection string");
            elem.Open();
            return true;
        }
        catch { }

        elem = null;
        return false;
    }

    // Check is element valid (invalid elements are removed automatically)
    protected override bool IsValidElement(SqlConnection elem)
    {
        return elem.State == System.Data.ConnectionState.Open;
    }

    // Element destruction logic
    protected override void DestroyElement(SqlConnection elem)
    {
        elem.Close();
    }
}


// usage
static void Main()
{
    // Creating the pool
    SqlConnectionPool pool = new SqlConnectionPool(10);

    // using the pool
    using (var element = pool.Rent())
    {
        var cmd = element.Element.CreateCommand();
        cmd.ExecuteNonQuery();
    }

    // Dispose the pool
    pool.Dispose();
}

Balancing static object pool

Static object pool is implemented by class 'BalancingStaticPoolManager'. Here the sample of usage:

// Creating the pool. We should pass the comparer for elements.
var pool = new BalancingStaticPoolManager<int>(
                   comparer: Comparer<int>.Default, 
                   name: "name");

// fill the pool
for (int i = 0; i < 10; i++)
    pool.AddElement(i);

// using the pool
using (var element = pool.Rent())
{
    // Here we got the best element (9)
    Console.WriteLine(element.Element);
}

// Dispose the pool
pool.Dispose();

Balancing dynamic object pool

Balancing dynamic object pool is implemented by class 'BalancingDynamicPoolManager'. Here the sample of usage:

// Create own subclass of BalancingDynamicPoolManager
public class BalancingIntPool : BalancingDynamicPoolManager<int>
{
    private readonly Random _random = new Random();

    public BalancingIntPool(int maxElementCount)
        : base(minElementCount: 0, maxElementCount: maxElementCount, name: "name")
    {
    }

    // Create element randomly
    protected override bool CreateElement(out int elem, int timeout, CancellationToken token)
    {
        elem = _random.Next(0, 100);
        return true;
    }
    // Always valid
    protected override bool IsValidElement(int elem)
    {
        return true;
    }
    // Destruction
    protected override void DestroyElement(int elem)
    {
    }
    // Implement comparison logic
    protected override int CompareElements(int a, int b, out bool stopHere)
    {
        // stopHere allow to stop search of the best on current element
        stopHere = false;
        // usual comparsion
        return a.CompareTo(b);
    }
}


static void Main()
{
    // Creating the pool
    BalancingIntPool pool = new BalancingIntPool(10);
    
    // using the pool
    using (var element = pool.Rent())
    {
        // Here we got the best element
        Console.WriteLine(element.Element);
    }
    
    // Dispose the pool
    pool.Dispose();
}

Performance

All tests performed on CPU with 8 cores. Every element is held for 100 processor ticks (Thread.Spin(100)). Less time is better.

Thread count, Element count DynamicPoolManager Naive (based on BlockingCollection) Boost
1, 1 4000 ms 4750 ms 1.18x
8, 8 1200 ms 3300 ms 2.75x
16, 16 1200 ms 3150 ms 2.63x
8, 1 9200 ms 15800 ms 1.72x
8, 4 1800 ms 7900 ms 4.39x