Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2006.02.26;
Скачать: [xml.tar.bz2];

Вниз

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

 
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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.6 MB
Время: 0.04 c
15-1139310662
Andy BitOff
2006-02-07 14:11
2006.02.26
Opera 9.0 Technology Preview 2 (сегодняшняя)


1-1137951205
jjj
2006-01-22 20:33
2006.02.26
замена библиотек


2-1139053550
nap<>
2006-02-04 14:45
2006.02.26
документооборот


15-1138909912
VseHotjatIJaHo4u
2006-02-02 22:51
2006.02.26
Вождение машины


4-1133763062
ra4fcr
2005-12-05 09:11
2006.02.26
Цветовая схема XP





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский