Inline - методы, раскрутка циклов. Надо ли?

Известно, что вызов метода (функции или процедуры) является отдельной командой процессора и на её выполнение так же требуется время, как и, например, на сложение. Оно не велико само по себе, но если вызов происходит очень часто (что не является редкостью), задержки становятся ощутимыми. Для исправления ситуации необходимо вставлять метод в то место кода, где он был вызван, то есть заменяя вызов метода на его тело. В C++ для этого введён атрибут inline, в .NET для этого встроены свои атрибуты, но выглядят они костыльно (так как инлайном занимается JIT-компилятор, а не компилятор языка), хотя и позволяют производить некоторую настройку. Однако, JIT - компилятор не в состоянии определить необходимость инлайна так же точно, как это может сделать программист. Атрибуты инлайна в .NET не могут заставить компилятор инлайнить метод, могут только “предложить” это сделать. Хотелось бы услышать мнение пользователей, нужно ли Паскалевскому компилятору инлайнить методы самостоятельно по указанию программиста, или нет. Ну и введение атрибута inline соответственно.

А давайте для начала вы найдёте какую то статью на хабре или где то ещё, рассказывающую что именно и как инлайнит JIT. И исходя из этого вы приведёте пример, где программист знает лучше чем JIT, что надо инлайнить.

Есть, например, обсуждение этого вопроса на stackoverflow: https://ru.stackoverflow.com/questions/606360/inline-термин-в-контексте-c-jit-компилятора

По сути, это - обычная оптимизация. Её можно сделать и вручную полностью, но код при этом будет страшным, мягко говоря. Компилятор, каким бы продвинутым он ни был, не в состоянии анализировать код так глубоко, как это делает человек.

Нет, что оно делает это понятно.

Но я спрашивал где такое бывает:

То есть, где компилятор не инлайнит, но если программист добавит AggressiveInlining и тогда метод всё равно инлайнится. Если верить списку по ссылке которую вы привели выше - есть только 1 случай, когда IL код метода больше 32 байт, но при этом в нём нет ни рекурсивности, ни обработки исключений, ни “экзотических инструкций”, проверок безопасности.

По моему, этот случай редкий, можно и атрибутом указывать… По крайней мере это не хуже добавления нового ключего слова. Хотя если его введут - буду пользоваться.

Не факт. Это в любом случае будет определено окончательно только JIT - компилятором, даже не компилятором языка.

Никто не говорит, что все остальные случаи инлайнятся. А вот случаев, когда это необходимо предостаточно.

Думаю, для этого ещё рано. Пусть кто то ещё выскажется по этому поводу. Но если введут - будет хорошо.

Хрен редьки не слаще. Атрибут ничего не гарантирует. Тут надо чтобы компилятор Паскаля сам инлайнил те методы, которые имеют соответствующий атрибут.

Вы всё продолжаете говорить абстрактно, как будто случае когда AggressiveInlining влияет на ситуацию. Если их полно - приведите больше примеров, потому что я нашёл только 1 (тот что я выше описал), и он очень редкий.

Что именно Вам нужно?

Ах вот как, вы хотите чтоб на уровне паскаля… Ну всё равно, тогда приведите пример, когда паскаль мог был проинлайнить, а JIT это уже не сделает. У JIT информация о процессоре под который он должен оптимизировать, так что, по моему, он как раз лучше знает.

Ну не надо за компилятор работу делать, а?..

3 лайка

Вот и я о том.

Предсказать, сделает это JIT или нет невозможно. Могу привести просто пример инлайна.

Да, но JIT не будет инлайнить всегда когда это имеет смысл. Или вы отталкиваетесь от мысли что он не качественный?)))

В любом случае, есть какие то правила по которым он работает, по моему надо сначала их найти, а потом уже утверждать, когда есть смысл выполнять за него эту работу или нет.

Нет, я не отталкиваюсь от этого, но важно понимать, что компьютер - не человек.

