Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > [ "Начинающим" ]
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 16.02.2013, 17:47
Pcrepair
 
Сообщения: n/a
По умолчанию Функция COPY. Быстродействие

Добрый день. Есть функция удаления подстроки типа <gg hhh>jjjj</gg> из строки
(на самом деле посимвольное копирование когда это не запрещено условием)

Код:
function DelUseless(const Data:string):string;
var
Len,I,EndTeg,Differ,J:integer;
DefineTeg:string; (*первые N символов после <*)
begin                  
Len:=Length(Data);
SetLength(Result, Len);
Differ:=0;
J:=0;
if Length(Data) = 0 then Exit else
for I := 1 to Length(Data) do
(*-----------------ЦИКЛ-----------------------------*)
 begin
   if Differ > 0 then Dec(Differ)
     else
      if (Data[i] = '<') then
        begin
          DefineTeg:=Copy(Data, I,10); (*тут тормоз?*)
             if (PosEx('<script', DefineTeg,1)= 0) then
               begin
                 Inc(J);
                 Result[J]:=Data[i];
               end
             else
               begin
                  EndTeg:=PosEx('</script>',Data,I);
                  if (EndTeg > 0) then Differ:=(EndTeg - I + 8);
               end
        end
   else
    begin
      Inc(J);
      Result[J]:=Data[i];
    end
 end;
(*-----------------конец-----------------------------*)
SetLength(Result, J);
end;  (*все работает*)

функция работает достаточно быстро (файл 3 мб за 30 мС обрабатывает)
но есть подозрение, что использование COPY (DefineTeg:=Copy(Data, I,10) увеличивает время обработки
Внимание!! Вопрос:
1. действительно ли COPY не самое лучшее решение по быстродействию
2. чем можно заменить COPY? может вставка на ASM?
Ответить с цитированием
  #2  
Старый 16.02.2013, 23:23
Snake22 Snake22 вне форума
Активный
 
Регистрация: 20.02.2011
Сообщения: 374
Репутация: 744
По умолчанию

а разве Copy уже не написана на асме?
не заморачивайся) 30мсек
Ответить с цитированием
  #3  
Старый 17.02.2013, 00:08
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Вообще не понимаю использования PosEx для сравнения строк.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.
Ответить с цитированием
  #4  
Старый 17.02.2013, 10:26
Аватар для NumLock
NumLock NumLock вне форума
Let Me Show You
 
Регистрация: 30.04.2010
Адрес: Северодвинск
Сообщения: 5,426
Версия Delphi: 7, XE5
Репутация: 59586
По умолчанию

тут проблема не в Copy, а в алгоритме.
__________________
Пишу программы за еду.
__________________
Ответить с цитированием
  #5  
Старый 17.02.2013, 11:17
Pcrepair
 
Сообщения: n/a
По умолчанию

Цитата:
Сообщение от Bargest
Вообще не понимаю использования PosEx для сравнения строк.

если можно поподробнее. ведь в букваре сказано:
функция для поиска в строке, именуемая PosEx.
Ответить с цитированием
  #6  
Старый 17.02.2013, 15:11
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Зачем искать в строке, когда можно просто сравнить строки на равенство? Копировать не 10 символов, а 7 (длина строки "<script"), а затем сравнить копированную строку со "<script" обычным "равно".
Вообще NumLock прав. Не понимаю этих плясок с бубном вокруг цикла for, когда гораздо быстрее было бы сделать while, на котором лишние итерации можно просто пропустить.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.
Ответить с цитированием
  #7  
Старый 17.02.2013, 17:21
Pcrepair
 
Сообщения: n/a
По умолчанию

тут все немножко сложнее, сейчас функция уже имеет вид
Код:
function DelUseless(const Data:string):string;
 
const
Teg1 = '<script'; Teg2 = '<style'; Teg3 = '<noscript'; Teg4 = '<applet';
Teg5 = '<object'; Teg6 = '<textarea'; Teg7 = '<audio'; Teg8 = '<button';
Teg9 = '<canvas'; Teg10 = '<comment'; Teg11 = '<datalist'; Teg12 = '<del';
Teg13 = '<meter'; Teg14 = '<noembed'; Teg15 = '<optgroup'; Teg16 = '<output';
Teg17 = '<progress';
 
