2014-07-22 2 views
1

Так что я оптимизирую программу на C#, которая очень часто использует байтовые массивы, я написал своего рода сборку для повторного использования массивов, которые должны были собираться GC. Как что:Повторное использование массивов в C#

public class ArrayPool<T> 
{ 
    private readonly ConcurrentDictionary<int, ConcurrentBag<T[]>> _pool; 

    public ArrayPool() 
    { 
     _pool = new ConcurrentDictionary<int, ConcurrentBag<T[]>>(); 
    } 

    public ArrayPool(int capacity) 
    { 
     _pool = new ConcurrentDictionary<int, ConcurrentBag<T[]>>(4, capacity); 
     for (var i = 1; i <= capacity; i++) 
     { 
      _pool.TryAdd(i, new ConcurrentBag<T[]>()); 
     } 
    } 

    public T[] Alloc(int capacity) 
    { 
     if (capacity < 1) 
     { 
      return null; 
     } 
     if (_pool.ContainsKey(capacity)) 
     { 
      var subpool = _pool[capacity]; 
      T[] result; 
      if (subpool != null) return subpool.TryTake(out result) ? result : new T[capacity]; 
      subpool = new ConcurrentBag<T[]>(); 
      _pool.TryAdd(capacity, subpool); 
      _pool[capacity] = subpool; 
      return subpool.TryTake(out result) ? result : new T[capacity]; 
     } 
     _pool[capacity] = new ConcurrentBag<T[]>(); 
     return new T[capacity]; 
    } 

    public void Free(T[] array) 
    { 
     if (array == null || array.Length < 1) 
     { 
      return; 
     } 
     var len = array.Length; 
     Array.Clear(array, 0, len); 
     var subpool = _pool[len] ?? new ConcurrentBag<T[]>(); 
     subpool.Add(array); 
    } 

} 

, и я также написал код, чтобы проверить его эффективность:

const int TestTimes = 100000; 
const int PoolCapacity = 1000; 
public static ArrayPool<byte> BytePool; 
static void Main() 
{ 
    BytePool = = new ArrayPool<byte>(PoolCapacity); 
    var watch = Stopwatch.StartNew(); 
    for (var i = 1; i <= TestTimes; i++) 
    { 
     var len = (i % PoolCapacity) + 1; 
     var array = new byte[len]; 
    } 
    watch.Stop(); 
    Console.WriteLine("Traditional Method: {0} ms.", watch.ElapsedMilliseconds); 
    watch = Stopwatch.StartNew(); 
    for (var i = 1; i <= TestTimes; i++) 
    { 
     var len = (i % PoolCapacity) + 1; 
     var array = BytePool.Alloc(len); 
     BytePool.Free(array); 
    } 
    watch.Stop(); 
    Console.WriteLine("New Method: {0} ms.", watch.ElapsedMilliseconds); 
    Console.ReadKey(); 
} 

Я думал, что это должно быть быстрее, если программа может повторно использовать память вместо таНос их каждый раз, но получается что мой код примерно в 10 раз медленнее, чем раньше:

Традиционный метод: 31 мс. Новый метод: 283 мс.

Так ли это, что результирующие массивы могут увеличить производительность на C#? Если это правда, почему мой код настолько медленный? Есть ли лучший способ повторного использования массивов?

Любой совет будет оценен. Благодарю вас.

+3

Перед началом таких экспериментов вы должны прочитать об управлении памятью на C#. 'malloc' на C# имеет практически нулевую стоимость, поэтому неудивительно, что ваш сложный ручной метод управления памятью хуже. –

+1

Объединение - это эффективная оптимизация для сокращения расходов на ХК. ConcurrentDictionary просто очень медленный по сравнению с распределением (малых) объектов. – usr

+0

@ KonradRudolph нулевая стоимость? Я должен это прочитать. Спасибо. Так что нет необходимости повторно использовать массивы вручную, не так ли? – Chris

ответ