понедельник, января 06, 2014

Delphi. Ненависть 2.

В посте Delphi. Ненависть. я показал как деградировал кодогенератор дельфей. Сегодня покажу еще раз, только уже с кодом на котором проблема воспроизводится.

Итак, код:

program inline_bug;

{$APPTYPE CONSOLE}

{$OPTIMIZATION ON}
{$INLINE ON}

{$R *.res}

uses
SysUtils;

Type

Utils = Class

Type

Namespace = Class

Class Function CompareString(Const ALeft, ARight : String) : Integer; Static; Inline;
Class Function CompareText(Const ALeft, ARight : String) : Integer; Static; Inline;

Class Function SameString(Const ALeft, ARight : String; ACaseSensitive : Boolean = True) : Boolean; Static; Inline;

End;

End;

{ Utils.NameSpace }

//
Class Function Utils.Namespace.CompareString(const ALeft, ARight : System.String) : Integer;
Begin

Result := SysUtils.CompareStr(ALeft, ARight);

End;
//

//
Class Function Utils.Namespace.CompareText(Const ALeft, ARight : String) : Integer;
Begin

Result := SysUtils.CompareText(ALeft, ARight);

End;
//

//
Class Function Utils.Namespace.SameString(Const ALeft, ARight : String; ACaseSensitive : Boolean) : Boolean;
begin

If ACaseSensitive Then
Result := Utils.Namespace.CompareString(ALeft, ARight) = 0
Else
Result := Utils.Namespace.CompareText(ALeft, Aright) = 0;

End;
//

Var

s1, s2 : String;

begin

Utils.Namespace.CompareString(S1, S2);
Utils.Namespace.SameString(S1, S2);
s1 := '';

end.


Теперь выхлопы разных версий компиляторов:



Delphi 2006 - XE:
-----------------------------------------------------------------
inline_bug.dpr.68: Utils.Namespace.CompareString(S1, S2);
0040915A 8B1524E24000 mov edx,[$0040e224]
00409160 A120E24000 mov eax,[$0040e220]
00409165 E8F2C7FFFF call CompareStr
inline_bug.dpr.69: Utils.Namespace.SameString(S1, S2);
0040916A 8B1524E24000 mov edx,[$0040e224]
00409170 A120E24000 mov eax,[$0040e220]
00409175 E8E2C7FFFF call CompareStr
0040917A 85C0 test eax,eax
0040917C 0F94C0 setz al

Delphi XE2 Update 4 (16.0.4429.46931):
-----------------------------------------------------------------
inline_bug.dpr.68: Utils.Namespace.CompareString(S1, S2);
0041B3AB 8D45EC lea eax,[ebp-$14]
0041B3AE 8B15D82E4200 mov edx,[$00422ed8]
0041B3B4 E84BA4FEFF call @UStrLAsg
0041B3B9 8D45E8 lea eax,[ebp-$18]
0041B3BC 8B15DC2E4200 mov edx,[$00422edc]
0041B3C2 E83DA4FEFF call @UStrLAsg
0041B3C7 8B55E8 mov edx,[ebp-$18]
0041B3CA 8B45EC mov eax,[ebp-$14]
0041B3CD E8F28FFFFF call CompareStr
inline_bug.dpr.69: Utils.Namespace.SameString(S1, S2);
0041B3D2 8D45E4 lea eax,[ebp-$1c]
0041B3D5 8B15D82E4200 mov edx,[$00422ed8]
0041B3DB E824A4FEFF call @UStrLAsg
0041B3E0 8D45E0 lea eax,[ebp-$20]
0041B3E3 8B15DC2E4200 mov edx,[$00422edc]
0041B3E9 E816A4FEFF call @UStrLAsg
0041B3EE 8D45DC lea eax,[ebp-$24]
0041B3F1 8B55E4 mov edx,[ebp-$1c]
0041B3F4 E80BA4FEFF call @UStrLAsg
0041B3F9 8D45D8 lea eax,[ebp-$28]
0041B3FC 8B55E0 mov edx,[ebp-$20]
0041B3FF E800A4FEFF call @UStrLAsg
0041B404 8B55D8 mov edx,[ebp-$28]
0041B407 8B45DC mov eax,[ebp-$24]
0041B40A E8B58FFFFF call CompareStr
0041B40F 85C0 test eax,eax
0041B411 0F94C0 setz al

