Производительность 2.0 vs 1.010

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

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

Ответить
anotherche

Сообщение anotherche »

Код тестовой программы (просто длинный цикл с вещественными вычислениями), откомпилированный с одинаковыми опциями исполняется на 20%(atlon) - 100%(p4) медленней. Почему?
Sniper
постоялец
Сообщения: 472
Зарегистрирован: 28.05.2005 13:02:42

Сообщение Sniper »

Разница в архитектуре у них ОГРОМНАЯ
Аватара пользователя
STAKANOV
энтузиаст
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение STAKANOV »

anotherche писал(а): Код тестовой программы (просто длинный цикл с вещественными вычислениями), откомпилированный с одинаковыми опциями исполняется на 20%(atlon) - 100%(p4) медленней. Почему?

Это разница между чем и чем?
А ОС какая?
Я правильно понял - один код был собран fpc 2.0.0 и fpc 1.010 и затем оба ипоняемых модуля были запущены на ПК с Atlon и ПК с P4 ?
А можно пример кода?
anotherche

Сообщение anotherche »

Программа была написана давно (видно, что со времен 486-х). Написана, как видно, большим пальцем левой ноги. Но для единообразия тестирования всех последующих конфигураций, она не модифицировалась (разве что длина внешнего цикла увеличивалась так, чтобы общее время счета было порядка секунд).


Это разница между чем и чем?...
Я правильно понял - один код был собран fpc 2.0.0 и fpc 1.010 и затем оба ипоняемых модуля были запущены на ПК с Atlon и ПК с P4 ?

да, все так.

Код: Выделить всё

