Вопрос

Я пытаюсь оптимизировать одновременную коллекцию, которая пытается свести к минимуму конфликт с блокировкой для чтения. First Pass использовал связанный список, который позволил мне заблокировать только записи, в то время как многие одновременные чтения могли продолжать разблокироваться. Это использовало обычай IEnumerator к урожай Следующее значение ссылки. Как только я начал сравнивать итерацию с коллекцией с равниной List<T> Я заметил, что моя реализация была примерно вдвое меньше (для from x in c select x В коллекции из 1* M* предметов я получил 24 мс за List<T> а также 49 мс для моей коллекции).

Итак, я подумал, что использую ReaderWriteLockSlim и жертвовать небольшим спором на чтениях, чтобы я мог использовать List<T> как мое внутреннее хранилище. Поскольку я должен запечатлеть блокировку чтения при запуске итерации и выпустить его после завершения, я сначала сделал схему урожая для моего IEnumerable, для каждогона внутреннем List<T>. Анкет Теперь я получал только 66 мс.

Я заглянул на то, что на самом деле делает список, и он использует внутренний магазин T[] и обычай IEnumerator Это перемещает индекс вперед и возвращает текущее значение индекса. Теперь вручную используя T[] Поскольку хранение означает гораздо больше работ по техническому обслуживанию, но wth, Я гоняюсь за микросекундами.

Однако даже имитируя IEnumerator Перемещение индекса на массиве, лучшее, что я мог сделать, это было ~ 38 мс. Анкет Так что дает List<T> Его секретный соус или, в качестве альтернативы, что более быстрая реализация для итератора?

ОБНОВИТЬ: Оказывается, моим основным виновником скорость была компиляция отладки, в то время как List<T> Очевидно, выпуск компиляции. В выпуске моя реализация по -прежнему медленнее, чем List<T>, в целом на моно, теперь это быстрее.

Еще одно предложение, которое я получил от друга, заключается в том, что BCL быстрее, потому что он находится в GAC и, следовательно, может быть предварительно скомпилирован системой. Придется провести мой тест в GAC, чтобы проверить эту теорию.

Это было полезно?

Решение

Приобретение и выпуск блокировки на каждой итерации звучит как плохая идея - потому что, если вы выполняете Add или же Remove Пока вы итерации над списком, это аннулирует итератор. List<T> Конечно, не понравится, например,.

Будет ли ваш вариант использования позволить вызывающим абонента ReaderWriterLockSlim Вокруг всего их процесса итерации, а не на основе каждого объекта? Это было бы более эффективным а также более надежный. Если нет, то как вы планируете решить проблему параллелистики? Если писатель добавляет элемент раньше, чем место, где я получил, простая реализация вернула бы один и тот же элемент дважды. Противоположность произойдет с удалением - итератор пропустит элемент.

Наконец, есть ли вариант .NET 4.0? Я знаю, что там есть некоторые оптимизированные одновременные коллекции ...

РЕДАКТИРОВАТЬ: Я не совсем уверен, какова ваша текущая ситуация с точки зрения создания итератора вручную, но одна вещь, которую вы, возможно, захотите исследовать, - это использование структуры для IEnumerator<T>, и, если ваша коллекция явно заявляет, что она возвращает это - вот что List<T> делает. Это делает Ознакомьтесь с изменчивой структурой, которая заставляет котят плакать по всему миру, но если это абсолютно важно для производительности, и вы думаете, что можете жить с ужасом, это, по крайней мере, стоит попробовать.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top