пятница, 24 октября 2014 г.

ToDo. Написать как замена "ассемблера" на нормальное ООП привело к отказу от "алгоритма маляра"

ToDo. Написать как замена "ассемблера" на "нормальное" ООП (в собственной реализации IStorage) привело к отказу от "алгоритма маляра".

И в итоге к значительному приросту производительности.

Пока - "крайне коротко" - поскольку - на ассемблере достаточно сложно программировать, особенно "объекты с хранимым состоянием", то "люди до меня" писали просто и незамысловато. Чтобы "самим не запутаться".Это неплохо. Но это привело к тому, что многое было сделано в стиле "алгоритма маляра" и "выливаем воду из чайника".

Потому что "состояния хранить" было негде, а с логикой можно было и "запутаться".

НА N-й итерации рефакторинга этого кода я нашёл эти проблемы и наконец - переделал.

Это было непросто - потому что эти проблемы были скрыты "бутылочным горлышком". Даже несколькими.

В итоге проблемы были переосмыслены только тогда когда были другие проблемы найдены.

Напишу про всё. Как дойдут руки.

Там много материала. Очень показательного для "меня лично". Прям новые горизонты открываются.

P.S. Ну и "вдогонку".

Просто приведу реальный код. (15-летней давности)

В нём есть "алгоритм маляра":

 procedure   Tm3StorageStream.Seek(AOffset     : Int64;
                                   AOrigin     : TSeekOrigin;
                                   var AResult : Int64;
                                   var AReturn : HRESULT
                                  );

  procedure    __Seek(const APosition: Int64;
                      var   AResult: Int64
                     );
  var
        LCount:                   Int64;
        LPosition1:               Int64;
        LPosition2:               Int64;
        LPosition3:               Int64;
  begin

   with f_StreamHeader.TOCItemDataPtr^.RBody do
    begin

     if ((APosition >= 0) and (APosition <= RRealSize))
      then
       begin

        if (APosition = FPosition)
         then
         else
          begin

           LPosition1:=FPosition-FTOCBuffBodyOffset;

           if ((APosition >= LPosition1) and (APosition < (LPosition1+TOCBuffBodySize)))
            then
             begin

              FTOCBuffBodyOffset:=APosition-LPosition1;

             end
            else
             begin

              with f_StreamHeader.FRootStreamManager do
               begin

                SaveTOCBuffData(RTOCBuffRootPosition, f_CurFilePos,
                                FTOCBuffData^, FTOCBuffDataCompare^,
                                FTOCBuffDataModifed, ReadOnly);

                LPosition2:=Int64(-1);
                LPosition3:=RTOCBuffRootPosition;

                LCount:=APosition div FTOCBuffBodySize;

                while (LCount <> 0) do
                 begin

                  LoadTOCBuffData(LPosition2,LPosition3,LPosition2,
                                  FTOCBuffData^,FTOCBuffDataCompare^,
                                  FTOCBuffDataModifed,False);

                  LPosition3:=FTOCBuffData^.RNextPosition;

                  Dec(LCount);

                 end;

                LoadTOCBuffData(LPosition2,LPosition3, f_CurFilePos,
                                FTOCBuffData^,FTOCBuffDataCompare^,
                                FTOCBuffDataModifed,True);

                FTOCBuffBodyOffset:=APosition mod FTOCBuffBodySize;

               end;

             end;

           FPosition:=APosition;

          end;

       end
      else
       begin

        Exit;

       end;

    end;

   AResult:=APosition;

  end;

 begin

  if SUCCEEDED(AReturn)
   then
    begin

     case AOrigin of

      soBeginning: begin

                        __Seek(AOffset,AResult);

                       end;

      soCurrent: begin

                        __Seek(AOffset+FPosition,AResult);

                       end;

      soEnd: begin

                        __Seek(AOffset + f_StreamHeader.TOCItemDataPtr^.RBody.RRealSize, aResult);

                       end;

      else             begin

                        OleError(E_UNEXPECTED);

                       end;

     end;
     
    end;

 end;

Оформление кода приведено "авторское".

Это не чтобы "кого-то попинать", а просто к тому, что "все люди - меняются".

