Соответствует ли действительности то, что компилятор Object Pascal в
Delphi лучше компилятора C++ в C++Builder? Для эксперимента я создал два
проекта на С++Builder и Delphi соответственно, и всего навсего в процедуре
обрабатывающей нажатие на кноку задал пустой цикл от 1 до 1 миллиарда:
В С++Builder соответсвующая функция выглядела сл. образом:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
for (int i = 0; i < 1000000000; i++);
}
а в Delphi соответствующая процедура выглядела так:
procedure TForm1.Button1Click(Sender: TObject);
var i : integer;
begin
for i:= 0 to 1000000000 do;
end;
И что же оказывается - Программа на Delphi выполняется в 5 раза быстрее(4
сек., а на С++Builder - 20 cек.)! ??
Тогда я решил выяснить в чём разница в конечном коде, генерируемом
компиляторами C++Builder и Delphi. Для этого я просто установил точки
останова(breakpoint) напротив циклов и во время выполнения заглянул в
Debug Windows/CPU и что оказалось:
код сгенерированный компилятором С++Builder, соответсвующий пустому циклу
в ассемблерном представлении выглядит сл. образом:
xor edx, edx
mov [ebp-0x34], edx
inc dword ptr [ebp-0x34]
mov ecx, [ebp-0x34]
cmp ecx, 0x3b9aca00
jl -0x0e
А у Delphi получился такой код:
mov edx, $3b9aca00
dec edx
jnz TForm1.Button1Click + $5
Т.О. отсюда уже понятны причины почему программа на Delphi быстрее
выполняется. Помимо того что бросается в глаза большее количество команд
видно ещё принципиальное отличие - в коде первой программы в качестве
переменной-счётчика используется ячейка памяти, а компилятор Delphi
сгенерировал код в котором используется регистр процессора в качестве
счётчика. Хорошо, можно и устранить последнее отличее сл. образом:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
for (register int i = 0; i < 1000000000; i++);
}, т.е. перед переменной-счётчиком i указали спецификатор register,
предварительно в настройках компилятора разрешив использование Register
Variables(Project/Options/Advanced Compiler/Register Variables).
Действительно тогда код сгенерированный компилятором С++Builder изменится
к виду:
mov eax, 0x3b9aca00
dec eax
test eax, eax
jnle -0x05
Как видим теперь уже почти не отличается от кода сгенерированного
компилятором Delphi! За исключением одной лишней команды - test eax,
eax(зачем она нужна??) и команды jnle вместо jnz. Вот за счёт этой лишней
команды test eax, eax, кот. выполняется в цикле и увеличивается время
выполнения (на 15 сек. становится дольше). Так что же это?! Низкое
качество генерируемого кода компилятором C++Builder в сравнении с
компилятором Delphi?? Специалисты! Помогите! Проясните ситуацию. Какой же
компилятор лучше - C++Builder или компилятор Delphi?? Или возможно как-то
добиться той же эффективности кода, настроив как-то компилятор С++ в
С++Builder? Очень благодарен за ответы с пояснением!
PS Ещё заметил такой прикол, что если в С++Builder вместо просто цикла от
1 до миллиарда использовать 2 равносильных цикла, т.е. один вложенный в
другой: внешний от 1 до миллиона, а внутренний от 1 до тысячи, вот тогда
как ни парадоксально скорость выполнения 2х циклов быстрее в 5 раз чем
просто одного от 1 до миллиарда! Т.Е. вариант:
for (int i = 0; i < 1000000000; i++);
много медленее, чем вариант:
for(int i = 1; i < 1000000; i++)
for(int j = 1; j < 1000; j++);
!!?? Получается что если нам надо выполнить какие-то действия в
программе миллиард раз, нужно это сделать не в одном цикле, а задать
внешний цикл от 1 до миллиона и внутренний от 1 до тысячи, например, и в
теле внутреннего описать все действия!!??
Максим
Всего в теме 346 сообщений
Добавить свое сообщение
Отслеживать это обсуждение
- Средства разработки. Языки программирования.
- Delphi 4 or Delphi 5
- Что приобрести в качестве средства разработки?
- Delphi6
- Delphi vs PowerBuilder
- Вот и вышла Delphi 7... Вы рады?
- Функциональное программирование
№ 306 08-07-2007 22:38 | |
Однако,
Установка директивы inline дала обратный эффект.
процедура: 39 сек.
оператор: 54 сек.
оператор (iline):68 сек.
Грустно.
№ 305 06-07-2007 03:02 | |
Разница понятно откуда - передача функцией результата через стек и последующее копирование его в переменную. Хотя я вроде как-то видел в CPU view как компилятор Delphi ухитрился передать результат функции по ссылке. Возможно, это было в шейстой версии. Сейчас, на седьмой, повторить не удается.
Разница эта, естественно, тем менее заметна, чем больше делает функция. Если напрягает, то можно сделать, чтобы в функцию и обратно передавались указатели. Тогда через регистры все будет передаваться.
Еще разницы не должно быть, если операторы будут заинлайнены. D2006 ведь поддерживает inline?
Кстати, конструкции
function Add(var a,b: Vector): Vector
и
procedure Add(const a,b: Vector; Result: : Vector);
не эквивалентны. Т.е. при аналогичном коде внутри могут давать разные результаты.
С++-компиляторах операторы скорее всего просто инлайнятся, иначе было бы аналогичное снижение производительности.
№ 304 06-07-2007 01:19 | |
Ответ на »сообщение 303« (Марина)
___________________________
Вот в том и проблема, что весь этот апгрейд Delphi с перегрузкой был ни к чему, всё равно писать придёться как в Delphi 7.
Несмотря на то, что я и сам пока на семерке, но проигрыш в скорости на 30% меня нисколько не смутит, к тому же, как вам уже показали, этот проигрыш не связан с "кривым компилятором".
PS: Зато в BDS2006 шустрый менеджер памяти...
№ 303 02-07-2007 00:50 | |
Тестировать уже пробовал:
Оператор и функция дают одинаковое время, процедура на 30% быстрее.
Вот в том и проблема, что весь этот апгрейд Delphi с перегрузкой был ни к чему, всё равно писать придёться как в Delphi 7.
№ 302 02-07-2007 00:25 | |
Ответ на »сообщение 301« (Марина)
___________________________
Два варианта поставлены в неравные условия, в одном случае функция в другом процедура. Попробуй протестировать: type Vector = record
x,y,z: real;
class operator Add(var a,b: Vector): Vector;
end;
function Add(var a,b: Vector): Vector;
var Vec1,Vec2,Vec3: TVector;
Vec3:=Vec1+Vec2;
Vec3:=Add(Vec1,Vec2);
№ 301 01-07-2007 22:47 | |
Ещё над одной особенностью компиляции задумался.
type Vector = record
x,y,z: real;
class operator Add(var a,b: Vector): Vector;
end;
procedure Add(var a,b,c: Vector);
var Vec1,Vec2,Vec3: TVector;
Vec3:=Vec1+Vec2;
Add(Vec1,Vec2,Vec3);
Вариант 2 работает на 30% быстрее.
Не то что бы это странно, просто обидно, сводит на нет такую удобную возможность как перегрузка операторов.
-------------------------
П.С. А подсветка синтаксиса на форуме всё ещё под Delphi 7.
№ 300 21-06-2007 04:34 | |
Ответ на »сообщение 293« (Марина)
___________________________
На Gamedev народ тоже столкнулся с проблеммой Delphi 10 работает медленнее чем Delphi 7.
http://www.gamedev.ru/flame/forum?id=66154&page=12
Скачал варианты для Delphi и VB (С++ варианта уж нет). На моем компьютере FPS одинаковый - ~40.
Скомпилировал (D7) - тоже 40. Причем не важно, с оптимизацией и проверкой границ, или без.
Что не удивительно, если разобраться.
Если отключить вывод в GDI и очистку буферов, то FPS ~300.
Если же закомментировать все обращения к памяти (т.е. в процедуре отрисовки линии), то FPS ~1300 и оптимизация сильно влияет (300%).
Собственно алгоритм занимает менее 1% времени.
Если на с++ действительно на 25% быстрее, то значит генерируемый С++ компилятором (ну и VB за одно :)) код в 25 раза быстрее того, что генерирует Delphi при прочих равных.
Ну либо там, как обычно, сравнивали скорость шины памяти, а не кода. ;)
Что вероятнее - думайте сами.
№ 299 21-06-2007 04:04 | |
Ответ на »сообщение 297« (Geo)
___________________________
Ответ на »сообщение 295« (Антон Григорьев)
___________________________
>>> Причина состоит в том, что поскольку финальная стадия компиляции происходит во время выполнения, JIT-компилятор на этот момент уже знает, на каком типе процессора запущена программа
Что-то мне подсказывает, что "финальная стадия компиляции" займет больше времени, чем может быть выигрыш от эффективного использования особенностей конкретного компилятора
Там, где время компиляции соизмеримо с временем выполнения, никакая оптимизация смысла не имеет. А если расчеты идут часами, то имеет смысл выжимать всю возможную оптимизацию, в том числе под конкретный кристал. Даже если оптимизировать при каждом запуске, что, конечно, забавно.
№ 298 21-06-2007 03:28 | |
>>>Что-то мне подсказывает, что "финальная стадия компиляции" займет больше времени, чем может быть выигрыш от эффективного использования особенностей конкретного компилятора
В принципе возможно ускорение, если оптимизация будет проводится с учетом размера первого уровня кэша (возможно и второго) что позволит опимально иползовать текущую конвейерную архитектуру. Значительно больший выигрыш можно получить если оптимально использовать многоядерность.
№ 297 21-06-2007 03:00 | |
Ответ на »сообщение 295« (Антон Григорьев)
___________________________
>>> Причина состоит в том, что поскольку финальная стадия компиляции происходит во время выполнения, JIT-компилятор на этот момент уже знает, на каком типе процессора запущена программа
Что-то мне подсказывает, что "финальная стадия компиляции" займет больше времени, чем может быть выигрыш от эффективного использования особенностей конкретного компилятора
Добавить свое сообщение
Отслеживать это обсуждение
Дополнительная навигация: |
|