Здравствуйте, alex_public, Вы писали:
_>Эм, я имел в виду foreach. )
_>Да, выглядит не плохо, хотя было бы красивее иметь что-то вроде foreach для Span.
С foreach немного проще, т.к. в теле цикла у нас нет индексного обращения, и мы работаем только с самим элементом.
Правила компиляции foreach весьма просты:
foreach(var t in span) sum+= t;
раскрывается в
Span<int>.Enumerator e = span.GetEnumerator();
try
{
while (e.MoveNext())
{
ref int t = e.Current;
sum += t; // original body
}
}
finally
{
(if e is IDisposable d)
d.Dispose();
}
Здесь я уже подставил те типы, которые получаются для Span<int>.
Код спановского енумератора можно посмотреть вот тут:
https://github.com/dotnet/coreclr/blob/master/src/System.Private.CoreLib/shared/System/Span.cs
Там всё выглядит хорошо: агрессивный инлайнинг, ref struct означает, что прикопать енумератор нельзя (и велик шанс разместиться в регистрах).
Но если честно, я код такого foreach не дизассемблировал, не знаю, насколько там всё хорошо оптимизируется по сравнению с честным перебором массива.
А, ну и надо учитывать, что автовекторизации в дотнете нет. Если хочется чего-то векторного — будьте любезны руками кастить Span<int> к Vector<int> и выполнять loop unrolling.
Выглядит страшненько. Я в прошлом году пробовал напилить библиотеку, которая бы преобразовывала запросы на linq поверх двумерных массивов к оптимальному векторному коду автоматически, но упёрся.
В простых случаях код векторизовался, но работал медленнее скалярного; в сложных там порождённый код просто ломал CLR и вылетал.
_>Контроль работает где-то на уровне потенциального доступа к указателям. Так что если есть шанс модифицирующего доступа, то оптимизации не будет.
Ну вот то-то и оно. const, получается, нужен программисту — чтобы он нечаянно не занёс модификации туда, куда не надо.
Ну и, потенциально, для кросс-модульных оптимизаций.