Я о том, что большую часть подобных оптимизаций компилятор делает лучше, чем человек. Зря вы думаете, что у программиста больше глубоких знаний о коде - черта с два. У Admin золотой курс в магистратуре, однако :slight_smile: Можете у него спросить, почему это так. Я корректно не отвечу… Совсем вкратце - вы больше знаете об алгоритме, компилятор - о платформе, памяти, и прочих железных и не только моментах. Когда мы пишем inline, разворачиваем вручную циклы, и занимаемся прочей преждевременной ручной оптимизацией, мы этим только мешаем компилятору сделать все правильно. “Вбитые гвозди” вида inline не нужны. Компилятор для того и сделан, чтобы программист думал над задачей, а не над тем, как байтики через регистры прокинуть. Стоит попробовать дать ему этим заняться.

1 лайк

Правильно, JIT основан не на нейронной сети, а это значит что его действия можно предусмотреть до мелочей. Если вы всё же решили лучше него разбираться когда нужна оптимизация - нужно всё равно в первую очередь разобраться что именно он делает хуже человека.

Я в основном согласен с @JediKnight, если не считать что оптимизация может быть на оперативку, объём программы, циклы и т.п., а раз у компилятора нет для этого переключателя - он значит делает или что то 1, или, что более вероятно, что то среднее. И атрибутам баланс тут можно изменить, вполне.

А попробуйте у себя эту программу:

Uses System;

function BuildIndex(X: Int32; Y: Int32; Z: Int32; Width: Int32; Depth: Int32): Int32;
begin
  Result := ((Width * Y) + X) * Depth + Z;
end;

begin
  var arr: array of Int32 := new Int32[64 * 1000 * 1000];
  for Var d := 0 to 63 do
    for Var y := 0 to 999 do
      for Var x := 0 to 999 do
      begin
        arr[((1000 * y) + x) * 64 + d] := x * y * d;
      end;
  var DT := DateTime.Now;
  for Var d := 0 to 63 do
    for Var y := 0 to 999 do
      for Var x := 0 to 999 do
      begin
        arr[((1000 * y) + x) * 64 + d] := x * y * d;
      end;
  Console.WriteLine((DateTime.Now - DT).TotalMilliseconds);
  for Var d := 0 to 63 do
    for Var y := 0 to 999 do
      for Var x := 0 to 999 do
      begin
        arr[BuildIndex(x, y, d, 1000, 64)] := x * y * d;
      end;
  DT := DateTime.Now;
  for Var d := 0 to 63 do
    for Var y := 0 to 999 do
      for Var x := 0 to 999 do
      begin
        arr[BuildIndex(x, y, d, 1000, 64)] := x * y * d;
      end;
  Console.WriteLine((DateTime.Now - DT).TotalMilliseconds);
  Console.ReadLine();
end.

Абсолютно одинаково по скорости, если компилировать в Release виде.

Я тоже в Release. Причём компилирую exe и запускаю его. А если этот код:

Uses System;

function BuildIndex(X: Int32; Y: Int32; Z: Int32; Width: Int32; Depth: Int32): Int32;
begin
  Result := ((Width * Y) + X) * Depth + Z;
end;

function mul(x: Int32; y: Int32; d: Int32): Int32;
begin
  Result := x * y * d;
end;

begin
  var arr: array of Int32 := new Int32[64 * 1000 * 1000];
  for Var d := 0 to 63 do
    for Var y := 0 to 999 do
      for Var x := 0 to 999 do
      begin
        arr[((1000 * y) + x) * 64 + d] := x * y * d;
      end;
  var DT := DateTime.Now;
  for Var d := 0 to 63 do
    for Var y := 0 to 999 do
      for Var x := 0 to 999 do
      begin
        arr[((1000 * y) + x) * 64 + d] := x * y * d;
      end;
  Console.WriteLine((DateTime.Now - DT).TotalMilliseconds);
  for Var d := 0 to 63 do
    for Var y := 0 to 999 do
      for Var x := 0 to 999 do
      begin
        arr[BuildIndex(x, y, d, 1000, 64)] := mul(x, y, d);
      end;
  DT := DateTime.Now;
  for Var d := 0 to 63 do
    for Var y := 0 to 999 do
      for Var x := 0 to 999 do
      begin
        arr[BuildIndex(x, y, d, 1000, 64)] := mul(x, y, d);
      end;
  Console.WriteLine((DateTime.Now - DT).TotalMilliseconds);
  Console.ReadLine();
end.

Ну так что тоже, а результаты у вас какие?

P.S. вторая программа тоже одинаковые результаты выдаёт.