Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.02.26;
Скачать: CL | DM;

Вниз

Как сократить время выполнения процедуры?   Найти похожие ветки 

 
Tatiana   (2006-02-04 21:59) [0]

Необходимо прочитать данные из *.txt файла. Данные структурированы. Проблема в том, что если количество строк большое (больше 10 000), то файл открывается недостаточно быстро. Каккие могут быть варианты, чтобы процедура выполнялась быстрее?


 
HITMAN ©   (2006-02-04 22:14) [1]

Попробуй в РичЕдит или процессор новый купи, побыстрее будет...


 
homm ©   (2006-02-04 22:18) [2]


>  Каккие могут быть варианты, чтобы процедура выполнялась
> быстрее?
Какая процедура? Вы не написали ничего конкретоного. Вым же нужно ускорить. А по сравнению с чем не написали.


> HITMAN ©   (04.02.06 22:14) [1]
На твою логику процесоров не напосешся.


 
ПОМОГИТЕ   (2006-02-04 22:37) [3]

Мож он на агат ХР поставил, всё обгредил, а проц ещё слабый...


 
Desdechado ©   (2006-02-04 22:51) [4]

написать быструю процедуру


 
PAVIA ©   (2006-02-05 02:56) [5]

Чтобы было быстро нужно загрузить файл в памить и там его разбирать. Проще всего через потоки TStream +F1.


 
Gero ©   (2006-02-05 03:05) [6]

TStringList работает достаточно быстро.


 
tesseract ©   (2006-02-05 15:43) [7]

tatiana - Использовать ReadBuffer, а файл открывать, как бинарный с собственным разбором CR/LF.


 
Desdechado ©   (2006-02-05 15:45) [8]

> Данные структурированы
вот это самое интересное и самое неосвещенное место в вопросе.
и кода нет
гадаем дальше?


 
Tatiana   (2006-02-05 16:14) [9]

подождите, не гадайте :) я как только смогу обязательно пошлю код и объясню подробнее.


 
Anatoly Podgoretsky ©   (2006-02-05 16:45) [10]

Объем менее одного мегабайта, любой метод загрузки будет мгновенный, хоть в StringList


 
Tatiana   (2006-02-05 20:52) [11]

Структура строки в *.txt файле:
Name         X         Y           Z           Description,
где Name и Description – String, а X, Y, Z – числа, с которыми впоследствии будут производиться математические преобразования.

У меня есть три варианта процедуры чтения файла, но по времени выполнения они не отличаются.

procedure TPoints.ReadPoints(aFileName: String);  var
   j: integer;
   s,St : String;
   Point: RPoint;
   F: TextFile;

begin
 try
   AssignFile(F, aFileName);
   Reset(F);
   While not(Eof(F)) do
     begin
       Readln(F, s);
       s := TrimLeft(s);
       s := TabToSpace(s);
       s := DotToDecSep(s);
       if s <> "" then
         begin
           St := "";
           j := 1;

           While s[j] <> " " do
             begin
               St := St + s[j];
               j := j+1;
             end;
           Point.Name := St;
           s := TrimLeft(RightStr(s, (Length(s)-Length(St))));
           St := "";
           j := 1;

           While s[j] <> " " do
             begin
               St := St + s[j];
               j := j+1;
             end;
           Point.X := StrToFloat(St);
           s := TrimLeft(RightStr(s, (Length(s)-Length(St))));
           St := "";
           j := 1;

           While s[j] <> " " do
             begin
               St := St + s[j];
               j := j+1;
             end;
           Point.Y := StrToFloat(St);
           s := TrimLeft(RightStr(s, (Length(s)-Length(St))));
           St := "";
           j := 1;

           While  s[j] <> " "  do
             begin
               if j > Length(s) then Break;
               St := St + s[j];
               j := j+1;
             end;
           Point.Z := StrToFloat(St);
           s := TrimLeft(RightStr(s, (Length(s)-Length(St))));
           St := "";
           j := 1;

           While j <= Length(s) do
             begin
               St := St + s[j];
               j := j+1;
             end;
           Point.Discription := St;
           AddPoint(Point);
         end;
     end;
 except on E:Exception do
   DoOnError (peReadError) ;
 end;
 CloseFile(F);
