Я думаю, у меня есть ответ! Да, это правда, что функция Contains() в списке (массиве) равна O(n), но если массив короткий и вы используете типы значений, он все равно должен быть достаточно быстрым. Но с помощью CLR Profiler [бесплатная загрузка с сайта Microsoft] я обнаружил, что Contains() упаковывает значения для их сравнения, что требует выделения кучи, что ОЧЕНЬ дорого (медленно). [Примечание: это .Net 2.0; другие версии .Net не тестировались.]
Вот полная история и решение. У нас есть перечисление под названием «VI» и создан класс под названием «ValueIdList», который является абстрактным типом для списка (массива) объектов VI. Первоначальная реализация была во времена древней .Net 1.1 и использовала инкапсулированный ArrayList. Недавно мы обнаружили в http://blogs.msdn.com/b/joshwil/archive/2004/04/13/112598.aspx, что универсальный список (List‹VI>) работает намного лучше, чем ArrayList для типов значений (например, наш enum VI), потому что значения не нужно упаковывать. Это правда, и это сработало... почти.
CLR Profiler преподнес сюрприз. Вот часть графика распределения:
- ValueIdList::Содержит bool(VI) 5,5 МБ (34,81%)
- Generic.List::Contains bool(‹UNKNOWN>) 5,5 МБ (34,81%)
- Generic.ObjectEqualityComparer‹T>::Equals bool (‹UNKNOWN> ‹UNKNOWN>) 5,5 МБ (34,88%)
- Ценности.VI 7,7 МБ (49,03%)
Как видите, Contains() неожиданно вызывает Generic.ObjectEqualityComparer.Equals(), что, по-видимому, требует упаковки значения VI, что требует дорогостоящего выделения кучи. Странно, что Microsoft исключила бокс из списка только для того, чтобы снова потребовать его для такой простой операции, как эта.
Наше решение состояло в том, чтобы переписать реализацию Contains(), что в нашем случае было легко сделать, поскольку мы уже инкапсулировали общий объект списка (_items). Вот простой код:
public bool Contains(VI id)
{
return IndexOf(id) >= 0;
}
public int IndexOf(VI id)
{
int i, count;
count = _items.Count;
for (i = 0; i < count; i++)
if (_items[i] == id)
return i;
return -1;
}
public bool Remove(VI id)
{
int i;
i = IndexOf(id);
if (i < 0)
return false;
_items.RemoveAt(i);
return true;
}
Сравнение значений VI теперь выполняется в нашей собственной версии IndexOf(), которая не требует упаковки и работает очень быстро. Наша конкретная программа ускорилась на 20% после этой простой перезаписи. О(н)... нет проблем! Просто избегайте напрасного использования памяти!
person
Kevin North
schedule
08.07.2013