Скорость SSE с разными оптимизациями: -O1 и -O3

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение Constructor » 06.10.2013 21:36:10

Здравствуйте форумчане, при компиляции проекта с использованием SSE и различными оптимизациями ( -O1 и -O3) выходят неожиданные результаты:
LCL :
SSE + -O1 = 432
SSE + -O3 = 812 (по идеи должно быть быстрее).

Console :
SSE + -O1 = 432
SSE + -O3 = 432

Цикл и функция в DLL, программа консольная и LCL :
SSE + -O1 = 432
SSE + -O3 = 812 (по идеи должно быть быстрее).

Вот код проверки (LCL):
Код: Выделить всё
implementation
uses
   windows;

{$R *.lfm}

{ TForm1 }

Function Test(B, C : Single) : Single;
begin
  Result := (((B + C) * B) / C) - B;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin

  P1 := 234.34;
  P2 := 4363.34;

  T := GetTickCount;
  For I := 0 to 99999999 do
    A := Test(P1, P2);
  T := GetTickCount - T;
  ShowMEssage(IntToStr(T));
end;

end.


Код проверки Console:
Код: Выделить всё
program test;
uses
   windows, SysUtils, Math;
var
  P1, P2, A : Single;
  I, T : Cardinal;

Function FasmAdd(Aa, Ba : Single) : Single;
begin
  Result := (((Aa + Ba) * Aa) / Ba) - Aa;
end;

begin

  writeln(inttostr( GetSSECSR ));

  P1 := 234.34;
  P2 := 4363.34;

  T := GetTickCount;
  For I := 0 to 99999999 do
    A := FasmAdd(P1, P2);
  T := GetTickCount - T;
 
  writeln(inttostr( T ));
  readln;
end.


При использовании DLL:
Код: Выделить всё
//******************************************************************
library test1;
uses
   windows, SysUtils, Math;

var
  P1, P2, A : Single;
  I, T : Cardinal;

Function FasmAdd(Aa, Ba : Single) : Single;
begin
  Result := (((Aa + Ba) * Aa) / Ba) - Aa;
end;

Function StartTest : Cardinal; StdCall;
begin

  P1 := 234.34;
  P2 := 4363.34;

  T := GetTickCount;
  For I := 0 to 99999999 do
    A := FasmAdd(P1, P2);
  Result := GetTickCount - T;
end;

exports
  StartTest;

begin
end.
//******************************************************************
program Console;

{$mode objfpc}{$H+}

uses
  SysUtils, Math;

Function StartTest : Cardinal; StdCall; external 'test1.dll';

begin
  writeln(inttostr( GetSSECSR ));
  writeln(inttostr( StartTest ));
  readln;
end.


Для использования SSE использовал: FPUTYPE и -Cfsse. Для включения оптимизаций: настройки проекта и -O1..3. Как и ожидалось различий нет (думал что Lazarus не те параметры сообщает компилятору)
Сверял использование исключений как FPU так и SSE, при всех компиляциях одинаковы.
Версия Lazarus 1.0.12, FPC 2.6.2.
Кто подскажет, почему падает производительность у LCL и DLL(учитывая что в ней весь цикл теста) при оптимизации 3 и использовании SSE?
Constructor
незнакомец
 
Сообщения: 2
Зарегистрирован: 06.10.2013 20:58:05

Re: Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение Devel0 » 07.10.2013 13:07:15

Интересен результат также для -O2: он вполне может быть лучше, чем для -O3
Devel0
новенький
 
Сообщения: 66
Зарегистрирован: 24.07.2011 10:43:13

Re: Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение vada » 07.10.2013 14:58:25

Я давно заметил что -О3 включает тормоза, и пользуюсь только -О1. В чем дело, разбираться как-то не досуг было.
Аватара пользователя
vada
энтузиаст
 
Сообщения: 691
Зарегистрирован: 14.02.2006 13:43:17