var
Len,I,EndTeg,Differ,J:integer;
DefineTeg:string; (*первые N символов после <*)
Pos1,Pos2:integer;
Pattern:string;
starttime,endtime,q:int64;
begin                    starttime:=GetTickCount;
Len:=Length(Data);
SetLength(Result, Len);
Differ:=0;
J:=0;
if Length(Data) = 0 then Exit else
for I := 1 to Length(Data) do
(*-----------------ЦИКЛ-----------------------------*)
 begin (*2 Cycle*)
  if Differ > 0 then Dec(Differ)
   else
      if Data[i] <> '<' then
             begin
               Inc(J);
               Result[J]:=Data[i]; (**)
             end
      else
         begin
          DefineTeg:=Copy(Data, I,11); (*копируем 12 символов*)
          Pos1:=PosEx(' ',DefineTeg,1); (*позиция первого пробела *)
          if Pos1 = 0 then (*если пробела нет ищем >*)
          Pos1:=PosEx('>',DefineTeg,1); (*позиция первого >*)
          Pos2:=Pos1-1;
          Pattern:=Copy(DefineTeg, 1, Pos2); (*выделяем паттерн тега*)
          (*----выбор тега на CASE------------------------*)
            case  AnsiIndexStr(Pattern,
            [Teg1, Teg2, Teg3, Teg4, Teg5, Teg6, Teg7, Teg8, Teg9,
            Teg10, Teg11, Teg12, Teg13, Teg14, Teg15, Teg16, Teg17]) of
              0:begin
                 EndTeg:=PosEx('</script>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 8);
                end;
              1:begin
                 EndTeg:=PosEx('</style>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 7);
                end;
              2:begin
                 EndTeg:=PosEx('</noscript>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
              3:begin
                 EndTeg:=PosEx('</applet>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
              4:begin
                 EndTeg:=PosEx('</object>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
              5:begin
                 EndTeg:=PosEx('</textarea>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
              6:begin
                 EndTeg:=PosEx('</audio>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
              7:begin
                 EndTeg:=PosEx('</button>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
              8:begin
                 EndTeg:=PosEx('</canvas>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
              9:begin
                 EndTeg:=PosEx('</comment>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
              10:begin
                 EndTeg:=PosEx('</datalist>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
              11:begin
                 EndTeg:=PosEx('</del>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
              12:begin
                 EndTeg:=PosEx('</meter>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
              13:begin
                 EndTeg:=PosEx('</noembed>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
              14:begin
                 EndTeg:=PosEx('</optgroup>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
              15:begin
                 EndTeg:=PosEx('</output>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
              16:begin
                 EndTeg:=PosEx('</progress>',Data,I);
                 if (EndTeg > 0) then Differ:=(EndTeg - I + 10);
                end;
            else
              begin
                Inc(J);
                Result[J]:=Data[i];
              end;
          (*------конец CASE------------------------------*)
            end;
         end;
 end;
 (*-------конец Цикла-------------------------------------*)
SetLength(Result, J);
endtime:=GetTickCount; q:=endtime-starttime;  ShowMessage('TIME = '+IntToStr(q));
end;  (*78(три тега) полная набор тегов 218 мС для 3 мб текста*)
и удаляет не один тег, а все 17 парных тегов, ненужных для передачи информации

вот как тут ускорить обработку? может кто подскажет?
Ответить с цитированием
  #8  
Старый 17.02.2013, 18:12
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Сделать цикл while, убрать этот никому не нужный differ и просто устанавливать i на символ после конца закрывающего тега.
Вообще скорее всего быстрее искать очередной тег, копировать всё до него, искать следующий после конца найденного. Посимвольно дописывать в конец строки - это не гуд.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.
Ответить с цитированием
  #9  
Старый 17.02.2013, 21:06
Pcrepair
 
Сообщения: n/a
По умолчанию

то есть вначале сделать быструю функцию типа
function Del(const Str,StartTeg,EndTeg:string):string;
а потом эту функцию кинуть в цикл, в котором поочередно подставлять в параметры пары тегов и по новой прогонять Str, поочередно удаляя все ненужное?
и так будет быстрее чем просто посимвольно скопировать Str в Result, запрещая копирование там где нужно?
Ответить с цитированием
  #10  
Старый 17.02.2013, 22:11
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Я бы сделал так.
A = 1.
Ищу первое вхождение < после A.
Копирую 10 символов с этой позиции во временную строку.
Смотрю на нужный тег. Если не нужный - ищу следующую < с этой позиции. Если нужный - копирую от A до текущей позиции, ищу к нему закрывающий и A = концу закрывающего.
Вернуться в начало.
Каждое прибавление символа к строке - это перевыделение памяти под строку.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.
Ответить с цитированием
  #11  
Старый 18.02.2013, 08:03
Pcrepair
 
Сообщения: n/a
По умолчанию

то есть использовать CASE для определения нужный тег, или не нужный?

и использовать Repeat для одного гарантированного прохода по строке
а копировать чем? COPY или MOVE. или через указатели? типа:
Код:
function TegsDelete2(const Source: string):string;
var
  Step, TagCnt : Integer;
  ResChar, SrcChar: PChar;
begin                      
  TagCnt:=0;
  SetLength(Result, Length(Source));  (*создаем выход с длинной =*)
  if Length(Source)=0 then   Exit;

  SrcChar:=@Source[1]; (*копировать первый символ указатель на память*)
  ResChar:=@Result[1];
  for Step:=1 to Length(Source) (*цикл*)
   do   begin
         case SrcChar^ of
           '<': Inc(TagCnt);
           '>': Dec(TagCnt);
           else   if TagCnt<=0 then
                       begin  (*вот тут копирование нужных символов*)
                         ResChar^:=SrcChar^;
                         Inc(ResChar);
                         TagCnt:=0
                       end
          end;
         Inc(SrcChar)
        end;
  SetLength(Result, ResChar-PChar(@Result[1]));  (*обрезаем лишнее в строке*)
end;
но тут еще вопрос: эта функция будет работать в потоке(THread), одномоментно будут работать до 10 нитей. не будет ли проблем
Ответить с цитированием
  #12  
Старый 18.02.2013, 11:45
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Да, получать первый тег можно тем же кейсом, использовать repeat, копировать copy (ее скорость нормальная). Через указатели - проблем не оберешься.
И причем тут потоки? Если они будут обрабатывать разные строки, то вообще никакой связи. А если одну... лучше так не делать.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.
Ответить с цитированием
  #13  
Старый 19.02.2013, 22:35
Pcrepair
 
Сообщения: n/a
По умолчанию

Цитата:
Сообщение от Bargest
Я бы сделал так.
A = 1.
Ищу первое вхождение < после A.
Копирую 10 символов с этой позиции во временную строку.
Смотрю на нужный тег. Если не нужный - ищу следующую < с этой позиции. Если нужный - копирую от A до текущей позиции, ищу к нему закрывающий и A = концу закрывающего.
Вернуться в начало.
Каждое прибавление символа к строке - это перевыделение памяти под строку.

Ну вот:
Код:
function DelUseless(const Data:string):string;
var
Len,J:integer;
DefineTeg:string; (*первые N символов после <*)
starttime,endtime,q:int64;

Count:integer; (*счетчик позиции символа в строке*)
Pos1:integer; (*позиция вхождения < в строке*)
TegEnd:Integer; (*позиция окончания тега*)
Pattern:string; (*выделенный Тег из строки*)
SubStr:string;
begin                     starttime:=GetTickCount;
Len:=Length(Data);
J:=0;
if Length(Data) = 0 then Exit else
Count:=1;
(*-----------------ЦИКЛ-----------------------------*)
repeat
Pos1:=PosEx('<', Data,Count);   (*поиск первого вхождения символа < в строке*)
if Pos1 = 0 then
     begin
      if Count = 1 then  (*эта ветка на тот случай если вообще нет тегов в странице*)
        begin
          Result:=Data;
          Break;
        end  else
        begin            (*эта ветка копирует все после того как закончатся <*)
          SubStr:=Copy(Data, Count, Len-Count-1);  
          J:=Length(SubStr)+Length(Result);        
          Insert(SubStr, Result, J+1);
          Break;
        end;
     end  else
 begin  (*тут конечно можно проще, но на скорость это не влияет*)
  DefineTeg:=Copy(Data, Pos1, 10); (*копируем первые 11 символов с позиции первого вхождения*)
  TegEnd:=PosEx(' ',DefineTeg,1); (*позиция первого пробела *)
  if TegEnd = 0 then (*если пробела нет ищем >*)
  TegEnd:=PosEx('>',DefineTeg,1); (*позиция первого >*)
  Pattern:=Copy(DefineTeg, 1, (TegEnd-1)); (*выделяем паттерн тега*)
  (*----------------------------------------------------------*)
  (*далее определяем есть ли в Перем нужный тег(<script)*)
    if Pattern = '<script' then 
     begin
       SubStr:=Copy(Data, Count, Pos1-Count);    (*копировать от Count до позиции Pos1*)
       J:=Length(SubStr)+Length(Result);         (*число символов в ВЫХОДЕ!!!!!*)
       Insert(SubStr, Result, J+1);              
       Count:=PosEx('</script>', Data, Pos1)+9;  (*переводим позицию на следующий символ*)
     end
     else
      begin
        SubStr :=Copy(Data, Count, Pos1-Count+1);  (*копировать все gghh<*)
        J:=Length(SubStr)+Length(Result);          
        Insert(SubStr, Result, J+1);               
        Count:=Pos1+1;                             (*переводим позицию на следующий символ*)
      end;
 end
until Pos1 = 0 ;
(*-----------------конец-----------------------------*)
endtime:=GetTickCount; q:=endtime-starttime;  ShowMessage('TIME = '+IntToStr(q));
end;  (*все работает 50 мС для 3 мБ*)
Но результат практически вдвое хуже чем в варианте с посимвольным копированием в цикле FOR
Если возможно, укажите на узкие участки и как их заменить, но без использования указателей. это пока не совсем понятно

и еще косячок: почемуто пропадают два последние символа в строке, было </html> стало </htm, непонятно куда

Последний раз редактировалось Pcrepair, 19.02.2013 в 22:40.
Ответить с цитированием
  #14  
Старый 19.02.2013, 23:10
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Зачем Insert? Можно просто дописать в конец. Что-то вроде
Код:
Count := 1;
cPos := 1;
Pos1:=PosEx('<', Data,1);
Result := '';
while Pos1 > 0 do
begin
   DefineTeg:=Copy(Data, Pos1, 10); (*копируем первые 11 символов с позиции первого вхождения*)
   TegEnd:=PosEx(' ',DefineTeg,1); (*позиция первого пробела *)
   if TegEnd = 0 then (*если пробела нет ищем >*)
       TegEnd:=PosEx('>',DefineTeg,1); (*позиция первого >*)
   if TegEnd = 0 then
       break;
   Pattern:=Copy(DefineTeg, 1, (TegEnd-1)); (*выделяем паттерн тега*)
   // можно и так, а можно
   // SetLength(DefineTag, TegEnd - 1);
   // и юзать DefineTag
   if Pattern = '<script' then
   begin
      Result := Result + Copy(Data, Count, Pos1 - Count);
      Count := PosEx('</script>', Data, Pos1 + 7) + 9;
      cPos1 := Count;
   end
   else
      cPos1 := Pos1 + 1;
   Pos1:=PosEx('<', Data,cPos);
end;
Result := Result + Copy(Data, Count, Length(Result) - Count);
ЗЫЖ не тестил, писал "на коленке", могут быть и ошибки.
Хотя вообще странно, что твой вариант медленнее. В таком случае может быть медленнее и тут. По идеи PosEx = rep scasb, одна команда; в то время как прибавление по одному символу - постоянные релоки строки.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.

Последний раз редактировалось Bargest, 19.02.2013 в 23:16.
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 22:24.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2023

ВКонтакте   Facebook   Twitter