Здравствуйте, Sinclair, Вы писали:
_>>Тут на самом деле не всё так просто — надо чтобы компилятор мог гарантированно видеть неизменность переменной "a" внутри цикла. Например в C++ компилятор проводит эту оптимизацию для любых пользовательских типов без всяких модификаторов чистоты. Однако при этом в некоторых специфических случаях она не срабатывает, т.к. компилятор видит потенциальную возможность изменения переменной (хотя на самом деле никаких изменений естественно нет). S>Основной инструмент, которым пользуется компилятор C++ — это инлайнинг. S>А дальше — строим SSA-форму, в которой видно, что и где изменяется, и что остаётся инвариантом.
Это да. Я просто уточнил, что вот не всегда он может это корректно определить. В большинстве случаев может, но иногда ошибается.
_>>Так что лучше сразу писать for(int i=0, е=a.GetLength(); i<е; i++), ну или использовать range for, которые транслируется (во всяком случае в C++) как раз в такую конструкцию. S>Для кого лучше? Например, на C# такой код для встроенных массивов — явная пессимизация. Потому что JIT умеет распознавать паттерн i<a.Count и устраняет проверки границ. А паттерн i<e требует значительно более длинного пути для доказательства невыхода за границы, поэтому проверки будут оставлены.
Ну я то писал про C++. Забавно, что в C# ситуация обратная. Кстати, а в какой код транслируется range for в C#? )
_>>P.S. Если вдруг кто-то думает, что оптимизация выше — это какая то непринципиальная ерунда с экономией пары процессорных тиков, то это совсем не так. Например если мы всё же используем запись цикла в первом варианте и оптимизация с a.GetLength не пройдёт, то это автоматически заблокирует нам автовекторизацию этого цикла. А это уже может быть реальным провалом в быстродействие в десятки раз. S>Ну вот в том-то и дело: декларация a.GetLength() как pure означает, что она не меняет мир — значит и a остаётся неизменной, поэтому можно оптимизировать всё.
Не значит. Если в теле цикла есть другой (модифицирующий) вызов a. Вот в случае константного a всё однозначно. А когда константности нет, то компилятор должен сам отслеживать и не всегда у него это удаётся верно.
Причём ещё нюанс: нам не обязательно требовать именно чистоты функций, вызываемых в теле цикла. Главное чтобы они не меняли a (т.е. если вдруг у a есть функция, печатающая некую отладочную информацию в консоль, то это не должно помешать такой оптимизации).