Re: Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение SeZuka » 07.10.2013 19:11:28

Скорее всего все зависит от процессора. Оптимизировал некоторые процедуры арифметического кодирования, переписывая их на ассемблер, некоторые оптимизации давали прирост на Pentium Dual-Core, но тормозили на i5, по сравнению с исходным кодом, некоторые наоборот. Но самое интересное, после уменьшения количества вычислений, код стал работать медленнее оригинального, это так и осталось для меня загадкой.
SeZuka
постоялец
 
Сообщения: 209
Зарегистрирован: 05.09.2012 14:58:05

Re: Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение Constructor » 08.10.2013 13:38:22

Devel0 писал(а):Интересен результат также для -O2: он вполне может быть лучше, чем для -O3

Как только буду дома отпишусь о проведении тестов с оптимизацией -O2 =)

SeZuka, Конечно я согласен что все зависит от процессора, но тормоза проявляются только в DLL и LCL. При компиляции обычного консольного приложения тормозов нет, производительность одинакова.

Забыл написать об еще одном тесте:
Если глянуть асм код без оптимизации, то первое число помещается в xmm0 и далее производятся арифметические действия с xmm0 и памятью. При использовании оптимизации он помещает обе переменные в xmm1, xmm2. Все вычисления производит в xmm0 используя xmm1, xmm2.
Если в LCL приложении без оптимизации использовать оптимизированный asm код вычислений, где числа хранятся в xmm1, xmm2. То снова проявляются тормоза. Перед вычислениями SSE параметр исключений равен 6400, после переходит в 6432.

Даже не знаю в какую сторону копать :cry: Даже уже использовал команду присвоения исключений с OpenGL модуля, не помогло. SetFPUException вроде.
Constructor
незнакомец
 
Сообщения: 2
Зарегистрирован: 06.10.2013 20:58:05

Re: Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение vada » 08.10.2013 14:43:58

Даже не знаю в какую сторону копать

Я вдумчиво переработал код, добавивь куда следует INLINE.
Аватара пользователя
vada
энтузиаст
 
Сообщения: 691
Зарегистрирован: 14.02.2006 13:43:17

Re: Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение Mirage » 08.10.2013 16:21:25

Asm в студию!
Он, кстати, отличается для O1 и O3?
Без деления разница тоже есть?
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение pda » 29.11.2013 23:14:32

Вы не правильно меряете. Сейчас процессоры с наворотами по энергосбережению, частоту постоянно сбросить норовят, ядер больше одного и операционка вас перебрасывает с одного на другое, а другое может быть в спячке. Чтобы адекватно мерять надо сначала привязывать главный поток к конкректному ядру (если измеряемый код однопоточный), а потом несколько секунд "греть" его, т.е. гонять в цикле какие-нибудь бессмысленные сложения, чтобы частотя ядра на максимум вышла. Иначе не результат, а цена на дрова будет. GetTickCount, вроде тоже может странные показания выдавать, если поток к одному ядру не привязан.
Аватара пользователя
pda
постоялец
 
Сообщения: 303
Зарегистрирован: 27.05.2005 19:59:53

Re: Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение Лекс Айрин » 30.11.2013 01:31:56

pda писал(а):Вы не правильно меряете.


pda писал(а):Иначе не результат, а цена на дрова будет.


, хотелось бы все же реальных результатов, а не синтетических. Пользователь не обязан ждать пока ядро прогреется и привяжется.
Аватара пользователя
Лекс Айрин
долгожитель
 
Сообщения: 5723
Зарегистрирован: 19.02.2013 16:54:51
Откуда: Волгоград

Re: Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение debi12345 » 30.11.2013 13:00:09

Надо смотреть ассемблерный код - насколько SSE реально в нем используется.

Добавлено спустя 21 минуту 56 секунд:
У меня скорость получается одинаковой :
Код: Выделить всё
fpc -O1/3 -Mobjfpc -a -OpPENTIUMM -CfSSE3 ssetest.pas

