Ruslan_blr

Forum Replies Created

Viewing 10 posts - 1 through 10 (of 10 total)
  • Author
    Posts
  • Ruslan_blr
    Participant
    Ruslan_blr
    Participant
    'TCount' wrote:

    😆 Предлагаю заканчивать дискуссию) надоело, правда) каждый решает сам, как ему программы писать.

    :wacko:

    Ruslan_blr
    Participant
    'TCount' wrote:

    В статье не написано, что это функция. Написано, что вызывается МЕТОД, возвращающий Count. Но Вы не поняли, о чем я говорил, дело не в методах и не в свойствах, а факт того, что лишние вычисления процессор проводит. На Вашем компе это не ощутимо, потому что в его проце как минимум 2 ядра, у меня одноядерный и результаты я Вам показал.

    Более того, эксперимент был проведен всего лишь на “b-1”, а не на сложной функции.

    Глупо полагать, что в цикле вроде “For a:=0 to b-1 do…” конечный итог вычисляется сразу. Цикл каждый раз вычисляет выражение “b-1”, потому как нигде не хранит в памяти результат выражения “заранее”.

    V_f0DQF6FWo.jpg

    Ruslan_blr
    Participant
    'TCount' wrote:

    Все правильно написано! Для простых выражений границы цикла вычисляются сразу. Так и написано, но в Вашем случае были неявные границы в цикле, поэтому ничего заранее не вычислялось.

    Вы сами-то задумывались, как может вот такое выражение вычислиться заранее и один раз?

    B:=1;

    FOR a:=0 TO B*2-1 DO BEGIN

    B:=a+5;

    if A>=1000 then BREAK;

    END;


    Вынесение инвариантного кода за пределы цикла – не выносится. Наиболее распространенный недочет – условие цикла записывается как:

    for i:=0 to memo1.lines.count – 1 do…

    Delphi будет при каждой итерации вызывать метод count, вычитать из результата 1 и потом уже сверять. Настоятельно рекомендуется переписывать подобный код как

    lin := .lines.count – 1;

    for i:=0 to lin do…

    Весь код VCL написан с нарушением этого правила. Очевидно, что проще подобного рода оптимизацию встроить в компилятор, нежели переписывать VCL 🙂


    Про остальное можно почитать здесь:

    http://delphist.ru/napisanie-optimalnogo-koda-pod-delphi/

    в статье про циклы написан БРЕД.

    count – это НЕ функция кот. возвращает кол-во , это СВОЙСТВО.

    вот кусок кода из System.Classes:

    Code:
    TStrings = class(TPersistent)
    protected

    function GetCount: Integer; virtual; abstract; // виртуальная, абстрактная – реализация в наследнике, т.е. в TStringList, т.к. my_TStrings:= TStringList.Create

    public

    property Count: Integer read GetCount;

    TStringList = class(TStrings)

    protected

    function GetCount: Integer; override;

    смотрим ее реализацию

    Code:
    function TStringList.GetCount: Integer;
    begin
    Result := FCount; // возвращает значение приватного поля, а не подсчитывает кол-во.
    end;

    FCount меняется в функциях типа Add, Delete и т.д.

    но это в принципе не очень важно, факт в том, что

    Quote:
    Delphi будет при каждой итерации вызывать метод count, вычитать из результата 1 и потом уже сверять

    полный бред. ну проведите же вы эксперименты

    Ruslan_blr
    Participant
    'TCount' wrote:

    А Вы переводили это?

    “…в заявлении является то, что в то время как цикл переоценивает finalValue до

    каждая итерация. Это может привести к снижению производительности заметно, если finalValue является сложным выражение, и это также означает

    что изменения в стоимости finalValue в заявлении может повлиять на выполнение цикла.”


    конечно, мы не говорим о сложных конструкциях, но нужно понимать, что финальное значение вычисляется во время цикла постоянно. Возникает вопрос “а нафиг мне нужно это лишние вычисление?”.

    пройдите по ссылке и найдите этот текст.

    переведу весь блок:

    Quote:
    For purposes of controlling the execution of the loop, the expressions initialValue and finalValue are evaluated only once, before the loop begins. Hence, the for…to statement is almost, but not quite, equivalent to this while construction:

    Code:
    begin
    counter := initialValue;
    while counter <= finalValue do
    begin
    … {statement};
    counter := Succ(counter);
    end;
    end.

    The difference between this construction and the for…to statement is that the while loop reevaluates finalValue before each iteration. This can result in noticeably slower performance if finalValue is a complex expression, and it also means that changes to the value of finalValue within statement can affect the execution of the loop.

    перевод

    Quote:
    Для целей управления выполнением цикла, выражения InitialValue и finalValue оцениваются только один раз, перед началом цикла.

    Таким образом, конструкция for..to почти, но не совсем, эквивалентна этой конструкции while:

    Code:
    begin
    counter := initialValue;
    while counter <= finalValue do
    begin
    … {statement};
    counter := Succ(counter);
    end;
    end.

    Разница между этой конструкции и конструкцией for..to является то, что цикл while переоценивает finalValue перед каждой итерации. Это может привести к заметно более низкой производительности, если finalValue является сложным выражением, и это также означает, что изменения значения finalValue в выражении может повлиять на выполнение цикла.

    а теперь осмыслите то, что я написал.

    Quote:
    А у меня результаты:

    1139

    1658

    я думаю эта разница в примерно 500 тиков уходит на одноразовое вычисление значения конечного значения счетчика цикла (b-1)

    сделайте так

    Code:
    procedure Test;
    var
    i, j: Cardinal;
    a, b : Integer;
    begin
    i:= GetTickCount;
    b:= 1000000000;
    for a := 0 to b do
    begin
    if (a=0) then i:= GetTickCount; // в кол-ве тиков не будет значения, затраченного на выч-е конечного значения счетчика цикла

    end;

    j:= GetTickCount – i;
    ShowMessage( IntToStr( j ) );

    b:= 1000000001;
    for a := 0 to b-1 do
    begin
    if (a=0) then i:= GetTickCount;

    end;

    j:= GetTickCount – i;

    ShowMessage( IntToStr( j ) );

    end;

    как не парадоксально, но

    выполняя 1-ый раз

    1969 и 1938,

    а 2-й

    1938 и 1984.

    я думаю тут точно не вычислишь, т.к. особенности реализации GetTickCount

    Ruslan_blr
    Participant

    емае я в шоке с Вас.. серьезно… я уже было начал сомневаться в своей адекватности …) проведите у себя эксперимент )))

    Code:
    var
    i, j: Cardinal;
    a, b : Integer;
    begin
    i:= GetTickCount;
    b:= 1000000000;
    for a := 0 to b do
    begin

    end;

    j:= GetTickCount – i;
    ShowMessage( IntToStr( j ) );

    i:= GetTickCount;
    b:= 1000000001;
    for a := 0 to b-1 do
    begin

    end;

    j:= GetTickCount – i;

    ShowMessage( IntToStr( j ) );

    end;

    результаты будут одинаковыми. у меня например 1688 И 1688. Это ж блин основы программирования.

    или например так

    Code:
    var
    a: Integer;

    st: TStrings;
    begin
    st:= TStringList.Create;
    st.Add('a');
    st.Add('b');
    st.Add('c');
    st.Add('d');
    st.Add('e');
    st.Add('f');
    st.Add('g');
    st.Add('h');

    // count = 8

    for a := 0 to st.Count-1 do // цикл идет до 7-ми
    begin
    ShowMessage( st.Strings[a] );

    if a = 2 then st.Delete(a); // удалил элемент. count = 7
    end;
    // на последнем шаге (a=7, но не как не st.Count-1, т.е 6 ) вывалится Exception class EStringListError with message 'List index out of bounds (7)'.

    end

    еще нужны доказательства ?

    Ruslan_blr
    Participant
    'TCount' wrote:

    Как раз я и прав)

    1. b:=10000;For a:=0 to b-1 do…

    2. b:=9999;For a:=0 to b do…

    Как думаете, что быстрее? Смысл не в том, когда цикл прерывается, а вообще в логике построения.

    Этот форум для абсолютно всех и все могут отвечать на те или иные вопросы. Просмотрите форум, не думаете же Вы, что на все вопросы отвечает поддержка?)

    Когда отвечает поддержка, то и ник у нее соответствующий)

    подтвержу документально. вот выдержка из офф. доки ембаркадеро ( http://docwiki.embarcadero.com/RADStudio/XE5/en/Declarations_and_Statements)

    For purposes of controlling the execution of the loop, the expressions initialValue and finalValue are evaluated only

    once, before the loop begins.

    The difference between this construction and the for… to statement is that the while loop reevaluates finalValue before

    each iteration. This can result in noticeably slower performance if finalValue is a complex expression, and it also means

    that changes to the value of finalValue within statement can affect the execution of the loop.

    Ruslan_blr
    Participant
    'TCount' wrote:

    В конструкции типа “for I := 0 to TitleBar_Main.Items.Count-1 do” каждый шаг переменной I вычисляется значение “TitleBar_Main.Items.Count-1”, а это лишняя операция, особенно если итемов много.

    Вы не правы ! в моей процедуре закомменте Break и после if then begin … end поставьте например if i=2 then TitleBar_Main.Items.Delete(i); count естестно уменьшится на -1 а цикл пойдет до конца и на последней итерации будет Argument out of range. считайте,что конечное зн-е счетчика цикла это константа и оно высчитывается только ОДИН РАЗ перед первой итерацией. это ж азы ёмаё )).

    а по поводу вылета… если это вопрос к разработчику, то кто тогда Вы ? смотрящий что-ли ?))

    Ruslan_blr
    Participant
    'TCount' wrote:

    У меня привычка оптимизировать)) В данном случае эта оптимизация не сильно влияет на общий результат, но если использовать ее повсеместно, то результат будет очевиден.

    Переменная введена для ускорения работы цикла, но с циклом Repeat..Until было бы еще быстрее (правда, это незаметно). Ну еще в процедуре испольщовано CONST.

    а как переменная ускоряет цикл ? конечное значение счетчика цикла вычисляется по-моему на первом шаге. тоесть если в теле цикла например удалить один из айтемов, то на последнем шаге будет что-то типа Index out of bounds. конечное значение счетчика цикла не пересчитывается каждый раз. ну и const..:) вы все по ГОСТу делаете ?)

    а по проблеме что скажете ? если компилить под win 8.1 x64 то вылетает.

    Ruslan_blr
    Participant

    интересно получилось…

    эти вылеты были на рабочем компе с Win 8.1 x64.

    ничего не меняя перекомпилил на домашнем Win7 x64 – и никаких вылетов.

    м.б. дело в этом ?

    и еще – процедура принадлежит форме ,а конкретнее сеттеру одного из её св-в(перед begin) , и TitleBar создан в дизайн-тайме. так что дело точно не в этом.

    и еще – какой смысл в предложенной оптимизации ? зачем заводить лишнюю переменную ?

    … только что скомпилил под Win7 x64 и запустил на виртуальке Win8 x64 – вылетов нет.

Viewing 10 posts - 1 through 10 (of 10 total)