program Test_PC;
uses crt,dos{,graph};
Var i,j,k, itnum : Longint;
    c,dT : double;
         {##################################################}
Function Time : double;
Var Ho,Mi,Se,Se1 : word; dH,dM,dS,dS1 : double;
begin
          GetTime(Ho,Mi,Se,Se1); dH:=Ho; dM:=Mi; dS:=Se; dS1:=Se1;
          Time:=(3600.0*dH +60.0*dM +dS +0.01*dS1); end;
           {########################################}
begin
itnum:=30;

WriteLn('###############       ','   Test of PC ,DOS','    ###############');
WriteLn('Time for PC 486        133 Mhz, Real   = ',16.53:15:9,'  Sec');
WriteLn('Time for PC PENTIUM    150 Mhz, Real   = ', 5.93:15:9,'  Sec');
WriteLn('Time for PC PENTIUM(1) 166 Mhz, Real   = ', 5.10:15:9,'  Sec');
WriteLn('Time for PC PENTIUM-2  233 Mhz, Real   = ', 4.61:15:9,'  Sec');
WriteLn('Time for PC Celeron    400 Mhz, Real   = ', 2.74:15:9,'  Sec');
WriteLn('Time for PC PENTIUM    225 Mhz, Real   = ', 2.69:15:9,'  Sec');
WriteLn('Time for PC Celeron    333 Mhz, Real   = ', 3.24:15:9,'  Sec');

WriteLn('Time for PC 486        133 Mhz, Protect= ',21.69:15:9,'  Sec');
WriteLn('Time for PC PENTIUM    150 Mhz, Protect= ',12.69:15:9,'  Sec');
WriteLn('Time for PC PENTIUM(1) 166 Mhz, Protect= ',11.64:15:9,'  Sec');
WriteLn('Time for PC PENTIUM-2  233 Mhz, Protect= ',10.11:15:9,'  Sec');
WriteLn('Time for PC Celeron    400 Mhz, Protect= ', 5.66:15:9,'  Sec');
WriteLn('Time for PC PENTIUM    225 Mhz, Protect= ', 5.33:15:9,'  Sec');
WriteLn('Time for PC Celeron    333 Mhz, Protect= ', 6.81:15:9,'  Sec');
WriteLn('Time for PC Athlon XP 1700 Mhz, Protect= ', 0.72:15:9,'  Sec');
WriteLn('                        Time for FREE PASCAL');
WriteLn('Time for PC Athlon    2000 Mhz, Protect= ', 0.160:15:9,'  Sec');
WriteLn('Time for PC Intel     3150 Mhz, Protect= ', 0.118:15:9,'  Sec');
WriteLn('Time for PC Intel     3300 Mhz, Protect= ', 0.113:15:9,'  Sec');
WriteLn('Time for PC Athlon XP 2800+Mhz, Protect= ', 0.150:15:9,'  Sec');

dT:=Time;
c:=0.0;
For k:=1 to itnum do begin
For j:=1 to 33 do begin
For i:=1 to 12345 do begin
c:=c +Ln(0.12*(i+j+k)) +sqrt(0.19*(i+j+k)) +exp(Ln(0.26*(i+j+k))) +sin((i+j+k)*0.34) +cos((i+j+k)*0.77);
end;
end;
end;
dT:=Time-dT;
WriteLn('Time for this PC            = ',dT/itnum:15:9,'  Sec');
Repeat Until KeyPressed;
end.
Аватара пользователя
STAKANOV
энтузиаст
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение STAKANOV »

ОС FreeBSD
P III, 866Мгц

FPC 1.0.10:

Код: Выделить всё

###############          Test of PC ,DOS    ###############
...
Time for this PC            =     0.331333333  Sec

FPC 2.0.0:

Код: Выделить всё

###############          Test of PC ,DOS    ###############
...
Time for this PC            =     0.318666667  Sec


Сначала порадовался потом позапускав несколько раз и получив очень(!!!) разные результаты понял, что тест не объективен для многозадачных систем.
PVOzerski
постоялец
Сообщения: 109
Зарегистрирован: 19.05.2005 13:45:10
Откуда: СПб
Контактная информация:

Сообщение PVOzerski »

Вы бы asm-код сравнили, что ли... Благо, можно попросить компилятор его сохранить.
anotherche

Сообщение anotherche »

Сначала порадовался потом позапускав несколько раз и получив очень(!!!) разные результаты понял, что тест не объективен для многозадачных систем.

Конечно, все это понятно. Но вся эта многозадачность, она же управляема. Выключаешь все, что мешает и вперед. Ошибка конечно будет всегда, но не катастрофичная. У меня она никогда не была хуже чем во втором знаке. Так что разницу в 20%, а тем более в 2 раза видно очень хорошо.
Судя по Вашему результату наблюдается тенденция - чем длиннее конвеер у процессора тем больше падение производительности.

Вы бы asm-код сравнили, что ли... Благо, можно попросить компилятор его сохранить


Сравнил. Я, честно говоря, с ассемблером не на ты, но случай кажется простым. Отличия небольшие. Мне кажется, что основная разница из-за того, что в случае fpc_2.0.0 в конце цикла вставляется команда wait. Как я понял она ждет пока сопроцессор чего-то там доделает (хотя вроде как эта команда означает разное для старых и новых процессоров и у новых это что-то связанное с исключениями).
Ну вот пример (он не для исходного кода, а для кода попроще, для наглядности). Это кусок кода, соответсвующий только циклу

Код: Выделить всё

For j:=1 to 10000 do begin
For i:=1 to 10000 do begin
c:=c +Ln((i+j)*0.001);
end;
end;


ассемблер, выданный fpc_1.0.6 (десятой у меня не было на тот момент)

Код: Выделить всё

;[9] For j:=1 to 10000 do begin
   movl   $1,_J
   .balign 4,144
.L8:
; Register %eax allocated
; [10] For i:=1 to 10000 do begin
   movl   $1,_I
   .balign 4,144
.L13:
; Register %eax allocated
; [11] c:=c +Ln((i+j)*0.001);
   movl   _I,%eax
   addl   _J,%eax
   pushl   %eax
; Register %eax released
   fildl   (%esp)
; Register %edi allocated
   popl   %edi
; Register %edi released
   fldt   .L18             ; это коэффициент 0.001
   fmulp   %st,%st(1)
   fldln2
   fxch
   fyl2x
   faddl   _C
   fstpl   _C
; Register %eax allocated
   cmpl   $10000,_I
   jge   .L12
   incl   _I
   jmp   .L13
; Register %eax released
.L12:
; Register %eax allocated
   cmpl   $10000,_J
   jge   .L7
   incl   _J
   jmp   .L8


а вот то, что дал fpc_2.0.0

Код: Выделить всё

; [9] For j:=1 to 10000 do begin
   movl   $1,U_P$MYTEST_J
   decl   U_P$MYTEST_J
   .balign 4
.L8:
   incl   U_P$MYTEST_J
; [10] For i:=1 to 10000 do begin
   movl   $1,U_P$MYTEST_I
   decl   U_P$MYTEST_I
   .balign 4
.L11:
   incl   U_P$MYTEST_I
; Register %eax allocated
; [11] c:=c +Ln((i+j)*0.001);
   movl   U_P$MYTEST_I,%eax
   addl   U_P$MYTEST_J,%eax
; Temp -4,4 allocated
   movl   %eax,-4(%ebp)
; Register %eax released
   fildl   -4(%ebp)
; Temp -4,4 released
   fldt   _$PROGRAM$_L14     ; это коэффициент 0.001
   fmulp   %st,%st(1)
   fldln2
   fxch
   fyl2x
   faddl   U_P$MYTEST_C
   fstpl   U_P$MYTEST_C
   fwait                                            ; а вот он wait
   cmpl   $10000,U_P$MYTEST_I
   jl   .L11
   cmpl   $10000,U_P$MYTEST_J
   jl   .L8


видны и другие отличия (правда мне думается они не принципиальные), и вот почему я грешу на wait. Приведенный код работает с одинаковой скоростью. И если вместо логарифма поставить синус, косинус или корень тоже все работает с одинаковой скоростью. Но вот если взять экспоненту, тут то все и начинается. Скорость падает почти вдвое. Экспонента, в отличие от остальных функций там вызывается не прямо (не как операция процессора), а как подпрограмма (где-то зашитая в freepascal), так что ее пришлось смотреть через IDA. Так вот, там видно, что опять таки отличие кода выдаваемого 106 от 200 заключается в инструкциях wait, их там несколько (кажется три, сейчас не помню точно).
Кто понимает? Действительно ли дело в wait-ах? Зачем fpc2.0.0 добавляет их в код? Может ли fpc компилировать свой asm листинг? (хочется проверить - убрать wait-ы из листинга сделанного в 2.0.0 и скомпилировать результат)
Илья Аввакумов
новенький
Сообщения: 50
Зарегистрирован: 04.05.2005 15:06:42
Откуда: Екатеринбург

Сообщение Илья Аввакумов »

В древности (8088) команда привыполнении инструкции fwait сопроцессор ждал, когда основной процессор будет готов принять данные от сопроцессора (не уверен, так как воспроизвожу по памяти, а было это ой как давно) Я был уверен, что сейчас эта команда не используется.
anotherche

Сообщение anotherche »

В древности (8088) команда привыполнении инструкции fwait сопроцессор ждал, когда основной процессор будет готов принять данные от сопроцессора (не уверен, так как воспроизвожу по памяти, а было это ой как давно) Я был уверен, что сейчас эта команда не используется.

по уточненным сведениям она теперь используется как запрос на возникновение исключения при вещественных вычислениях . То есть в принципе компилятор прав, он предлагает код который в случае чего вызовет исключение сразу после его возникновения (иначе это происходит при следующем вещественном вычислении, а оно может произойти очень далеко от точки ошибки), но опять же он не прав :) ведь в этом цикле вещественные вычисления идут подряд друг за другом и по большому счету не так уж и важно чтобы об ошибке стало известно прямо в срок. Эту проверку можно отложить вообще до конца цикла :). А еще лучше чтобы у компилятора была опция . Я вот сейчас вспомнил что в более привычном для меня MSVC были подобные опции, почитал msdn и точно - у них есть три режима компиляции вещественных вычислений - fast, precise (по умолчанию) и strict (вот там то компилятор и добавляет wait инструкции после каждого вещественного вычисления). Может у fpc и так есть что-то подобное? (я вообще-то паскалем занимаюсь очень эпизодически, только когда шеф попросит, так что не уверен). А если таких опций у fpc нет, то не плохо бы разработчикам их добавить. Я так думаю :)
PVOzerski
постоялец
Сообщения: 109
Зарегистрирован: 19.05.2005 13:45:10
Откуда: СПб
Контактная информация:

Сообщение PVOzerski »

ИМХО, это стоило бы с переводом на аглицкий переслать в FPC developers Mailing List (официальный). Может, кто займется? А то мне переводить некогда :)
anotherche

Сообщение anotherche »

Все, я разобрался в вопросе. Проверил напрямую - собрал из ассемблера два варианта с wait'ами и без них. И оказалось... что wait здесь ни при чем :). А виновата одна инструкция в коде вычисления экспоненты. Впрочем эта инструкция тоже связана с обработкой исключений.
Вот экспонента, которую делает fpc_106

Код: Выделить всё

fn_exp proc
    push    ebp
    mov     ebp,esp
    fld     tbyte ptr [ebp+8]
    fldl2e
    fmulp   st(1),st
    fstcw    tmpcw1
    fstcw   tmpcw2
    and     tmpcw2,0F3FFh
    or       tmpcw2,400h
    fldcw   tmpcw2
    fld       st(0)
    frndint
    fldcw   tmpcw1
    fxch    st(1)
    fsub    st,st(1)
    f2xm1
    fld1
    faddp   st(1),st
    fscale
    fstp      st(1)
    jmp     loc_004027A5
loc_004027A5:
    leave
    ret     0Ch
fn_exp endp


А вот fpc_200

Код: Выделить всё

fn_exp proc
    push    ebp
    mov     ebp,esp
    sub     esp,0Ch
    fld     tbyte ptr [ebp+8]
    fldl2e
    fmulp   st(1),st
    fstcw   tmpcw1
    fstcw   tmpcw2
    wait
    and     tmpcw2,0F3FFh
    or       tmpcw2,400h
    fldcw   tmpcw2
    fld     st(0)
    frndint
    fldcw   tmpcw1
    fxch    st(1)
    fsub    st,st(1)
    f2xm1
    fld1
    faddp   st(1),st
    fscale
    fstp    st(1)
    fclex
    jmp     loc_004027A5
loc_004027A5:
    leave
    ret     0Ch
fn_exp endp


Инструкция fclex очищает флаги исключений в регистре Status Word и очищение это как видно отнимает нимало производительности (на p4 замедление счета в 2 раза). Похоже, что в 2.0.0 просто перестраховываются при расчете экспоненты.
Ответить