625...670

Вообще глядя в ассемблерный листинг, видно что "-О1/3" влияет не на юзание SSE (используется одинаково интенсивно), а на попытки использовать операции с регистрами вместо оных с памятью.
Аватара пользователя
debi12345
долгожитель
 
Сообщения: 5761
Зарегистрирован: 10.05.2006 23:41:15
Откуда: Ташкент (Узбекистан)

Re: Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение pda » 30.11.2013 13:56:01

Лекс Айрин писал(а):Пользователь не обязан ждать пока ядро прогреется и привяжется.


Пользователь тут не причём. Не имеет смысла сравнивать два подхода в поисках лучшего, если погрешность измерений одного порядка с измеряемой величиной...
Аватара пользователя
pda
постоялец
 
Сообщения: 303
Зарегистрирован: 27.05.2005 19:59:53

Re: Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение Лекс Айрин » 30.11.2013 15:22:22

pda писал(а): Не имеет смысла сравнивать два подхода в поисках лучшего, если погрешность измерений одного порядка с измеряемой величиной...


А при чем тут погрешность? Лично меня, допустим, не волнуют значения работы теста, которые не применимы в реальных приложениях. Если погрешность слишком большая, значит: а) используется не тот инструмент; б) разница не играет роли.

А так, конечно, можно и в жидком азоте охлаждать, и специальным образом тесты готовить, которые дадут преимущество в определенных условиях. Но это все не распространено в реальных приложениях -- следовательно интересно только "любителям попугаев".
Аватара пользователя
Лекс Айрин
долгожитель
 
Сообщения: 5723
Зарегистрирован: 19.02.2013 16:54:51
Откуда: Волгоград

Re: Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение debi12345 » 30.11.2013 15:29:44

Правильнее будет сравнить эту программу c вариантом на Intel C++. Если "вариант" выиграет в разы - значит FPC в чем-то косячит.

Добавлено спустя 2 часа 4 минуты 4 секунды:
Очень сильно влияет кэш проца. Например в следующем С-коде
Код: Выделить всё
#include <iostream>
#include <stdint.h>
#include <windows.h>

#define FasmAdd(Aa, Ba) (((Aa + Ba) * Aa) / Ba) - Aa
#define LARGE_INT2INT64(A) (uint64_t)((unsigned int)A.LowPart + (((uint64_t)A.HighPart) << 32))

int main(int argc, char** argv) {
   double P1 = 234.34, P2 = 4363.34, A = 0, B = 0;
   uint64_t i;
   LARGE_INTEGER T1,T2, FREQ;
   QueryPerformanceFrequency(&FREQ);
   QueryPerformanceCounter(&T1);
   for (i=0; i <= 99999999; i++ ) {
      A = FasmAdd(P1, P2);
      B =  B + A;
   }
   QueryPerformanceCounter(&T2);
   printf("%f microseconds for i= %I64d and B= %f", 1000000*((double)(LARGE_INT2INT64(T2) - LARGE_INT2INT64(T1)))/(double)LARGE_INT2INT64(FREQ),i,B);
   return 0;
}



доп-операция "B = B + A;" обязательна, иначе всегда будем иметь мгновенное выполнение (SSE4.1).
Также рекомендую тайминг перевести на PerformaceCounter/Frequency-ы. Есть кто может переписать этот код на FPC ?

Добавлено спустя 4 часа 44 минуты 3 секунды:
C-код выполняется 60 миллисекунд.
Его паскальный аналог :
Код: Выделить всё
program ssetest;

{$mode objfpc}

uses
   windows, SysUtils, Math;

var
  P1,P2,A,B: extended;
  i: cardinal;
  T1,T2, FREQ: LARGE_INTEGER;

  function LARGE_INT2INT64(A: LARGE_INTEGER):int64; inline;
  begin
    result:= int64(dword(A.LowPart) + ((qword(A.HighPart)) shl 32));
  end;