Delphi XE5 Update 2 (19.0.14356.6604):
-----------------------------------------------------------------
inline_bug.dpr.68: Utils.Namespace.CompareString(S1, S2);
0041B3F4 8D45EC lea eax,[ebp-$14]
0041B3F7 8B15BC2E4200 mov edx,[$00422ebc]
0041B3FD E85EACFEFF call @UStrLAsg
0041B402 8D45E8 lea eax,[ebp-$18]
0041B405 8B15C02E4200 mov edx,[$00422ec0]
0041B40B E850ACFEFF call @UStrLAsg
0041B410 33C0 xor eax,eax
0041B412 55 push ebp
0041B413 684AB44100 push $0041b44a
0041B418 64FF30 push dword ptr fs:[eax]
0041B41B 648920 mov fs:[eax],esp
0041B41E 8B55E8 mov edx,[ebp-$18]
0041B421 8B45EC mov eax,[ebp-$14]
0041B424 E8DB79FFFF call CompareStr
0041B429 8945CC mov [ebp-$34],eax
0041B42C 33C0 xor eax,eax
0041B42E 5A pop edx
0041B42F 59 pop ecx
0041B430 59 pop ecx
0041B431 648910 mov fs:[eax],edx
0041B434 6851B44100 push $0041b451
0041B439 8D45EC lea eax,[ebp-$14]
0041B43C E803A9FEFF call @UStrClr
0041B441 8D45E8 lea eax,[ebp-$18]
0041B444 E8FBA8FEFF call @UStrClr
0041B449 C3 ret
0041B44A E901A0FEFF jmp @HandleFinally
0041B44F EBE8 jmp $0041b439
inline_bug.dpr.69: Utils.Namespace.SameString(S1, S2);
0041B451 8D45E4 lea eax,[ebp-$1c]
0041B454 8B15BC2E4200 mov edx,[$00422ebc]
0041B45A E801ACFEFF call @UStrLAsg
0041B45F 8D45E0 lea eax,[ebp-$20]
0041B462 8B15C02E4200 mov edx,[$00422ec0]
0041B468 E8F3ABFEFF call @UStrLAsg
0041B46D 8D45DC lea eax,[ebp-$24]
0041B470 E8CFA8FEFF call @UStrClr
0041B475 8D45D8 lea eax,[ebp-$28]
0041B478 E8C7A8FEFF call @UStrClr
0041B47D 8D45D4 lea eax,[ebp-$2c]
0041B480 E8BFA8FEFF call @UStrClr
0041B485 8D45D0 lea eax,[ebp-$30]
0041B488 E8B7A8FEFF call @UStrClr
0041B48D 33C0 xor eax,eax
0041B48F 55 push ebp
0041B490 6838B54100 push $0041b538
0041B495 64FF30 push dword ptr fs:[eax]
0041B498 648920 mov fs:[eax],esp
0041B49B 8D45DC lea eax,[ebp-$24]
0041B49E 8B55E4 mov edx,[ebp-$1c]
0041B4A1 E8BAABFEFF call @UStrLAsg
0041B4A6 8D45D8 lea eax,[ebp-$28]
0041B4A9 8B55E0 mov edx,[ebp-$20]
0041B4AC E8AFABFEFF call @UStrLAsg
0041B4B1 33C0 xor eax,eax
0041B4B3 55 push ebp
0041B4B4 68EBB44100 push $0041b4eb
0041B4B9 64FF30 push dword ptr fs:[eax]
0041B4BC 648920 mov fs:[eax],esp
0041B4BF 8B55D8 mov edx,[ebp-$28]
0041B4C2 8B45DC mov eax,[ebp-$24]
0041B4C5 E83A79FFFF call CompareStr
0041B4CA 8945C4 mov [ebp-$3c],eax
0041B4CD 33C0 xor eax,eax
0041B4CF 5A pop edx
0041B4D0 59 pop ecx
0041B4D1 59 pop ecx
0041B4D2 648910 mov fs:[eax],edx
0041B4D5 68F2B44100 push $0041b4f2
0041B4DA 8D45DC lea eax,[ebp-$24]
0041B4DD E862A8FEFF call @UStrClr
0041B4E2 8D45D8 lea eax,[ebp-$28]
0041B4E5 E85AA8FEFF call @UStrClr
0041B4EA C3 ret
0041B4EB E9609FFEFF jmp @HandleFinally
0041B4F0 EBE8 jmp $0041b4da
0041B4F2 837DC400 cmp dword ptr [ebp-$3c],$00
0041B4F6 0F9445CB setz byte ptr [ebp-$35]
0041B4FA 33C0 xor eax,eax
0041B4FC 5A pop edx
0041B4FD 59 pop ecx
0041B4FE 59 pop ecx
0041B4FF 648910 mov fs:[eax],edx
0041B502 683FB54100 push $0041b53f
0041B507 8D45E4 lea eax,[ebp-$1c]
0041B50A E835A8FEFF call @UStrClr
0041B50F 8D45E0 lea eax,[ebp-$20]
0041B512 E82DA8FEFF call @UStrClr
0041B517 8D45DC lea eax,[ebp-$24]
0041B51A E825A8FEFF call @UStrClr
0041B51F 8D45D8 lea eax,[ebp-$28]
0041B522 E81DA8FEFF call @UStrClr
0041B527 8D45D4 lea eax,[ebp-$2c]
0041B52A E815A8FEFF call @UStrClr
0041B52F 8D45D0 lea eax,[ebp-$30]
0041B532 E80DA8FEFF call @UStrClr
0041B537 C3 ret
0041B538 E9139FFEFF jmp @HandleFinally
0041B53D EBC8 jmp $0041b507
-----------------------------------------------------------------


