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

У меня второй пример работает дольше примерно на 100 мсек.

100 миллисекунд это не показатель, вызов метода больше, на много, тратит. Запустите много раз.

Слышал, что в мехмате есть открытые семинары по программированию. Жаль только, что далеко от меня до Ростова.

Uses System;

begin
  var DT := DateTime.Now;
  var a: Double;
  for Var i := 0 to 10000000 do
  begin
    for Var j := 0 to 19 do
    begin
      a := i * j;
    end;
  end;
  Console.WriteLine((DateTime.Now - DT).TotalMilliseconds);
  DT := DateTime.Now;
  for Var i := 0 to 10000000 do
  begin
    a := i;
    a := i * 1;
    a := i * 2;
    a := i * 3;
    a := i * 4;
    a := i * 5;
    a := i * 6;
    a := i * 7;
    a := i * 8;
    a := i * 9;
    a := i * 10;
    a := i * 11;
    a := i * 12;
    a := i * 13;
    a := i * 14;
    a := i * 15;
    a := i * 16;
    a := i * 17;
    a := i * 18;
    a := i * 19;
  end;
  Console.WriteLine((DateTime.Now - DT).TotalMilliseconds);
end.

Кстати циферки тут очень интересные. Примерно во столько раз программа на C# быстрее, чем на Паскале.

Не так уж и много. Но запускал несколько раз - разница доходила до секунды.

Давайте в процентах говорить лучше.

Вот у меня разница может колебаться (в обе стороны) на 10-15%, а если я атрибутом отключаю инлайнинг - вызов функции становится на 30-50% медленнее.

Как Вы его отключаете?

Uses System;
uses System.Runtime.CompilerServices;

[MethodImpl(MethodImplOptions.NoInlining)]
function BuildIndex(X: Int32; Y: Int32; Z: Int32; Width: Int32; Depth: Int32): Int32;
begin
  Result := ((Width * Y) + X) * Depth + Z;
end;

[MethodImpl(MethodImplOptions.NoInlining)]
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.

Хорошо. Вопрос с инлайном закрыт. Но вот что с раскруткой циклов?

А при чём тут они? И что с ними собственно, вы предлагаете?

Раскручивать. Посмотрите на мой предыдущий пост. Inline - методы. Надо ли?

И правильно. Компилятор не генерирует ассемблерный код. Этим занимается JIT. Ему лучше знать, какую функцию инлайнить.

Хорошо. Я уже проверил это. Но что делать с раскруткой? Код выше.

“ускорение” этой программы связано с тем, что 18 из 19ти тупо не выполняются…

А если так:

Uses System;

begin
  var DT := DateTime.Now;
  var a: Double;
  for Var i := 1 to 10000000 do
  begin
    for Var j := 1 to 19 do
    begin
      a := i * j;
    end;
  end;
  Console.WriteLine((DateTime.Now - DT).TotalMilliseconds);
  DT := DateTime.Now;
  for Var i := 1 to 10000000 do
  begin
    a := i * 1;
    a := i * 2;
    a := i * 3;
    a := i * 4;
    a := i * 5;
    a := i * 6;
    a := i * 7;
    a := i * 8;
    a := i * 9;
    a := i * 10;
    a := i * 11;
    a := i * 12;
    a := i * 13;
    a := i * 14;
    a := i * 15;
    a := i * 16;
    a := i * 17;
    a := i * 18;
    a := i * 19;
  end;
  Console.WriteLine((DateTime.Now - DT).TotalMilliseconds);
  Console.ReadLine();
end.

Кхм, и правда, это уже не малая разница)) Но, по моему давать компилятору раскручивать циклы - не лучшая идея. Лучше, к примеру, директивой. К примеру я вот сейчас EXAPUNKS играю (очередная игра про програминг на ассемблере) - там, по моему, очень красивое решение.

@REP 3
ADDI X {3,2} X
@END

Развернётся в:

ADDI X 3 X
ADDI X 5 X
ADDI X 7 X

То есть скопирует строчки выделенные директивой и заменит {...} на первое число, но в каждой копии строчки будет увеличивать его с шагом который записан вторым числом.

С языка сняли :rofl:

Нет, проблема в том что у вас предыдущие значения не смысла присваивать, потому что их сразу перезаписывает. Так правильнее:

Uses System;

begin
  var DT := DateTime.Now;
  var a: Double;
  for Var i := 1 to 10000000 do
  begin
    for Var j := 1 to 19 do
    begin
      a += i * j;
    end;
  end;
  Console.WriteLine((DateTime.Now - DT).TotalMilliseconds);
  a := 0;
  DT := DateTime.Now;
  for Var i := 1 to 10000000 do
  begin
    a += i * 1;
    a += i * 2;
    a += i * 3;
    a += i * 4;
    a += i * 5;
    a += i * 6;
    a += i * 7;
    a += i * 8;
    a += i * 9;
    a += i * 10;
    a += i * 11;
    a += i * 12;
    a += i * 13;
    a += i * 14;
    a += i * 15;
    a += i * 16;
    a += i * 17;
    a += i * 18;
    a += i * 19;
  end;
  Console.WriteLine((DateTime.Now - DT).TotalMilliseconds);
  Console.ReadLine();
end.

Но разница всё равно в 2 раза, так что смысл в этом всём есть.

Привет, переполнение переменной! Но всё равно, к сожалению компилятор не увидел и не исправил этой чуши :joy:

Как считаете, может сделать опрос по директиве раскрутки цикла?

Ну хорошо:

Uses System;

begin
  var DT := DateTime.Now;
  var a: Double;
  for Var i := 1 to 10000000 do
  begin
    a -= a*0.99;
    for Var j := 1 to 19 do
    begin
      a += i * j;
    end;
  end;
  Console.WriteLine((DateTime.Now - DT).TotalMilliseconds);
  a := 0;
  DT := DateTime.Now;
  for Var i := 1 to 10000000 do
  begin
    a -= a*0.99;
    a += i * 1;
    a += i * 2;
    a += i * 3;
    a += i * 4;
    a += i * 5;
    a += i * 6;
    a += i * 7;
    a += i * 8;
    a += i * 9;
    a += i * 10;
    a += i * 11;
    a += i * 12;
    a += i * 13;
    a += i * 14;
    a += i * 15;
    a += i * 16;
    a += i * 17;
    a += i * 18;
    a += i * 19;
  end;
  Console.WriteLine((DateTime.Now - DT).TotalMilliseconds);
  writeln(a);
  Console.ReadLine();
end.

Всё равно, разница такая же, в 2 раза.