begin

  P1 := 234.34;
  P2 := 4363.34;
  B:= 0;

  QueryPerformanceFrequency(@FREQ);
  QueryPerformanceCounter(@T1);
  For i:= 0 to 99999999 do begin
    A := (((P1 + P2) * P1) / P2) - P1;
    B:= B + A;
  end;
  QueryPerformanceCounter(@T2);
  writeln(1000000*(extended(LARGE_INT2INT64(T2) - LARGE_INT2INT64(T1)))/extended(LARGE_INT2INT64(FREQ)),
   'microseconds for i=',i,' and B=', B
  );
  readln;
end.

выполняется около 1 секунды то есть в 16 раз медленнее. Кстати, INLINE компилятор для функции не отработал (в ассемблерном коде стек сохраняется и восстанавливается как у обычной функции). Или я чего-то напутал..
Но этот инлайн на результаты все равно него не влияет.
Аватара пользователя
debi12345
долгожитель
 
Сообщения: 5761
Зарегистрирован: 10.05.2006 23:41:15
Откуда: Ташкент (Узбекистан)

Re: Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение pda » 30.11.2013 22:28:41

Лекс Айрин писал(а):Лично меня, допустим, не волнуют значения работы теста, которые не применимы в реальных приложениях.


Вы не получаете данных, которыми можно оперировать. У вас тест может показать, что более оптимальный код "работает" медленнее, потому что попал на холодное ядро. Соотвтественно, смысл сравнения полностью теряется. И делать какие-то выводы относительно: так лучше, а так - нет, невозможно.

Но если вы не хотите этого понимать, то я не буду вас заставлять. Извините, что побеспокоил.
Аватара пользователя
pda
постоялец
 
Сообщения: 303
Зарегистрирован: 27.05.2005 19:59:53

Re: Скорость SSE с разными оптимизациями: -O1 и -O3

Сообщение debi12345 » 30.11.2013 23:13:17

Цикл и команды С:
Код: Выделить всё
      .L4:
   add   eax, 1    # ivtmp.29,
//   for (i=0; i <= 99999999; i++ ) {
//       A = FasmAdd(P1, P2);
//       B =  B + A;
      addpd   xmm6, xmm0    # vect_var_.23, vect_cst_.24
      cmp   eax, 50000000    # ivtmp.29,
          jne   .L4    #,
//}


Используются векторные команды.

FPC делает цикл как :
Код: Выделить всё
.Lj19:
   incl   -16(%ebp)
   fldt   U_P$SSETEST_P2
   fldt   U_P$SSETEST_P1
   faddp   %st,%st(1)
   fldt   U_P$SSETEST_P1
   fmulp   %st,%st(1)
   fldt   U_P$SSETEST_P2
   fdivrp   %st,%st(1)
   fldt   U_P$SSETEST_P1
   fsubrp   %st,%st(1)
   fstpt   U_P$SSETEST_A
   fldt   U_P$SSETEST_A
   fldt   U_P$SSETEST_B
   faddp   %st,%st(1)
   fstpt   U_P$SSETEST_B
   cmpl   $99999999,-16(%ebp)
   jb   .Lj19

то есть использует SSE о-о-о-очень неоптимальным способом.

Добавлено спустя 5 минут 34 секунды:
Также видно, что умный (на максимальной оптимизации) G++-компилятор "раскусил" что "FasmAdd(P1, P2)" - чистая (зависящая только от входных аргументов) функция и поэтому ее достаточно выполнить один раз, закэшировать результат и потом его использовать в цикле как константу в регистре. FPC таким интеллектом пока еще не обладает.
Аватара пользователя
debi12345
долгожитель
 
Сообщения: 5761
Зарегистрирован: 10.05.2006 23:41:15
Откуда: Ташкент (Узбекистан)

След.

Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 8

Рейтинг@Mail.ru