Конференция "Прочее" » Как оптимизировать
 
  • Pavia © (29.01.17 00:24) [0]
    Есть код который разбирает БД=300 мб разбирает долго порядка 30 секунд.
    Основные тормоза приходятся на функции POS и Copy.

    function GetFirstDelim(s:String):Integer;
    begin
    Result:=0;
    Result:=Pos(';',s);
    if Result<=0 then Result:=Length(s)+1;
    end;

    procedure Separate(var s,s1:String);
    var
     p:Integer;
    begin
    p:=GetFirstDelim(s);
    s1:=s;
    s:=Copy(S1,p+1,Length(S)-p);
    SetLength(s1,p);

    //s1:=Copy(S,1,p-1);
    //s:=Copy(S,p+1,Length(S)-p);
    end;

    procedure LoadDB(Var List:TList; name:String);
    var
     f:TextFile;
     s,s1:String;
     FileDesc:PFileDesc;
     Path:String;
     SearchRec:TSearchRec;
     StringList:TStringList;
     i:Integer;
    begin
    FillChar(SearchRec,SizeOf(SearchRec),0);

    List.Clear;
    AssignFile(f,Name);
    Reset(f);
    StringList:=TStringList.Create;
    StringList.LoadFromFile(Name);
    //while not eof(f) do
    for i:=0 to StringList.Count-1 do
     begin
     //ReadLn(F,s);
     s:=StringList[i];

     Separate(s,s1);
     Path:=s1;
     Separate(s,s1);

     Separate(s,s1);
     TryStrToInt(s1,SearchRec.Attr);

     Separate(s,s1);
     TryStrToInt(s1,Integer(SearchRec.FindData.nFileSizeLow));

     Separate(s,s1);
     TryStrToInt(s1,Integer(SearchRec.FindData.nFileSizeHigh));

     GetFileDesc(FileDesc, Path, SearchRec);
     if FileDesc<>nil then
       List.Add(FileDesc);
     end;
    CloseFile(f);
    end;
  • Jeer © (29.01.17 02:09) [1]
    ну да, можно и бардак считать за базу данных.
  • DVM © (29.01.17 09:50) [2]

    > Pavia ©   (29.01.17 00:24) 


    > Основные тормоза приходятся на функции POS и Copy.
    >

    Переделай на машину состояний, не будет POS, не будет TryStrToInt, не будет кучи лишних действий которые ты совершаешь, причем многократно делая проход по одним и тем же данным, а будет один проход. Обработать 300 мег можно примерно за 1 сек.
    Выкинь всякие стринглисты, достаточно стрима, лучше TMemoryStream.
  • Pavia © (29.01.17 12:11) [3]
    Не понимаю, чем автомат мне поможет тоже чтение и тоже копирование?
    Если бы там были числовые значения или любые не текстовые, то прирост был бы. Но тут у меня основную массу составляет текст в его исконной чистой и неизменной форме.

    100 мб/с время чтения с диска. Так что за 1 секунду не уложиться.
    3 секунды чтение.
    Латентность кэша составляет 4 такта. Что даёт при чтение по байтно примерно 1٫2 секунды. Пусть запись примерно столько же и того в пределе имеем 5 секунд. Интересно в теории можно ли совместить чтение и разбор данных?

    Что касается copy, то она несколько избыточно. Но в целом это не более 2-х раз.
    Убрал излишнее копирование*. Время разбора сократилось до 12 секунд.
    POS проходит по базе ровно 1 раз. И на него приходиться 12%.

    К TryStrToInt  претензий нет она не тормозит.


    > Выкинь всякие стринглисты, достаточно стрима, лучше TMemoryStream.

    К StringList претензий особо нет кроме того, что он грузит всё в память.
    Можно выкинуть это тоже несильно ускорит секунды на 3. А вот разбор усложнит.

    *)

    procedure GetDelimsIndex(const s:string; var indexs:TArrayInteger);
    var
     tmp:Integer;
     i:Integer;
    begin
     SetLength(Indexs,BDCountFild);
     tmp:=0;
     Indexs[0]:=tmp;
     for i:=1 to BDCountFild-1 do
       begin
       tmp:=PosEx(';',s, tmp+1);
       if tmp<=0 then tmp:=Length(s)+1;
       Indexs[i]:=tmp;
       end;
    end;

    procedure SeparateRow(s:String; indexs:TArrayInteger; var goal:TArrayString);
    var
     i:Integer;
    begin
     SetLength(Goal, Length(Indexs)-1);
     for i:=1 to BDCountFild do
       begin
       //Goal[i-1]:=Copy(s,Indexs[i-1]+1, Indexs[i]-1-Indexs[i-1]);
       SetString(Goal[i-1],PChar(@s[Indexs[i-1]+1]), Indexs[i]-1-Indexs[i-1]); // Чуть быстрее чем Copy.
       end;
    end;

    procedure LoadDB(Var List:TList; name:String);
    var
     f:TextFile;
     s,s1:String;
     FileDesc:PFileDesc;
     Path:String;
     SearchRec:TSearchRec;
     StringList:TStringList;
     i:Integer;
     Row:TArrayString;
     Indexs:TArrayInteger;
    begin
    FillChar(SearchRec,SizeOf(SearchRec),0);

    List.Clear;
    AssignFile(f,Name);
    Reset(f);
    StringList:=TStringList.Create;
    StringList.LoadFromFile(Name);
    //while not eof(f) do
    for i:=0 to StringList.Count-1 do
     begin
     //ReadLn(F,s);
     s:=StringList[i];

     GetDelimsIndex(s, Indexs);
     SeparateRow(s, Indexs, Row);
     path:=Row[0];
     TryStrToInt(Row[2],SearchRec.Attr);
     TryStrToInt(Row[3],Integer(SearchRec.FindData.nFileSizeLow));
     TryStrToInt(Row[4],Integer(SearchRec.FindData.nFileSizeHigh));

     GetFileDesc(FileDesc, Path, SearchRec);
     if FileDesc<>nil then
       List.Add(FileDesc);
     end;
    CloseFile(f);
    end;


    Если есть пример подобного то неоткажите покажите свои наработки.
  • dmk © (29.01.17 13:04) [4]
    Вот для замены Copy.
    Встроенная Move медленнее чем MoveBytes в 2 раза из-за кейсов.
    Осталось видимо для совместимости. Можно копировать через FPU.
    У него выборка быстрее чем у обычных регистров. Хотя видимо от процессоров зависит.
    Только под себя подпилить надо немного. Она для 64-бит.

    procedure MoveBytes(src, dest, count: uint64);
    asm
     mov r9, rdi
     mov r10, rsi

     mov rsi, rcx //src
     mov rdi, rdx //dest
     mov rcx, r8 //count
     and r8, 03h //Чистим верхние биты и сохраняем первые 3 бита, чтобы найти остаток
     shr rcx, 2 //делим на 4, т.к. пишем двойными словами
     rep movsd
     mov rcx, r8 //пишем остаток
     rep movsb

     mov rsi, r10
     mov rdi, r9
    end;
  • dmk © (29.01.17 14:09) [5]
    Протестировал. Получилось Move 85% от 100% MoveBytes на моем 6950X + ddr4-2666.
    Но все равно быстрее на 15%. Уже профит.
  • dmk © (29.01.17 14:15) [6]
    SetLength реже вызывать. Она тормозная.
    Добавлять блоки заранее. Например так:
    У меня по крайней мере работает быстрее за счет этого.

    procedure TZObject.AddPoint(P: TZPoint);
    begin
     //Добавляем память блоками
     if (FNumPoints mod BlockSize) = 0 then
     begin
       SetLength(FPoints, FNumPoints + BlockSize);
     end;
     Inc(FNumPoints);
     FPoints[FNumPoints - 1] := P;
    end;
  • Юрий Зотов © (29.01.17 16:08) [7]
    Если "основные тормоза приходятся на функции POS и Copy", то библиотека QStrings должна решить проблему. В сети она есть.
  • Sha © (29.01.17 20:05) [8]
    F1 ReadLn
  • NoUser © (29.01.17 21:02) [9]
    Pavia,
    смотри, я научу тебя плохому! ))


    type
     PMyStrRec = ^TMyStrRec;
     TMyStrRec = packed record
     {$IF defined(CPU64BITS)}
       _Padding: Integer; // Make 16 byte align for payload..
     {$ENDIF}
       codePage: Word;
       elemSize: Word;
       refCnt: Integer;
       length: Integer;
       //
       CharPtr : PChar;
     end;

    function Next(var CurPosInBigText:PChar; var tS: TMyStrRec):PString;
    var
    c:Char;
    begin
    tS.length := 0;
    tS.CharPtr := CurPosInBigText;
    c := CurPosInBigText^;
    while ( c<>#0 ) do begin
     if ( c = #$D ) then begin
      CurPosInBigText^ := #0;
      Inc(CurPosInBigText);
      if (CurPosInBigText^ = #$A ) then Inc(CurPosInBigText);
      Break;
     end;
     if ( c = ';' ) or ( c = #$A ) then begin
      CurPosInBigText^ := #0;
      Inc(CurPosInBigText);
      Break;
     end;
     Inc(tS.length);
     Inc(CurPosInBigText);
     c := CurPosInBigText^;
    end;
    Result := @tS.CharPtr;
    end;

    function Test:string;
    const
    buff = String('c:\mydir\;bla-bla;123;1234;-123456'#$D#$A'd:\mydir\;bla_bla;321;4321;-654321');
    var
    dummy :String;
    //
    pMem  :PChar;
    pBuf  :PChar;
    tS    :TMyStrRec;
    //
    i     :Integer;
    a,b,c :array [1..2] of Integer;
    Path  :array [1..2] of String;
    //
    begin
    //                                                     // set abstract string header
    SetLength(dummy,1);
    tS := PMyStrRec( PByte(PPointer(@dummy)^) - (SizeOf(tS)-SizeOf(tS.CharPtr) ) )^;
    //

    //                                                     // get/set big buffff
    pMem := AllocMem(Length(buff) + ts.elemSize*2);
    CopyMemory(pMem, PChar(buff), Length(buff)*SizeOf(Char));
    pBuf := pMem;
    //

    //                                                     // process
    for i := 1 to 2 do begin
     SetString(Path[i], PChar(Next(pBuf,tS)^), tS.length); // 1
     Next(pBuf,tS);                                        // 2
     TryStrToInt(Next(pBuf,tS)^, a[i]);                    // 3
     TryStrToInt(Next(pBuf,tS)^, b[i]);                    // 4
     TryStrToInt(Next(pBuf,tS)^, c[i]);                    // 5
    end;
    //

    //                                                    // result test
    Result :=Path[1]+Inttostr(a[1])+Inttostr(b[1])+Inttostr(c[1])+
             Path[2]+Inttostr(a[2])+Inttostr(b[2])+Inttostr(c[2]);

    //
    FreeMem(pMem);
    end;
  • DVM © (29.01.17 21:19) [10]

    > Не понимаю, чем автомат мне поможет тоже чтение и тоже копирование?
    >  
    > Если бы там были числовые значения или любые не текстовые,
    >  то прирост был бы. Но тут у меня основную массу составляет
    > текст в его исконной чистой и неизменной форме.

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


    > 100 мб/с время чтения с диска. Так что за 1 секунду не уложиться.
    >  

    Поставь SSD. :) Я про обработку говорил. Уже загруженных данных. Ну прибавь еще на загрузку 3 секунды.


    > Латентность кэша составляет 4 такта. Что даёт при чтение
    > по байтно примерно 1٫2 секунды. Пусть запись примерно столько
    > же и того в пределе имеем 5 секунд. Интересно в теории можно
    > ли совместить чтение и разбор данных?

    Я тебе предлагаю конечный автомат. В него как раз хоть побайтно можно подавать данные кусками произвольного размера.


    > К StringList претензий особо нет кроме того, что он грузит
    > всё в память.
    > Можно выкинуть это тоже несильно ускорит секунды на 3. А
    > вот разбор усложнит.

    Не усложнит. Все твои данные могут быть разобраны путем прохода по каждому байту ОДИН раз.
  • NoUser © (29.01.17 22:37) [11]
    > Все твои данные могут быть разобраны путем прохода по каждому  байту ОДИН раз.

    да там почти так и есть,  а вот  (2хSetLength + 5xSetString) на каждую строку - это затратно.
  • DayGaykin © (29.01.17 23:09) [12]
    Как вариант, можно изменить формат хранения данных, что загружать сразу в разобранном виде.
  • Pavia © (30.01.17 01:14) [13]
    11.6 секунд

    1 pos
    1 move *2
    1 pos - второй проход

    4*4*300/700=6.8
    Плюс чтение 3 секунды=9٫8
    И всякая мелочь ещё 2 секунды.

    Выкинуть лишние POS который даёт второй проход. Плюс сразу хранить размер строки это избавит ещё от одного pos даст прирост в 2 раза.


    > Как вариант, можно изменить формат хранения данных, что
    > загружать сразу в разобранном виде.

    Это отдельный разговор. Если менять, то совсем всё.
  • Eraser © (30.01.17 08:36) [14]
    кстати, задача не сложно распараллеливается.
 
Конференция "Прочее" » Как оптимизировать
Есть новые Нет новых   [134431   +10][b:0][p:0.001]