end;

procedure TPoints.ReadPoints(aFileName: String);  var
   j, i: integer;
   s,St : String;
   Point: RPoint;
   List: TStringList;
begin
 try
   List := TStringList.Create;
   List.LoadFromFile(aFileName);
    i := 0;
   While i <= List.Count - 1 do
     begin
       s := List[i];
       s := TrimLeft(s);
       s := DotToDecSepSpace(s);
       if s <> "" then
         begin
           St := "";
           j := 1;

           While s[j] <> " " do
             begin
               St := St + s[j];
               j := j+1;
             end;
           Point.Name := St;
           s := TrimLeft(RightStr(s, (Length(s)-Length(St))));
           St := "";
           j := 1;

           While s[j] <> " " do
             begin
               St := St + s[j];
               j := j+1;
             end;
           Point.X := StrToFloat(St);
           s := TrimLeft(RightStr(s, (Length(s)-Length(St))));
           St := "";
           j := 1;

           While s[j] <> " " do
             begin
               St := St + s[j];
               j := j+1;
             end;
           Point.Y := StrToFloat(St);
           s := TrimLeft(RightStr(s, (Length(s)-Length(St))));
           St := "";
           j := 1;

           While  s[j] <> " "  do
             begin
               if j > Length(s) then Break;
               St := St + s[j];
               j := j+1;
             end;
           Point.Z := StrToFloat(St);
           s := TrimLeft(RightStr(s, (Length(s)-Length(St))));
           St := "";
           j := 1;

           While j <= Length(s) do
             begin
               St := St + s[j];
               j := j+1;
             end;
           Point.Discription := St;
           AddPoint(Point);
         end;
      i:= i + 1
     end;
 except on E:Exception do
   DoOnError (peReadError) ;
 end;
 List.Free;
end;

procedure TPoints.ReadPoints(aFileName: String);  var
   i : integer;
   s : String;
   Point: RPoint;
   List: TStringList;

begin
 try
   List := TStringList.Create;
   List.LoadFromFile(aFileName);
   i := 0;
   While i <= List.Count - 1 do
     begin
       s := List[i];
       s := Trim(s);
       s := DotToDecSepSpace(s);
       if s <> "" then
         begin
           Point.Name := LeftStr(s, Pos(" ", s) - 1);
           s := TrimLeft(RightStr(s, (Length(s)-Pos(" ", s))));

           Point.X := StrToFloat(LeftStr(s, Pos(" ", s) - 1));
           s := TrimLeft(RightStr(s, (Length(s)-Pos(" ", s))));

           Point.Y := StrToFloat(LeftStr(s, Pos(" ", s) - 1));
           s := TrimLeft(RightStr(s, (Length(s)-Pos(" ", s))));

           if Pos(" ", s) = 0 then
             Point.Z := StrToFloat(s)
           else
             begin
               Point.Z := StrToFloat(LeftStr(s, Pos(" ", s) - 1));
               s := TrimLeft(RightStr(s, (Length(s)-Pos(" ", s))));
               Point.Discription := s;
             end;

           AddPoint(Point);

           end;
           i:= i + 1
         end;

 except on E:Exception do
   DoOnError (peReadError) ;
 end;
 List.Free;
end;


 
Desdechado ©   (2006-02-05 21:17) [12]

> Структура строки в *.txt файле:
не виду структуры, вижу только, что туда записано
структура подразумевает еще и "как записано"
т.е. должен быть указан формат, чем разделены поля, чем разделены строки, есть ли "шапка" и т.п.


 
Tatiana   (2006-02-05 21:35) [13]

Файл заранее обработан таким образом, что в нем только цифры и , может быть слова (Name,Discriptoin), шапки нет, цифры (в смысле, поля) м.б. разделены пробелом, либо табуляцией, строки - вводом.


 
Gero ©   (2006-02-05 22:01) [14]

