Здравствуйте, Sinclair, Вы писали:
S>Если бы мы говорили об императивном языке, то информация о чистоте функциии позволяла бы компилятору делать всякие полезности типа:
S>S>for(int i=0; i<a.GetLength(); i++)
S> average += a[i]/a.GetLength();
S>
S>=>
S>S>var tmp = a.GetLength();
S>for(int i=0; i<tmp; i++)
S> average += a[i]/tmp;
S>
S>Современный CLR устраняет только первый вызов (и сравнение) для встроенных типов — то есть имеем хардкод. Не существует способа, кроме инлайнинга, который бы помог сделать loop invariant reduction в подобных сценариях.
S>Наличие pure как двустороннего контракта позволяет делать такие трансформации.
Тут на самом деле не всё так просто — надо чтобы компилятор мог гарантированно видеть неизменность переменной "a" внутри цикла. Например в C++ компилятор проводит эту оптимизацию для любых пользовательских типов без всяких модификаторов чистоты. Однако при этом в некоторых специфических случаях она не срабатывает, т.к. компилятор видит потенциальную возможность изменения переменной (хотя на самом деле никаких изменений естественно нет).
Так что лучше сразу писать for(int i=0, е=a.GetLength(); i<е; i++), ну или использовать range for, которые транслируется (во всяком случае в C++) как раз в такую конструкцию.
P.S. Если вдруг кто-то думает, что оптимизация выше — это какая то непринципиальная ерунда с экономией пары процессорных тиков, то это совсем не так. Например если мы всё же используем запись цикла в первом варианте и оптимизация с a.GetLength не пройдёт, то это автоматически заблокирует нам автовекторизацию этого цикла. А это уже может быть реальным провалом в быстродействие в десятки раз.