...
Unlike a traditional off-line C++ compilation process, the time spent in the JIT compiler is a "wall clock time" delay, in each user's face, so the JIT compiler does not have the luxury of exhaustive optimization passes. Even so, the list of optimizations the JIT compiler performs is impressive:
Constant folding
Constant and copy propagation
Common subexpression elimination
Code motion of loop invariants
Dead store and dead code elimination
Register allocation
Method inlining
Loop unrolling (small loops with small bodies)
GIV>...
GIV>Unlike a traditional off-line C++ compilation process, the time spent in the JIT compiler is a "wall clock time" delay, in each user's face, so the JIT compiler does not have the luxury of exhaustive optimization passes. Even so, the list of optimizations the JIT compiler performs is impressive:
GIV>Constant folding
GIV>Constant and copy propagation
GIV>Common subexpression elimination
GIV>Code motion of loop invariants
GIV>Dead store and dead code elimination
GIV>Register allocation
GIV>Method inlining
GIV>Loop unrolling (small loops with small bodies)
Спасибо. Просто мне кажется где-то пробегала такая тема,
что вот такой код
ArrayList ar = new ArrayList();
for(int i = 0; i < ar.Count; i++)
{
ar[i] = ...;
}
будет компилятором оптимизирован в такой
ArrayList ar = new ArrayList();
int count = ar.Count;
for(int i = 0; i < count; i++)
{
ar[i] = ...;
}
Дык вот в IL-е ничего подобного я не заметил. Идет как и положено постоянно ображение к get_Count()
GIV>> Constant folding
GIV>> Constant and copy propagation
GIV>> Common subexpression elimination
GIV>> Code motion of loop invariants
GIV>> Dead store and dead code elimination
GIV>> Register allocation
GIV>> Method inlining
GIV>> Loop unrolling (small loops with small bodies)
> Спасибо. Просто мне кажется где-то пробегала такая тема, > что вот такой код >
> ArrayList ar = new ArrayList();
> for(int i = 0; i < ar.Count; i++)
> {
> ar[i] = ...;
> }
>
> будет компилятором оптимизирован в такой
>
> ArrayList ar = new ArrayList();
> int count = ar.Count;
> for(int i = 0; i < count; i++)
> {
> ar[i] = ...;
> }
>
> Дык вот в IL-е ничего подобного я не заметил. Идет как и положено > постоянно ображение к get_Count()
Чтоб понять что наоптимизировал JIT Comiler надо смотреть native код, который он порождает (как это сделать я не знаю). В принципе этот пример попадает под "Code motion of loop invariants" но будет ли реально это сделано я не берусь утверждать. Сделай тесты — посмотри.
Здравствуйте, Аноним, Вы писали:
А>Спасибо. Просто мне кажется где-то пробегала такая тема, А>что вот такой код А>будет компилятором оптимизирован в такой А>Дык вот в IL-е ничего подобного я не заметил. Идет как и положено постоянно ображение к get_Count()
Все правильно. Вычисление того, является ли get_Count() инвариантом в цикле, в общем случае невозможно. Поэтому и не происходит вынос инварианта из цикла.
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Здравствуйте, GarryIV, Вы писали:
GIV>Чтоб понять что наоптимизировал JIT Comiler надо смотреть native код, который он порождает (как это сделать я не знаю).
Здравствуйте, xvost, Вы писали:
X>Все правильно. Вычисление того, является ли get_Count() инвариантом в цикле, в общем случае невозможно. Поэтому и не происходит вынос инварианта из цикла.
На уровне компиляции JIT многое другое возможно. Например inlining get_Count. Я не особо помню, подсчитывает ли он в рантайме количество (по моему да), так что весьма вероятно преобразование в ссылку на значение.
Здравствуйте, Аноним, Вы писали: А>Выполняет ли JIT оптимизацию IL-кода, или все компилится один к одному?
Если в методе используются поля обьектов, то создаются локальные переменные и инициализируются этими полями. Потом работа идёт через них (в этом случае экономится один push).
P.S. смотри в рефлекторе.
Здравствуйте, oleg542, Вы писали:
А>>Выполняет ли JIT оптимизацию IL-кода, или все компилится один к одному? O>Если в методе используются поля обьектов, то создаются локальные переменные и инициализируются этими полями. Потом работа идёт через них (в этом случае экономится один push). O>P.S. смотри в рефлекторе.
чтоб смотреть натив релиза надо сделать делние но 0 в том месте где ты хочешь тормознуть прогу, после этого он тебе предложет посмотреть дизасембли, а если не покажет то заходишь в дебуг и сам его включаеш.
---------------------
Ты можеш все ибо все есть в тебе.
> cordbg.exe идущий в SDK.
Зачем? В VS2002-2003 (7.0-7.1) cтавишь break-point на то место где хочешь посмотреть исходный код, лезешь в меню Debug->Windows->Disassembly, открываешь появившуюся страницу и смотришь native код.
Любое сложное решение — проявление лени человека, которому это поручили
Здравствуйте, QuICE, Вы писали:
>> cordbg.exe идущий в SDK. QIC>Зачем? В VS2002-2003 (7.0-7.1) cтавишь break-point на то место где хочешь посмотреть исходный код, лезешь в меню Debug->Windows->Disassembly, открываешь появившуюся страницу и смотришь native код.
Для запускаемого под отладчиком приложение принципиально не генерируется оптимальный код. Так что или пытаться приатачиться студией на ходу (с бабушка надвое предсказанным результатом), или cordbg.exe.
... << RSDN@Home 1.1.4 beta 4 rev. 351>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, xvost, Вы писали:
X>Все правильно. Вычисление того, является ли get_Count() инвариантом в цикле, в общем случае невозможно. Поэтому и не происходит вынос инварианта из цикла.
Вообще-то более чем возможно. Просто оптимизатор халявит.
... << RSDN@Home 1.1.4 beta 4 rev. 351>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Corwin_XX, Вы писали:
C_X>Судя по результатом тестов, про которые я читал (результаты сравнения различных компиляторов), это делается. А "бесполезные" циклы, вообще, удаляются.
"Не читайте советских газет перед едой." (с)
... << RSDN@Home 1.1.4 beta 4 rev. 351>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, <Аноним>, Вы писали:
А>Как он может в это оптимизировать? А если я сделаю al.Add() в цикле, что, всё сломается?
И тем не менее при наличии хорошего оптимизатора подобная оптимизация возможна. К сожалению, подобные оптимизации не делаются даже с массивами и в foreach-ах. А уж там им ни что не препятствует.
... << RSDN@Home 1.1.4 beta 4 rev. 351>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>И тем не менее при наличии хорошего оптимизатора подобная оптимизация возможна. К сожалению, подобные оптимизации не делаются даже с массивами и в foreach-ах. А уж там им ни что не препятствует.
Здравствуйте, VladD2, Вы писали:
X>>Все правильно. Вычисление того, является ли get_Count() инвариантом в цикле, в общем случае невозможно. Поэтому и не происходит вынос инварианта из цикла. VD>Вообще-то более чем возможно. Просто оптимизатор халявит.
Вот кусок кода:
class C
{
private ArrayList myList;
public C(ArrayList list) {myList = list;}
public void foo() {myList.Add(1);}
}
......
ArrayList list = new ArrayList (collection);
C c = new C(list);
for (int i=0; i<list.Count(); i++)
c.foo();
И как тут оптимизатор будет думать????
list.Count() не является инвариантом в этом цикле
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Здравствуйте, <Аноним>, Вы писали:
А>Спасибо. Просто мне кажется где-то пробегала такая тема, А>что вот такой код А>
А>ArrayList ar = new ArrayList();
А>for(int i = 0; i < ar.Count; i++)
А>{
А> ar[i] = ...;
А>}
А>
А>будет компилятором оптимизирован в такой
А>
А>ArrayList ar = new ArrayList();
А>int count = ar.Count;
А>for(int i = 0; i < count; i++)
А>{
А> ar[i] = ...;
А>}
А>
А>Дык вот в IL-е ничего подобного я не заметил. Идет как и положено постоянно ображение к get_Count()
буквально только сегодня прочитал в блоге Brad Abrams`а (здесь) про оптимизацию такого случая.
Если вкратце, то первый вариант будет работать даже медленнее чем второй. Происходит из-за того что JIT должен проводить проверку выхода за пределы массива. Так вот во втором случае он распознает паттерн перебора массива и проверку производит только один раз. А во втором на каждой итерации.
Мораль такова, не стоит пытаться перехитрить оптимизатор, писать надо естесственным образом, а уж он разберется что к чему. К тому же он постоянно развивается. Например в том же блоге написано, что и первый вариант со временем тоже возможно будет распознаваться.
X>class C
X>{
X> private ArrayList myList;
X> public C(ArrayList list) {myList = list;}
X> public void foo() {myList.Add(1);}
X>}
X>......
X>ArrayList list = new ArrayList (collection);
X>C c = new C(list);
X>for (int i=0; i<list.Count(); i++)
X> c.foo();
X>
X>И как тут оптимизатор будет думать???? X>list.Count() не является инвариантом в этом цикле
Тут — никак. Но это не означает, что невозможно распозновать случае которые не вызывают модифицирующие методы. Особенно это полезно для встроенных коллекций которые используются очень часто. Подобная оптимизация могла бы неплохо ускорить код приложений.
... << RSDN@Home 1.1.4 beta 4 rev. 351>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, qxWork, Вы писали:
W>Это следствие отсутствия в .NET модификатора const.
Подобный модификатор дотнету не нужен. В дотнете можно читать IL. Это позволяет определить наличие побочных эффектов без дополнительной помощь. К сожалению разработчики дотнета не озаботились подобными проблемами. Но в будущем, я уверен, до этого обязательно дойдет дело.
... << RSDN@Home 1.1.4 beta 4 rev. 351>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Igor Trofimov, Вы писали:
VD>>И тем не менее при наличии хорошего оптимизатора подобная оптимизация возможна. К сожалению, подобные оптимизации не делаются даже с массивами и в foreach-ах. А уж там им ни что не препятствует.
iT>здесь
Предпочитаю практику .
Ниже приведен набор тестов для второго фрэймворка.
1. Тест List<int> с выносом длинны в перемнную.
Console.WriteLine("Framework Version: " + Environment.Version);
const int count = 10000;
List<int> array = new List<int>(count);
for (int i = 0; i < count; i++)
array.Add(i % 20);
Console.WriteLine("Start");
int start = Environment.TickCount;
int sum = 0;
int len = array.Count;
for (int k = 0; k < 100000; k++)
for (int i = 0; i < len; i++)
sum += array[i];
Console.WriteLine("Time is {0}, sum = {1}",
Environment.TickCount - start, sum);
Результат:
Framework Version: 2.0.50215.44
Start
Time is 2719, sum = 910065408
2. Тест List<int> с проверкой длинны внутри цикла.
Console.WriteLine("Framework Version: " + Environment.Version);
const int count = 10000;
List<int> array = new List<int>(count);
for (int i = 0; i < count; i++)
array.Add(i % 20);
Console.WriteLine("Start");
int start = Environment.TickCount;
int sum = 0;
for (int k = 0; k < 100000; k++)
for (int i = 0; i < array.Count; i++)
sum += array[i];
Console.WriteLine("Time is {0}, sum = {1}",
Environment.TickCount - start, sum);
Framework Version: 2.0.50215.44
Start
Time is 2250, sum = 910065408
3. С копрированием в масси и проверкой длинны массива внутри цикла.
Console.WriteLine("Framework Version: " + Environment.Version);
const int count = 10000;
List<int> array1 = new List<int>(count);
for (int i = 0; i < count; i++)
array1.Add(i % 20);
Console.WriteLine("Start");
int start = Environment.TickCount;
int sum = 0;
int len = array1.Count;
int[] array = array1.ToArray();
for (int k = 0; k < 100000; k++)
for (int i = 0; i < array.Length; i++)
sum += array[i];
Console.WriteLine("Time is {0}, sum = {1}",
Environment.TickCount - start, sum);
Framework Version: 2.0.50215.44
Start
Time is 1359, sum = 910065408
4. С копрированием в масси и выносом длинны в переменную.
Console.WriteLine("Framework Version: " + Environment.Version);
const int count = 10000;
List<int> array1 = new List<int>(count);
for (int i = 0; i < count; i++)
array1.Add(i % 20);
Console.WriteLine("Start");
int start = Environment.TickCount;
int sum = 0;
int len = array1.Count;
int[] array = array1.ToArray();
for (int k = 0; k < 100000; k++)
for (int i = 0; i < len; i++)
sum += array[i];
Console.WriteLine("Time is {0}, sum = {1}",
Environment.TickCount - start, sum);
Framework Version: 2.0.50215.44
Start
Time is 1359, sum = 910065408
5. На чистом массиве с выносом длинны в переменную:
Console.WriteLine("Framework Version: " + Environment.Version);
const int count = 10000;
int [] array = new int[count];
for (int i = 0; i < count; i++)
array[i] = i % 20;
Console.WriteLine("Start");
int start = Environment.TickCount;
int sum = 0;
int len = array.Length;
for (int k = 0; k < 100000; k++)
for (int i = 0; i < len; i++)
sum += array[i];
Console.WriteLine("Time is {0}, sum = {1}",
Environment.TickCount - start, sum);
Framework Version: 2.0.50215.44
Start
Time is 1344, sum = 910065408
6. На чистом массиве с проверкой длинны в цикле:
Console.WriteLine("Framework Version: " + Environment.Version);
const int count = 10000;
int [] array = new int[count];
for (int i = 0; i < count; i++)
array[i] = i % 20;
Console.WriteLine("Start");
int start = Environment.TickCount;
int sum = 0;
int len = array.Length;
for (int k = 0; k < 100000; k++)
for (int i = 0; i < array.Length; i++)
sum += array[i];
Console.WriteLine("Time is {0}, sum = {1}",
Environment.TickCount - start, sum);
Framework Version: 2.0.50215.44
Start
Time is 906, sum = 910065408
Вызов свойства Count точно менее выгодно нежели копирование его значения в переменную. А вот с массивом все не так очевидно. Можно смело Сказать, что для второго фрэймворка проверка длинны массива не является проблемой и не требует оптимизации. В то время как проверка длинны коллекции более затратная операция.
Однако все это такие милиметры, что ловить их в большинстве случаев смысла не имеет.
... << RSDN@Home 1.1.4 beta 4 rev. 351>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Агащазкакже
От:
Аноним
Дата:
25.04.05 12:45
Оценка:
интересно, как это нет const??? или что то другое под этим подразумеваеться?
W>>Это следствие отсутствия в .NET модификатора const. VD>Подобный модификатор дотнету не нужен. В дотнете можно читать IL. Это позволяет определить наличие побочных эффектов без дополнительной помощь. К сожалению разработчики дотнета не озаботились подобными проблемами. Но в будущем, я уверен, до этого обязательно дойдет дело.
К сожалению, анализ IL очень дорог, чтобы его использовать в оптимизации. Использовать слово const значительно проще и быстрее, да и код становится читабельнее в разы.
Здравствуйте, qxWork, Вы писали:
W>К сожалению, анализ IL очень дорог, чтобы его использовать в оптимизации. Использовать слово const значительно проще и быстрее, да и код становится читабельнее в разы.
Ну, на счет читабельности С++-ного кода я бы лучше помолчал.
А что до родоговизны... Он один фиг читается. Иначе все инлайнинги и т.п. были бы просто не возвожны. К тому же метод можно один раз ататически анализировать и добавлять ему атрибут. А в дальнейшем анализировать только его.
В общем, это более чем реально. Но создатели дотнета не сделали этого. Будем надеяться что только пока.
... << RSDN@Home 1.1.4 beta 4 rev. 351>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.