I was wondering about the possibility to create an implementation of IList<T>
that is faster than List<T>
using C#, or at least faster at adding elements.
I thought to try to write out one using a technique similar to the binary tree that just came up to my mind. Basically, each item is a pointer to a block of memory (let me call it entry) containing three more pointers:
- A pointer to the item
- A pointer to the previous entry
- A pointer to the next entry
Here's my implementation, which is not complete yet, though:
public class FastList<T> : IList<T>
{
private static readonly int sPtrSize = IntPtr.Size;
private static readonly int sItemSize = sPtrSize * 3;
private static readonly int sItemOffset = 0;
private static readonly int sPreviousOffset = sItemSize - (sPtrSize << 1);
private static readonly int sNextOffset = sItemSize - sPtrSize;
private static IntPtr CreateItem() => Marshal.AllocHGlobal(sItemSize);
private static GCHandle GetItemHandle(IntPtr ptr)
{
IntPtr itemPtr = Marshal.ReadIntPtr(ptr, sItemOffset);
return GCHandle.FromIntPtr(itemPtr);
}
private static void DeleteItem(IntPtr item)
{
GetItemHandle(item).Free();
Marshal.FreeHGlobal(item);
}
private static T GetItem(IntPtr ptr)
{
GCHandle handle = GetItemHandle(ptr);
return (T)handle.Target;
}
private static void SetItem(IntPtr ptr, T item)
{
GCHandle handle = GCHandle.Alloc(item, GCHandleType.Pinned);
IntPtr itemPtr = GCHandle.ToIntPtr(handle);
Marshal.WriteIntPtr(ptr, sItemOffset, itemPtr);
}
private static IntPtr GetPrevious(IntPtr ptr) => Marshal.ReadIntPtr(ptr, sPreviousOffset);
private static void SetPrevious(IntPtr ptr, IntPtr previous) => Marshal.WriteIntPtr(ptr, sPreviousOffset, previous);
private static IntPtr GetNext(IntPtr ptr) => Marshal.ReadIntPtr(ptr, sNextOffset);
private static void SetNext(IntPtr ptr, IntPtr next) => Marshal.WriteIntPtr(ptr, sNextOffset, next);
private class Enumerator : IEnumerator<T>
{
private readonly FastList<T> mList;
private int mPosition = -1;
private IntPtr mCurrent = IntPtr.Zero;
public Enumerator(FastList<T> list) => mList = list;
public T Current => GetItem(mCurrent);
object IEnumerator.Current => Current;
public bool MoveNext()
{
if (++mPosition >= mList.Count)
return false;
if (mCurrent == IntPtr.Zero)
{
mCurrent = mList.mStart;
return mList.Count != 0;
}
mCurrent = GetNext(mCurrent);
return mCurrent != IntPtr.Zero;
}
public void Reset() => mCurrent = mList.mStart;
public void Dispose()
{
}
}
private readonly IntPtr mStart = CreateItem();
private IntPtr mEnd;
public FastList()
{
SetNext(mStart, IntPtr.Zero);
mEnd = mStart;
}
~FastList() => DeleteItem(mStart);
public T this[int index]
{
get => GetItem(FindItem(index));
set => SetItem(FindItem(index), value);
}
public int Count
{
get;
private set;
}
public bool IsReadOnly
{
get;
} = false;
private IntPtr FindItem(int index)
{
int count = Count;
IntPtr item;
if (index < count >> 1)
{
item = mStart;
for (; index != -1 && (item = GetNext(item)) != IntPtr.Zero; index--) ;
}
else
{
item = mEnd;
index = count - index;
for (; index != -1 && (item = GetPrevious(item)) != IntPtr.Zero; index--) ;
}
return item;
}
public void Add(T item)
{
SetItem(mEnd, item);
IntPtr previous = mEnd;
mEnd = CreateItem();
SetPrevious(mEnd, previous);
SetNext(mEnd, IntPtr.Zero);
SetNext(previous, mEnd);
Count++;
}
public void Clear()
{
SetNext(mStart, IntPtr.Zero);
for (IntPtr item = mStart; (item = GetNext(item)) != IntPtr.Zero;)
DeleteItem(item);
Count = 0;
}
private void RemoveItem(IntPtr item)
{
IntPtr previous = GetPrevious(item);
IntPtr next = GetNext(item);
SetNext(previous, next);
SetPrevious(next, previous);
DeleteItem(item);
}
public bool Contains(T item) => IndexOf(item) != -1;
public void CopyTo(T[] array, int arrayIndex) => throw new NotImplementedException();
public int IndexOf(T item) => throw new NotImplementedException();
public void Insert(int index, T item)
{
IntPtr next = FindItem(index);
IntPtr previous = GetPrevious(next);
IntPtr itemPtr = CreateItem();
SetItem(itemPtr, item);
SetPrevious(itemPtr, previous);
SetNext(itemPtr, next);
SetPrevious(next, itemPtr);
SetNext(previous, itemPtr);
}
public bool Remove(T item) => throw new NotImplementedException();
public void RemoveAt(int index) => RemoveItem(FindItem(index));
public IEnumerator<T> GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Unfortunately, however, it turns out that it is roughly 10 times slower than List<T>
. I couldn't still understand why.
Could someone, please, tell me why it results so slow and suggest me how can I speed this up?
FastList
is slower thanList
:-) How did you measure and compare them? \$\endgroup\$