P.P.S. Что я ещё хотел сказать? А то, что "читабельность кода" очень часто влияет на его производительность.

Хотя мне известны и обратные примеры.

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

  1. Этот комментарий был удален автором.

    ОтветитьУдалить
  2. Жаль выкидывать... :-)
    После применения formatter.exe из Delphi XE7 и простейших формальных преобразований, получилось следующее:
    <code>
    procedure Tm3StorageStream.Seek(AOffset: Int64; AOrigin: TSeekOrigin; var AResult: Int64; var AReturn: HRESULT);

      procedure __Seek(const APosition: Int64; var AResult: Int64);
      var
        LCount, LPosition1, LPosition2, LPosition3: Int64;
      begin
        with f_StreamHeader.TOCItemDataPtr^.RBody do
          if (APosition >= 0) and (APosition <= RRealSize) and (APosition <> FPosition) then
            begin
              LPosition1 := FPosition - FTOCBuffBodyOffset;
              if (APosition >= LPosition1) and (APosition < (LPosition1 + TOCBuffBodySize)) then
                FTOCBuffBodyOffset := APosition - LPosition1;
              else with f_StreamHeader.FRootStreamManager do
                begin
                  SaveTOCBuffData(RTOCBuffRootPosition, f_CurFilePos, FTOCBuffData^, FTOCBuffDataCompare^, FTOCBuffDataModifed, ReadOnly);
                  LPosition2 := Int64(-1);
                  LPosition3 := RTOCBuffRootPosition;
                  LCount := APosition div FTOCBuffBodySize;
                  while LCount <> 0 do
                    begin
                      LoadTOCBuffData(LPosition2, LPosition3, LPosition2, FTOCBuffData^, FTOCBuffDataCompare^, FTOCBuffDataModifed, False);
                      LPosition3 := FTOCBuffData^.RNextPosition;
                      Dec(LCount);
                    end;
                  LoadTOCBuffData(LPosition2, LPosition3, f_CurFilePos, FTOCBuffData^, FTOCBuffDataCompare^, FTOCBuffDataModifed, True);
                  FTOCBuffBodyOffset := APosition mod FTOCBuffBodySize;
                end;
              FPosition := APosition;
              AResult := APosition;
            end;
      end;

    begin
      if SUCCEEDED(AReturn) then
        case AOrigin of
          soBeginning:
            __Seek(AOffset, AResult);
          soCurrent:
            __Seek(AOffset + FPosition, AResult);
          soEnd:
            __Seek(AOffset + f_StreamHeader.TOCItemDataPtr^.RBody.RRealSize, AResult);
        else
            OleError(E_UNEXPECTED);
        end;
    end;
    </code>

    Хотя я бы ещё избавился от with-ов, поскольку они усложняют понимание контекста.
    "Алгоритм маляра" о котором говорит Александр, вероятно связан с циклом:

    <code>
    while LCount <> 0 do
      begin
        LoadTOCBuffData(LPosition2, LPosition3, LPosition2, FTOCBuffData^, FTOCBuffDataCompare^, FTOCBuffDataModifed, False);
        LPosition3 := FTOCBuffData^.RNextPosition;
        Dec(LCount);
      end;
    </code>

    который всякий раз начинается "от корня" структуры: LPosition2 := Int64(-1).
    Возможно я ошибаюсь, но априори меня смущает любой цикл в реализации операции позиционирования :-)

    ОтветитьУдалить
    Ответы
    1. Ну да. Таки и есть.

      Про with - *тем более* - согласен.

      Удалить
    2. Написать "переделанный код"? Или "всё и так понятно"?

      Удалить
    3. «Написать "переделанный код"? Или "всё и так понятно"?»
      -- Конечно напишите - интересно.

      Удалить
    4. Завтра напишу.. Раз Вам - интересно... :-)

      Удалить
    5. Специально для вас - http://programmingmindstream.blogspot.ru/2014/10/blog-post_30.html

      Я надеюсь, что вы разглядите, что там и как.

      Удалить
    6. @NameRec.

      Можно ваше письмо опубликовать?

      Удалить
    7. «Можно ваше письмо опубликовать?»
      -- Да конечно.

      Удалить