views:

208

answers:

2

I need to improve memory performance on my application and I could see that I have problems with memory fragmentation.

I've read an interesting article on large objects from Andrew Hunter of Red Gate, and one of the solutions he recommends is:

If large data structures need to live for a long time, and especially if they need to grow in size over time, the best approach is simply to consider using or writing a different data structure to store them. Arrays can contain up to around 10,000 elements before they are put on the large object heap and can cause problems, so a very effective way to store 100,000 entries might be to store 10 arrays each containing 10,000 elements: none will end up on the large object heap so no fragmentation will occur. This could be written as an IList subclass, which would make it easy to drop in transparently to replace existing code.

How do I implement his suggestion in my code?

My program has a very complex form (with an object that leaves residual memory every time it opens. I found a complex list that may be the culprit, and I'd like to implement his suggestion to see if it fixes the issue.

+2  A: 

What's wrong with using List for that? That's nothing but an implementation of IList and you can do the partitioning yourself. But if you want to do it transparently:

Implement IList (it's just an interface, nothing special about it. Maybe I don't understand the question?) and back it up by arrays of your desired size. Your Get() would then take the index / sizeOfArrays as index of the array containing the desired item and return the index % sizeOfArraysth item in that array.


For fun, because it's a lazy friday, I wrote something up. Note:

  • I didn't test it
  • I cannot comment on the correctnes of your quoted claims that this might help avoiding memory fragmentation, I just blindly looked at your request
  • I don't know if List or any other collection is already smart enough to do just that
  • I made some decisions that might not be right for you (i.e. you cannot blindly drop this in your code if you're using arrays now. Look at the Item implementation, especially the setter, for example

That said, here's a starting point that reduced my pre-weekend motivational deficit. I left some interesting methods as exercise to the dear reader (or OP).. ;-)

public class PartitionList<T> : IList<T> {
    private readonly int _maxCountPerList;
    private readonly IList<IList<T>> _lists;

    public PartitionList(int maxCountPerList) {
        _maxCountPerList = maxCountPerList;
        _lists = new List<IList<T>> { new List<T>() };
    }

    public IEnumerator<T> GetEnumerator() {
        return _lists.SelectMany(list => list).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator() {
        return GetEnumerator();
    }

    public void Add(T item) {
        var lastList = _lists[_lists.Count - 1];
        if (lastList.Count == _maxCountPerList) {
            lastList = new List<T>();
            _lists.Add(lastList);
        }
        lastList.Add(item);
    }

    public void Clear() {
        while (_lists.Count > 1) _lists.RemoveAt(1);
        _lists[0].Clear();
    }

    public bool Contains(T item) {
        return _lists.Any(sublist => sublist.Contains(item));
    }

    public void CopyTo(T[] array, int arrayIndex) {
        // Homework
        throw new NotImplementedException();
    }

    public bool Remove(T item) {
        // Evil, Linq with sideeffects
        return _lists.Any(sublist => sublist.Remove(item));
    }

    public int Count {
        get { return _lists.Sum(subList => subList.Count); }
    }

    public bool IsReadOnly {
        get { return false; }
    }

    public int IndexOf(T item) {
        int index = _lists.Select((subList, i) => subList.IndexOf(item) * i).Max();
        return (index > -1) ? index : -1;
    }

    public void Insert(int index, T item) {
        // Homework
        throw new NotImplementedException();
    }

    public void RemoveAt(int index) {
        // Homework
        throw new NotImplementedException();
    }

    public T this[int index] {
        get {
            if (index >= _lists.Sum(subList => subList.Count)) {
                throw new IndexOutOfRangeException();
            }
            var list = _lists[index / _maxCountPerList];
            return list[index % _maxCountPerList];
        }
        set {
            if (index >= _lists.Sum(subList => subList.Count)) {
                throw new IndexOutOfRangeException();
            }
            var list = _lists[index / _maxCountPerList];
            list[index % _maxCountPerList] = value;
        }
    }
}
Benjamin Podszun
A: 

The .NET framework already has types for this purpose. The System.Collections.Generic.LinkedList<T> could be useful to you. It doesn't use arrays internally, but uses, uhh a linked list of course :-)

Steven
I would assume he wants random access.
Dan Tao
In that case he would indeed be better of creating a custom implementation as Benjamin Podszun has done.
Steven