Добавить тут нечего - леденящий душу пиздец :( Голосуем за багу в QC#121566 Кстати, баги описанные в прошлом посте значатся под номерами QC#121483 и QC#121484.



Ради интереса попробовал откомпилировать код на FreePascal 2.7.1. Результат:



inline_bug.dpr:68                 Utils.Namespace.CompareString(S1, S2);
00401535 8b1510604100 mov 0x416010,%edx
0040153B a100604100 mov 0x416000,%eax
00401540 e82bbc0000 call 0x40d170
inline_bug.dpr:69 Utils.Namespace.SameString(S1, S2);
00401545 8b1510604100 mov 0x416010,%edx
0040154B a100604100 mov 0x416000,%eax
00401550 e81bbc0000 call 0x40d170
00401555 85c0 test %eax,%eax
00401557 0f94c0 sete %al


 



Устыдись абракадабра пред бесплатным компилятором.

30 комментариев:

Николай Зверев комментирует...

проголосовал

Kazantsev Alexey комментирует...

Спасибо. Жаль что это сделали не все прочитавшие.

Arioch, the комментирует...

в xe2 много хорошего. Еще пример: http://synopse.info/forum/viewtopic.php?pid=3256#p3256

А чуть выше - как оно "оптимизирует" деление на 2, 4, etc

Но - объективности ради - а кто-нибудь это гонял в профайлере? может быть в современных процессорах это реально окажется не хуже ?

Kazantsev Alexey комментирует...

Я, как раз, в дизассемблер-то полез от того, что у меня показатели эталонного теста грохнулись с переходом от XE2 к XE5. В дизассемблер глянул, а там такое...

Arioch, the комментирует...

Мне тут бросилась в глаза в последней портянке TRY. (кстати, дисасм делался по умолчанию, или с Use Debug .DCUs?). Попробую я из мухи восстановить слона.

Я предположу, что последняя портянкa связана вовсе не с кодогенератором. Не стреляйте в пианиста, такую уж ему партитуру дали.

Сразу скажу, я остановился на XE2 и дальнейших извращений не видл, так что просто попытаюсь экстраполировать.

Ещё раз: всё это чисто спекуляции, попытка попасть пальцем в небо.

1) некогда, что-то в районе 2007, Borland решила перейти от NIH Syndrome к активному использованию FLOSS наработок. В частности, чтобы ускорить и без того быстрые в Delphi строки, были всосаны FastMM и FastCode

2) Однако в XE2, в связи с реализацией (как некоторые тролли говорят, цельнотянутой) x64 и планами на ARM, была начата работа по переписыванию системных функций обратно на Паскаль. Можно бы, конечно, и дописать оптимизированные реализаций x64 и ARM, но кто это убдет делать, для разругавшегося с опенсорсниками теряющими рынок эмбаркадеровцами ?

3) как мы помним на примере AnyDAC, начиная с XE4 политика дописывания паскаля сменилась политикой жесткого уждаления всех остатоков ассемблера. Во имя Delphi/LLVM. Предположу, что все string-, pointers-, heap related функции из FastCode в XE4/XE5 отсутствуют.

