Так что я оптимизирую программу на 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#? Если это правда, почему мой код настолько медленный? Есть ли лучший способ повторного использования массивов?
Любой совет будет оценен. Благодарю вас.
Перед началом таких экспериментов вы должны прочитать об управлении памятью на C#. 'malloc' на C# имеет практически нулевую стоимость, поэтому неудивительно, что ваш сложный ручной метод управления памятью хуже. –
Объединение - это эффективная оптимизация для сокращения расходов на ХК. ConcurrentDictionary просто очень медленный по сравнению с распределением (малых) объектов. – usr
@ KonradRudolph нулевая стоимость? Я должен это прочитать. Спасибо. Так что нет необходимости повторно использовать массивы вручную, не так ли? – Chris