Боксинг IEnumerator #скорость #память
Кажется, что не всем понятна борьба за использование исходной коллекции в .NET. Поясняю весьма избитую истину - бежать по IEnumerable (IList, IReadOnlyCollection и т.п.) сильно дороже, чем по исходной коллекции. Как по памяти, так и по скорости.
Позволю себе напомнить внутреннюю работу .NET на примере List.
Класс List обладает методом GetEnumerator, который вызывается при попытке сделать по нему foreach. Как можно заметить, List.Enumerator - структура. Это важно, поскольку структура создаётся на стеке, а её методы вызываются напрямую (см. call и callvirt). Это позволяет пробегать по списку быстро и без затрат на выделение места в куче, а значит без работы GC.
Но стоит нам скастить List к IList - ситуация изменится. В этом случае foreach вызовет метод интерфейса IEnumerable.GetEnumerator, который возвращает IEnumerator, то есть интерфейс перечислителя. В случае List’a это будет та же самая структура, но размещённая в куче и доступом к её методам через callvirt. То есть мы не только создадим небольшой memory traffic, но и сильно замедлим перебор коллекции.
Насколько всё это будет медленнее - см. результаты 'https://t.me/csharp_gepard/119?comment=1239' rel='nofollow'>бенчмарка.
Кажется, что не всем понятна борьба за использование исходной коллекции в .NET. Поясняю весьма избитую истину - бежать по IEnumerable (IList, IReadOnlyCollection и т.п.) сильно дороже, чем по исходной коллекции. Как по памяти, так и по скорости.
Позволю себе напомнить внутреннюю работу .NET на примере List.
Класс List обладает методом GetEnumerator, который вызывается при попытке сделать по нему foreach. Как можно заметить, List.Enumerator - структура. Это важно, поскольку структура создаётся на стеке, а её методы вызываются напрямую (см. call и callvirt). Это позволяет пробегать по списку быстро и без затрат на выделение места в куче, а значит без работы GC.
Но стоит нам скастить List к IList - ситуация изменится. В этом случае foreach вызовет метод интерфейса IEnumerable.GetEnumerator, который возвращает IEnumerator, то есть интерфейс перечислителя. В случае List’a это будет та же самая структура, но размещённая в куче и доступом к её методам через callvirt. То есть мы не только создадим небольшой memory traffic, но и сильно замедлим перебор коллекции.
Насколько всё это будет медленнее - см. результаты 'https://t.me/csharp_gepard/119?comment=1239' rel='nofollow'>бенчмарка.