procedure SplitString(const Str: string; Delimiter: Char; Strings: TStrings);
var
 s: string;
 Pos, Pos2: Integer;
begin
 Strings.BeginUpdate;
 try
   Strings.Clear;
   Pos := 1;
   for Pos2 := 1 to Length(Str) do begin
     if Str[Pos2] = Delimiter then begin
       s := Copy(Str, Pos, Pos2 - Pos);
       Strings.Add(s);
       Pos := Pos2 + 1;
     end;
   end;
   s := Copy(Str, Pos, Length(Str) - Pos + 1);
   Strings.Add(s);
 finally
   Strings.EndUpdate;
 end;
end;


(c) DMClient team

Функция заносит подстроки в строке, разделенные Delimiter в Strings.

Надеюсь, с чтением файла построчно проблем не возникнет.


 
begin...end ©   (2006-02-05 22:09) [15]

> Gero ©   (05.02.06 22:01) [14]

procedure SplitString(const Str: string; Delimiter: Char; Strings: TStrings);
begin
 Strings.Delimiter := Delimiter;
 Strings.DelimitedText := Str
end


 
Gero ©   (2006-02-05 22:17) [16]

> begin...end ©   (05.02.06 22:09)

Да, согласен, спасибо :)
Код старый и сюда был бездумно скопирован )


 
Tatiana   (2006-02-05 23:10) [17]

Насколько я понимаю "." и "," тоже относятся к Delimiter? Если так, то процедура не подходит, т.к. в строке есть числа в виде десятичных дробей. Процедура SplitString отделит целые части от десятичных, а мне нужно все число. Я права???


 
Gero ©   (2006-02-05 23:43) [18]

> Насколько я понимаю "." и "," тоже относятся к Delimiter?

Delimiter передается аргументом в функцию и может быть каким угодно на твое усмотрение.


 
Desdechado ©   (2006-02-06 10:52) [19]

основное время уходит не на чтение из файла, а на операции по разрезанию строк
подозреваю, что количество табуляций/пробелов и прочего между числами плавающее (для визуального выравнивания в файле)
тогда нужно их позаменять на нормальный разделитель одного формата (например, табуляцию) StringReplace, а уж после этого применять SplitString


 
Tatiana   (2006-02-06 16:14) [20]

Да, вы правы. Перед разделением строки я заменяю все разделители на пробелы, единственно, не меняю их количество, т.е. если было три табуляции, после выполнения функции они превращаются в три пробела.


 
Tatiana   (2006-02-08 16:57) [21]