4) Но мы также знаем, что 90% работы FireMonkey - это передача, копировнаие и поиск строк. А также, что обезьяна теперь - на самом главном флаге.

5) Соотвественно скорость обезьяны просела ниже приличного даже по меркам EMBT. И генерал всех обезьян сказал: "ничего не знаю, но чтобы к утру было сделано!"

6) А что можно сделать, особенно не обладая не временем ни опытом ? Вернуть FastCode генерал запрещает. Использовать оптимизирующий компилятор LLVM/x86 нельзя - эту инновацию ещё за отдельные бабки продавать. Остаётся ковровый inline.

7) версия (пальцем в небо): имеющийся в последней портянке TRY - это раскрытая системная функция типа _UStrCler или _LStrCopy или какая-то такая.

А уж кодогенератор честно выдал, что ему в RTL написано.

Kazantsev Alexey комментирует...

> Мне тут бросилась в глаза в последней портянке TRY. (кстати, дисасм делался по умолчанию, или с Use Debug .DCUs?).

Да, там есть try. Но это то, что генерирует компилятор. Я специально после вызова функций добавил очистку строки, которая служила маркером. Так что все честно :) Использование Use Debug DCU на результат не влияет.

Вообще, в данном коде компилятору сразу известно чем заменять каждый из вызовов. Оба вызова сводятся к SysUtils.CompareStr. Вся разница между ними в том, что первый вызов вложен в inline-функцию, а у второго вложенность еще раз увеличена. Вот это обстоятельство и сносит крышу компилятору, что демонстрирует размер кода для первого вызова и для второго. Если же мы сделаем самый обычный вызов SysUtils.CompareStr с теми-же самыми параметрами, то сгенерированный код будет самым обычным:
Project1.dpr.70: SysUtils.CompareStr(s1, s2);
0041B549 8B15C02E4200 mov edx,[$00422ec0]
0041B54F A1BC2E4200 mov eax,[$00422ebc]
0041B554 E8AB78FFFF call CompareStr

То есть это не результат отказа в RTL от привлеченнго кода, тем более что он никуда не делся :)

Arioch, the комментирует...

а ты остальные опции не пробовал намертво задать в исходниках? ctrl+O,O

разные там generate stack frames и прочее.

Другое объяснение, что они просто все свящанные с дженериками глюки и ICE "исправляли" отключая оптимизации одну за другой. Т.е. в XE поверх AST для той портянки проходили еще оптимизаторы, выкидывая redundant код, но иногда с ошибками. И чтобы эти ошибки исключить - исключии сами оптимизаторы. LLVM и сам спотимизируеть, а DCC никому уже не нужен...

Arioch, the комментирует...

Кстати, хороший оптимизатор бфыы в первом примере и

0040917A 85C0 test eax,eax
0040917C 0F94C0 setz al

выкинул нафиг...

впрочем, на фоне XE5 это уже смешные придирки.

Kazantsev Alexey комментирует...

>а ты остальные опции не пробовал намертво задать в исходниках?

Сейчас попробовал. Та же фигня.

>Кстати, хороший оптимизатор...

Ох, когда у дельфей был хороший оптимизатор :(

Arioch, the комментирует...

FPC, однако, тоже не убрал, идентичный код выдал

Kazantsev Alexey комментирует...

FPC тоже никогда не отличался оптимизациями, там на фичи упор делают. Хотя читая лист рассылки разработчиков видно, что над этим работают :)

Kazantsev Alexey комментирует...

Кстати, фичастость FPC выгодно отличает его от дельфей, где решили все повырезать. Скоро, наверное, можно будет перефразировать рекламу: Delphi должен быть правильным. Вот FPC - правильный Delphi.

Arioch, the комментирует...

Были бы в FPC аналоги BPL (compile-time type safety в плагинах) и лаконичного синтаксиса анонимные методы (типа C++ 2011 lambda) вместо дельфийского бойлерплейта, и у DCC вообще бы преимуществ не осталось

Kazantsev Alexey комментирует...

Лично меня пока останавливает неполная поддержка имеющихся дельфийских возможностей, как перегрузка дефолтных свойств, например. Ну и еще некоторые заморочки:
http://bugs.freepascal.org/view.php?id=25011
http://bugs.freepascal.org/view.php?id=21124

Но к следующему релизу, надеюсь, это поправят.

jack128 комментирует...

>>Ради интереса попробовал откомпилировать код на FreePascal 2.7.1

