![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Ошибка в delphi 7 [D7, WinXP]
Sicilla © (2004-11-17 18:34:00)Привет всем. Постваил себе delphi 7 взамен delphi 5. delphi 5 не удалял. И теперь ошибка: при отладке F7 например в переменной i значение 2, а кажет, что там 10 или -5. Хотя считает правильно в целом.
Gero (2004-11-30 13:19:00) [1]
> не городите чепухи. посмотрите asm код этого цикла
Простите, а вы сами-то его смотрели?
В [135] все сказано, повторяться не буду.
begin...end (2004-11-30 09:44:00) [2]Дополнение к [136]:
Результат "For с Break: 30; while без Break: 60" - это при включённом Range Checking. Если Range Checking выключить, то у меня получается: "For с Break: 40; while без Break: 50". Т.е. всё равно код с while проигрывает.
begin...end (2004-11-30 09:41:00) [3]> [133] Defunct © (30.11.04 07:01)
> > Хотя даже этот код хуже примера с for.
> Он лучше хотя бы тем, что не используется break.
Очень веский аргумент.
> > + необходимость инициализации
> Плюс возможность изменения индекса в произвольном порядке
> и с произвольным шагом.
Произвольный порядок - downto к Вашим услугам. Насчёт произвольного шага - согласен, но для данного случая, когда проверяется каждый элемент массива, шаг как раз должен быть равен 1.
> > + вычисление индекса последнего элемента массива в каждой
> итерации цикла.
> не городите чепухи. посмотрите asm код этого цикла.
И что - этот asm код показывает, что для while НЕ происходит вычисление High(Arr) для каждой итерации?
И вообще - мы говорили не о вопросах производительности, а о вопросах прозрачности и лёгкости безошибочного написания. Так что asm код тут вообще ничего не докажет.
А насчёт производительности - вот, пожалуйста:
function IsError(Item: Integer): Boolean;
begin
Result := Item < 0
end;
var
I, J, K: Integer;
T1, T2: Cardinal;
Arr: array of Integer;
begin
// Заполнение массива
SetLength(Arr, 1000000);
Randomize;
for I := 0 to High(Arr) - 1 do
Arr[I] := Random(100);
Arr[High(Arr)] := -1;
// Подсчёт времени выполнения for с Break
T1 := GetTickCount;
for J := 0 to High(Arr) do
begin
if IsError(Arr[J]) then Break;
end;
T1 := GetTickCount - T1;
// Подсчёт времени выполнения while без Break
T2 := GetTickCount;
K := 0;
while (K <= High(Arr)) and not IsError(Arr[K]) do
begin
Inc(K);
end;
T2 := GetTickCount - T2;
// Результаты
ShowMessage(Format('For с Break: %d; while без Break: %d', [T1, T2]))
end.
Выполните этот код, можете запостить сюда результат.
У меня получилось вот что: "For с Break: 30; while без Break: 60".
> Думаю теперь нет смысла спорить дальше.
Вот и последуйте своему же совету.
Verg (2004-11-30 09:17:00) [4]
> для цикла вашего цикла for:
>
> for i:=0 to High(Arr) do
> begin
> if IsError(Arr[i]) then break;
> end;
>
> mov eax,[ebp-$04]
> call @DynArrayHigh
> mov esi, eax
> test esi, esi
> jl +$16
> inc esi
> xor ebx, ebx
> mov eax,[ebp-$4]
> mov eax,[eax+ebx*4]
> call IsError
> test al,al
> jnz +$4 <<--- вот он ваш break
> inc ebx
> dec esi
> jnz -$13
> -->> выход из цикла (14 команд)
Вот все тело цикла (жирным). Состоит оно из восьми команд. Содержит ровно столько же "ветвлений" условных. Но не содержит вызова call @DynArrayHigh (ветвление безусловное) на каждой итерации цикла. Оптимизатор выкинул из тела цикла инварантный код High(Arr).
Какой из циклов будет выполнен быстрее?
Defunct (2004-11-30 07:14:00) [5]Defunct © (30.11.04 07:01) [133]
Подведу итог.
Каждый break добавляет дополнительный условный jmp, а как вы знаете число конвееров в процессоре ограничено. Если сильно много условныз ветвлений, то блок предсказания ветвлений неминуемо ошибается, что приводит к сбросу конвеера и замедлению выполнения цикла.
Defunct (2004-11-30 07:01:00) [6]Gero © (29.11.04 17:59) [132]
> Непонятно только, зачем Defunct городил невесть что.
Что этой фразой вы хотели сказать?
Втянули меня в дурацкий спор, и теперь еще спрашиваете что я городил. Надо было сразу написать лично вам этот код, потому как вам было виднее, что делает ваш код с циклом for.
> Хотя даже этот код хуже примера с for.
Он лучше хотя бы тем, что не используется break.
> + необходимость инициализации
Плюс возможность изменения индекса в произвольном порядке и с произвольным шагом. Как по мне это плюс.
{B-} всегда установлен по-умолчанию.
> + вычисление индекса последнего элемента массива в каждой итерации цикла.
не городите чепухи. посмотрите asm код этого цикла.
для цикла вашего цикла for:
for i:=0 to High(Arr) do
begin
if IsError(Arr[i]) then break;
end;
mov eax,[ebp-$04]
call @DynArrayHigh
mov esi, eax
test esi, esi
jl +$16
inc esi
xor ebx, ebx
mov eax,[ebp-$4]
mov eax,[eax+ebx*4]
call IsError
test al,al
jnz +$4 <<--- вот он ваш break
inc ebx
dec esi
jnz -$13
-->> выход из цикла (14 команд)
теперь сравним с циклом while (без break который)
i := 0;
while (i <= High(Arr)) and not IsError(Arr[i]) do
begin
Inc(i);
end;
xor ebx, ebx <-- i:=0
jmp +1
inc ebx <-- inc(i)
mov eax, [ebp,-$4]
call @DynArrayHigh
cmp ebx, eax
jnle +$F
mov eax,[ebp-$4]
mov eax,[eax+ebx*4]
call IsError
test al,al
jz -$1c
--->> выход из цикла (12 команд)
Думаю теперь нет смысла спорить дальше.
Gero (2004-11-29 17:59:00) [7]Итак,
i := 0;
while (i <= High(Arr)) and not IsError(Arr[i]) do
begin
<Код>
Inc(i);
end;
Непонятно только, зачем Defunct городил невесть что.
Хотя даже этот код хуже примера с for.
Как уже было сказано, необходимость слежения за {$B} + необходимость инициализации i + вычисление индекса последнего элемента массива в каждой итерации цикла.
Gero (2004-11-29 17:53:00) [8]
> begin...end © (29.11.04 17:28)
Да, ты прав, она лишняя.
begin...end (2004-11-29 17:28:00) [9]> [128] Gero © (29.11.04 17:06)
> if Length(Arr) > 0 then
Раз уж {$B-}, то эту строку в Вашем случае всё же можно убрать, т.к. для пустого массива High(Arr) = -1.
Однако, разумеется, необходимость слежения за {$B-}/{$B+} уже сама по себе является существенным недостатком.
Gero (2004-11-29 17:07:00) [10]
> Вроде как без ошибок, при условии что
Эта строка лишняя.
Gero (2004-11-29 17:06:00) [11]
i := 0;
if Length(Arr) > 0 then
while (i <= High(Arr)) and not IsError(Arr[i]) do
begin
<Код>
Inc(i);
end;
При условии {$B-}.
Это наиболее простой код, который смог написать я для данного случая.
Вроде как без ошибок, при условии что
Здесь 7 строк.
В коде [15] их 5.
Нам приходится инициализировать переменную i и еще к тому же проверять, содержит ли массив элементы.
И неужели после этого товарищ Defunct будет утверждать, что без Break проще?
KSergey (2004-11-29 14:00:00) [12]> [126] begin...end © (29.11.04 13:17)
> А в случае, если массив Arr пуст, эта строка вызовет ошибку.
Позор мне!
Я действительно проглядел самое главное... Из-за чего я так долго "давил" (прошу прощения за термин) Defunc'а... Но так и "не выдавил", увы...
Ну да ладно ;)
begin...end (2004-11-29 13:17:00) [13]> Defunct {[115] KSergey © (29.11.04 07:46)}
Расставим точки над "i".
1. Код Gero с for + Break:
> for i := 0 to High(Arr) do
> begin
> if IsError(Arr[i]) then Break;
> <Код>
> end;
Производится проверка IsError для всех элементов массива, если массив вообще заполнен. При обнаружении элемента, для которого IsError = True, цикл заканчивается. Ошибок я тут не вижу. По крайней мере, для случая, когда Arr - это динамический массив, или это статический массив с нулевой нижней границей.
2. Ваш код с while:
> i := 0;
> CanGo:= NoError(Arr[0]);
Введение дополнительной переменной. Это хорошо?
А в случае, если массив Arr пуст, эта строка вызовет ошибку. Добавьте соответствующую проверку, - и код станет ещё более многословным. Заметьте, в коде [17] эта проверка не нужна: если массив пуст, то цикл просто не выполнится ни разу.
> while CanGo do
> begin
> <Код>
> inc(i);
> if i <High(Arr) then CanGo := NoError(Arr[i])
> else CanGo := False
> end
Почему функцией NoError не проверяется последний элемент массива?
3. Ваш код с repeat:
> i := 0
> if i <High(Arr) then
Случай, когда массив Arr состоит из одного элемента, Вы игнорируете?
> Repeat
> if not IsError(Arr[i]) then
> begin
> <Код>
> end;
> inc(i);
> Until (i>=High(Arr)) or IsError(Arr[i-1]);
Цикл дошёл до i = High(Arr). Тогда выделенное условие равно True. Следовательно, последний элемент массива не будет проверен. А ведь если этот непроверенный элемент массива дал бы IsError = False, то нужно было выполнить ещё и <Код>.
Итак, что мы имеем? Имеем ещё 2 неправильных варианта кода. Иначе говоря - лишнее подтверждение того, что безошибочный код без Break для данного случая написать сложнее, чем с Break; и того, что даже безошибочный код без Break будет выглядеть намного более громоздким, чем с Break.
kostan (2004-11-29 10:17:00) [14]вот что "гонял" (это чтоб оптимизатор не с оптимизировал:)
Memo1.clear;
for dd:=10 to 1000 do
begin
rr:=dd*100;
ff:=dd*rr;
memo1.lines.add(floattostr(ff));
end;
(это не ошибка компилятора - а глюк мат.сопроцессора-
был описан еще в книжке по DOS паскалю)
ЮЮ (2004-11-29 09:55:00) [15]>щас специально 'погонял' в Delphi7
Включи оптимизацию и она вернется. Только не ошибка это. А вот вычислять незнамо что и незнамо зачем и анализировать это в отладчике - это ошибка :)
kostan (2004-11-29 09:49:00) [16]ошибка была в Delphi5
щас специально 'погонял' в Delphi7
че-то не вылазит (ошибка) - так что простите, наверно
убрали ее
[но все равно интересно:) ]
KSergey (2004-11-29 09:44:00) [17]> [121] kostan © (29.11.04 09:37)
> чесное слово ... :0) [Borland предупреждает ;)]
О какой конкрентно версии речь?
kostan (2004-11-29 09:37:00) [18]<ЮЮ © (29.11.04 09:23) [119]
код конечно -профонация (так для примера)
<KSergey © (29.11.04 09:23)
чесное слово ... :0) [Borland предупреждает ;)]
сам ловился (хватило одного раза , просидел в отладчике
несколько часов - пока 'умный' парень не помог)
причем счет сбивается всегда с разных значений i
(а бывает что выполняется успешно )
ЮЮ (2004-11-29 09:23:00) [19]>выдаст не верный результат
И кто здесь чего выдаёт? если gg используется только для профанации, то, возможно, и будет "подсчитана неправильно", да только об этом никто не узнает.
KSergey (2004-11-29 09:23:00) [20]> [117] kostan © (29.11.04 09:12)
Может когда и было... Проверять лень, но сомнение меня разбирает...