А кроме оптимизации кода, могут быть еще какие-нибудь варианты, чтобы заадча выполнялась быстрее. Дело в том, что ни один из вариантов, указанных выше, все равно по времени не отличается :(


 
Alex_C ©   (2006-02-08 17:08) [22]

Тебе уже ответили - тут выход один
ReadBlock и читать ну скачем по 64000 байта за раз.
Кстати недавно столкнулся с проблемой - надо было в базу переконвертировать текстовый фаил размером 19 мег. Так никакие TStringList.LoadFromFile и затем работа в памяти не помогли - очень долго, а вот читать как бинарный фаил блоками - наиболее быстро. Только по строкам прийдется тебе самому разбивать.


 
vovnuke ©   (2006-02-08 17:15) [23]

а что если с этой структурой работать с помощью ClientDataSet, а из него уже сохранять в файл?


 
Tatiana   (2006-02-09 00:21) [24]

Проблема не в скачивании в память - это происходит быстро, а в разбиении строк.

procedure TPoints.ReadPoints(aFileName: String);
{&#241;&#247;&#232;&#242;&#251;&#226;&#224;&#229;&#242; &#228;&#224;&#237;&#237;&#251;&#229; &#232;&#231; &#244;&#224;&#233;&#235;&#224; &#241; &#232;&#236;&#229;&#237;&#229;&#236; aFileName &#226; FStartPointsArr}
 var
   i : integer;
   s : String;
   Point: RPoint;
   List: TStringList;
   myStrings : TStringList;

begin
 try
   List := TStringList.Create;
   myStrings := TStringList.Create;
   List.LoadFromFile(aFileName);

   i := 0;
 While i <= List.Count - 1 do
   begin
     List[i] := TrimLeft(List[i]);
     List[i] := DotToDecSepSpace(List[i]);
     i := i + 1;
   end;
{до этого места все идет достаточно быстро, а вот на нижеследующий цикл тратится слишком много времени. Может быть, я преувеличиваю, но если в *.txt файле около 17 000 строк (912KB), то программа его обрабатывает 5 минут. Ожидание кажется бесконечным...}

  i := 0;
   While i <= List.Count - 1 do
     begin
       s := List[i];
       myStrings.Clear;
       SplitString(s, " ", myStrings);
       if s <> "" then
         begin
           Point.Name := myStrings[0];
           Point.X := StrToFloat(myStrings[1]);
           Point.Y := StrToFloat(myStrings[2]);
           Point.Z := StrToFloat(myStrings[3]);

           if myStrings.Count > 4 then
           Point.Discription := myStrings[4];
         end;
       AddPoint(Point);
       i:= i + 1
     end;

 except on E:Exception do
   DoOnError (peReadError) ;
 end;
 List.Free;
 myStrings.Free;
end;


 
Defunct ©   (2006-02-09 03:59) [25]

Tatiana   (09.02.06 00:21) [24]

несущественное замечание: вместо while лучше использовать for.
существенное замечание: вместо SplitString для ускорения работы лучше единожды задать delimiter для myStrings, и просто менять DelimitedText (без очистки содержимого):


   myStrings := TStringList.Create;
   myStrings.Delimiter := " ";  // <- единожды задать разделитель
  ...

  with List do                      // для ускорения работы
  for i := 0 to Count - 1 do    // <-- вместо while
     begin
     ///  myStrings.Clear;  Выбросить
     ///  SplitString(..);  Выбросить
          S := Strings[i];

          if S <> "" then
          begin
              myStrings.DelimitedText := S;
             ...
              AddPoint(..);  // ВНЕСТИ В ТЕЛО IF (иначе у вас некоторые записи будут дублироваться)
          end
   end
   


А также неплохо было бы привести код AddPoint,
для теста попробуйте вначале вообще ее (AddPoint) закоментировать.


 
vovnuke ©   (2006-02-09 09:39) [26]

2Tatiana
А тебе очень важно хранить данные именно в *.txt файле.
То работай со своими данными с использованием ClientDataSet, с ним будешь работать как с таблицей базы данных, сохраненной в файле.
Я думаю что загрузить из таблицы 17000 записей, будет побыстрее, чем из текстового файла 17000 строк.


 
Tatiana   (2006-02-09 11:11) [27]


> А тебе очень важно хранить данные именно в *.txt файле.

Лично мне все равно, но не я выбираю :(


 
Gero ©   (2006-02-09 11:14) [28]

>   myStrings.Delimiter := " ";  // <- единожды задать разделитель

Это лишнее.


 
Leonid Troyanovsky ©   (2006-02-09 11:40) [29]


> Tatiana   (09.02.06 00:21) [24]

> {до этого места все идет достаточно быстро, а вот на нижеследующий
> цикл тратится слишком много времени. Может быть, я преувеличиваю,
>  но если в *.txt файле около 17 000 строк (912KB), то программа
> его обрабатывает 5 минут. Ожидание кажется бесконечным..


Подобные файлы на моем Celeron 2GHz 248M of RAM обрабатываются за 0.5 с
(файл 26М - 12 сек, 53М - 25 сек, 107М- 50 сек).


uses
 Classes;

procedure TForm1.Button1Click(Sender: TObject);
var
 fs: TFileStream;
 p: TParser;
 sl : Longint;
 i: Longint;
 f: array [0..2] of Extended;
 n, s: String;
 t : Cardinal;
begin
 t := GetTickCount;
 fx := 0;
 fs := TFileStream.Create("file1.txt", fmOpenRead);
 p := TParser.Create(fs);
 while p.Token <> toEOF do
   begin
     sl := p.SourceLine;
     if (p.Token =  toSymbol) then // если имя - идентификатор паскаля
         begin
           n := p.TokenString;
           s := "";

           for i := 0 to 2 do
             begin
              p.NextToken;
              p.CheckToken(toFloat); // если все числа в формате Float (with ".")
              {см. также toInteger}
              f[i] := p.TokenFloat;
             end;

           p.NextToken; // формируем Description
           while (p.SourceLine = sl) and (p.Token <> toEOF) do
             begin
               s := s + " " + p.TokenString;
               p.NextToken;
             end;
           {здесь записываем (обрабатываем) полученные значения}
         end
     else
       p.NextToken;
   end;
   p.Free;
   fs.Free;
   ShowMessage(Format("%d",[GetTickCount-t]));
end;


Работа над ошибками оставлена в качестве домашнего задания.

--
Regards, LVT.


 
Tatiana   (2006-02-09 11:54) [30]


> Defunct ©   (09.02.06 03:59)

Вот это да!!! Действительно на AddPoint тратится все время....

procedure TPoints.AddPoint(Point: RPoint);
{существует класс, одним из полей является одномерный массив FPoints Arr, функция MCount текущую длину этого массива}
begin
 SetLength(FPointsArr, MCount + 1);
 FPointsArr[High(FPointsArr)] := Point;
end;


 
Defunct ©   (2006-02-09 12:54) [31]

Gero ©   (09.02.06 11:14) [28]

Почему? delimiter по-умолчанию не инициализируется значением " ".

Tatiana   (09.02.06 11:54) [30]
>  SetLength(FPointsArr, MCount + 1);

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

Думаю стоит задавать длину однократно. Вы точно знаете сколько строк прочитано сразу после загрузки файла в List. Вот и задайте массиву количество строк в качестве размера, перед заполением элементов. Далее просто считайте количество добавленных (реальных) элементов. После добавления всех элементов в массив - подкорректируйте его размер - подставьте в SetLength количество подсчитанных элементов.


 
begin...end ©   (2006-02-09 13:10) [32]

> Defunct ©   (09.02.06 03:59) [25]

> просто менять DelimitedText (без очистки содержимого)

Без очистки не получится, т.к. при изменении DelimitedText вызывается Clear.

> with List do // для ускорения работы

LOL

> Defunct ©   (09.02.06 12:54) [31]
> Почему? delimiter по-умолчанию не инициализируется значением " ".

with TStringList.Create do
 try
   DelimitedText := "abc def";
   ShowMessage(IntToStr(Count))
 finally
   Free
 end

Разгадка -- в коде TStrings.SetDelimitedText, вестимо.


 
Defunct ©   (2006-02-09 15:25) [33]

begin...end ©   (09.02.06 13:10) [32]
> Без очистки не получится, т.к. при изменении DelimitedText вызывается Clear.

Вдумчиво вчитайся в то, что было мной выброшено из кода. А именно был выброшен Clear.

>> with List do // для ускорения работы
> LOL
по словам Digitman, с with генерируемый код получается более эффективным. К тому же коментарий относится также и к for.

> Разгадка -- в коде TStrings.SetDelimitedText, вестимо.
что ж спасибо, хорошо, что всегда найдутся начитанные люди.

PS: что я и говорил ранее - желание "подклоть" так и прет.


 
Gero ©   (2006-02-09 15:53) [34]

> > Разгадка -- в коде TStrings.SetDelimitedText, вестимо.
> что ж спасибо, хорошо, что всегда найдутся начитанные люди.
>
> PS: что я и говорил ранее - желание "подклоть" так и прет.

Ты о чем? begin..end дал вполне конкретное объяснение, разгадка действительно в TStrings.SetDelimitedText.


 
Defunct ©   (2006-02-09 16:01) [35]

Gero ©   (09.02.06 15:53) [34]

Я о "LOL" и о первом предложении.

в том что begin...end дал верный ответ насчет SetDelimitedTest я не сомневаюсь, однако опять же тон каким этот ответ.


 
begin...end ©   (2006-02-09 17:00) [36]

> Defunct ©   (09.02.06 15:25) [33]

> Вдумчиво вчитайся в то, что было мной выброшено из кода.
> А именно был выброшен Clear.

Я читал. Только не понял, почему это было названо "существенным замечанием", а замена while на for -- "несущественным". Потому что на самом деле всё наоборот.

> с with генерируемый код получается более эффективным

По крайней мере, в данном случае он не получится более эффективным.

> PS: что я и говорил ранее - желание "подклоть" так и прет.

LOL. 3 раза.


 
Defunct ©   (2006-02-09 17:27) [37]

> Я читал.
А почему ж тогда написал: "Без очистки не получится, т.к. при изменении DelimitedText вызывается Clear."

>Только не понял, почему это было названо "существенным замечанием", а замена while на for -- "несущественным". Потому что на самом деле всё наоборот.
Если скажу честно, то все равно ведь не поверишь, и в очередной раз напишешь дурацкий lol. Оба замечания - несущественные, и на скорость практически не влияют, просто написав первое замечание, искал второе более существенное, но так и не найдя, забыл исправить слово "существенное" на "несущественное".
Фирштейн?

> По крайней мере, в данном случае он не получится более эффективным.
И это послужило поводом для LOL? над коментарием???


 
begin...end ©   (2006-02-09 17:41) [38]

> Defunct ©   (09.02.06 17:27) [37]

> А почему ж тогда написал: "Без очистки не получится, т.к.
> при изменении DelimitedText вызывается Clear."

Потому что без очистки действительно не получится.

> Оба замечания - несущественные, и на скорость практически
> не влияют

ОК, с этим могу согласиться. Но всё же, если сравнивать первое и второе замечания, первое действительно существеннее.

> просто написав первое замечание, искал второе более существенное,
> но так и не найдя, забыл исправить слово "существенное"
> на "несущественное".

Уфф, как сложно всё...
Действительно, верится с трудом.
:о)

> И это послужило поводом для LOL? над коментарием???

Конечно. Мне действительно было смешно, потому что ускорению работы кода за счёт with здесь просто неоткуда взяться.


 
Defunct ©   (2006-02-09 17:47) [39]

> Уфф, как сложно всё...
> Действительно, верится с трудом.
> :о)
Ну так, на самом деле так и есть :)


>> И это послужило поводом для LOL? над коментарием???

> Конечно. Мне действительно было смешно, потому что ускорению работы кода за счёт with здесь просто неоткуда взяться.


А если тоже самое прочесть не отрывая от следующего коментария, тоже будет смешно?

Что-то я не понимаю, проблему автора вопроса я локализовал, что еще от меня требуется? И почему я должен выслушивать насмешки?


 
begin...end ©   (2006-02-09 17:55) [40]

> Defunct ©   (09.02.06 17:47) [39]

> А если тоже самое прочесть не отрывая от следующего коментария,
> тоже будет смешно?

Да, тоже будет. Потому что про ускорение работы написано именно в строке с with. Значит, этот комментарий относится, по крайней мере, и к with тоже (если не только к нему). А это, как уже говорилось, смешно.



Страницы: 1 2 вся ветка

Текущий архив: 2006.02.26;
Скачать: CL | DM;

Наверх




Память: 0.61 MB
Время: 0.037 c
2-1139443081
VitalikS
2006-02-09 02:58
2006.02.26
CD-ROM


1-1138122133
integeri
2006-01-24 20:02
2006.02.26
робота со шрифтом


15-1139300111
unknown
2006-02-07 11:15
2006.02.26
Платная электронная почта?


9-1125503769
Nonstop
2005-08-31 19:56
2006.02.26
Gjvjubnt vyt f z dfv pfgkfxe


11-1120535815
Fanny
2005-07-05 07:56
2006.02.26
Проблемы с динамическими массивами