на http://www.freepascal.org/download.var
The latest release is 2.6.2.
Опечатка ?

Kazantsev Alexey комментирует...

>Опечатка ?

Нет. Компилятор из транка. У них же нечетные версии это версии в разработке, а четные - релизы :)

Готовый 2.7.1 можно взять c одного из зеркал сборок Lazarus (http://mirrors.iwi.me/lazarus/snapshots/ правда тут он очень старый). А можно взять CodeTyphon (http://www.pilotlogic.com/sitejoom/) там и Lazarus и FPC транковые на момент релиза.

Arioch, the комментирует...

B G+ попросили сделать сравнение Win64 кодегена в xe2/xe5

Arioch, the комментирует...

Ну и FPC/x64 заодно, пожалуй

Kazantsev Alexey комментирует...

Ближе к вечеру сделаю.

Kazantsev Alexey комментирует...

Готово: http://kazav.blogspot.com/2014/02/delphi-2-64.html. Хотел еще попробовать мобильный компилятор, но в виртуальную машину USB почему-то не прокинулся, а ставить XE5 на рабочую желания нет.

Arioch, the комментирует...

advertized https://plus.google.com/101175546080106411874/posts/DbYDU2KaYun

thanks

Kazantsev Alexey комментирует...

В общем, повоевал я с виртуалкой и все таки запустил из-под неё отладку на устройстве. Посмотрел листинг дизассемблера. Но то-ли Delphi глючит при работе с GDB, то-ли еще что, но там такая простыня... И тоже, кстати, куча _UStrClr. Правда смущает меня тот факт, что в листинге дизассемблера точки останова дублируются (т.е. смотришь на 85-ю (условно) строку, листаешь-листаешь вниз, за ней идет 86, а потом снова 85), да и сам отладчик входит в каждую точку по два раза. Поэтому постить сюда эту простынь я поостерегся, дабы напраслину не наводить :)

Arioch, the комментирует...

Сглазил ты меня своим кодегеном... http://www.sql.ru/forum/1075936/xe2-a-vot-komu-poynterov-a-vot-hren-komu

Arioch, the комментирует...

Любопытный коммент от французский около-военных.

In our code, we tried to prevent such patterns in the pascal code which may produce such asm.
For instance, XE4 is only a few percent slower than Delphi 7.
Even when compiled with the 64 bit DCC compiler, mORMot code speed is almost the same as with Win32.

But this is mainly due to the fact that we follow our own patterns, and our own optimized string-process functions and classes, in SynCommons.pas.

http://blog.synopse.info/post/2012/12/20/How-to-make-it-fast

Kazantsev Alexey комментирует...

Я сейчас тоже выбираю путь который позволит избавиться от мусора кодогенератора. Но только без возвращаения в каменный век Delphi 7 и без переизобретания RTL :)

Arioch, the комментирует...

Популярную ты тему запустил.

Народ начал в Delphi/LLVM вглядываться

http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=1034973&msg=15555899

Arioch, the комментирует...

по слухам в xe-6 поправили этот ужас со строками, не смотрел ?

Kazantsev Alexey комментирует...

Да, в XE6 этого бага уже нет (кстати, он касался не только строк, но и вообще любых управляемых типов). Я об этом писал (http://kazav.blogspot.com/2014/03/qps.html), без привязки к версии правда :)

Arioch, the комментирует...

что они тикет закрыли я знаю, нол при чём тут сам баг ? :-D

в общем если не в лом, добавьте асм того же кода из XE6 или XE10, просто чтобы понимать насколько они исправили, а насколько просто закрыли

Kazantsev Alexey комментирует...

XE6 генерирует точно такой же код, что и версии до XE2:

Project1.dpr.66: Utils.Namespace.CompareString(S1, S2);
0041B462 8B15C02E4200 mov edx,[$00422ec0]
0041B468 A1BC2E4200 mov eax,[$00422ebc]
0041B46D E8D679FFFF call CompareStr
Project1.dpr.67: Utils.Namespace.SameString(S1, S2);
0041B472 8B15C02E4200 mov edx,[$00422ec0]
0041B478 A1BC2E4200 mov eax,[$00422ebc]
0041B47D E8C679FFFF call CompareStr
0041B482 85C0 test eax,eax
0041B484 0F94C0 setz al

Это XE6U1. На скуле, вроде бы, говорили, что в XE6 без апдейта этот баг воспроизводится.