Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[44.220.43.170] |
|
Сообщ.
#1
,
|
|
|
Доброе время суток, уважаемые форумчане.)
Нужно перевести функцию с delphi в delphi asm. Не силен в asm'e. Спасибо. function PreXML(S: string): string; var i:integer; begin for I := 1 to Length(s) do if s[i] = #62 then insert(#13#10,s,i+1); Result:= S; end; |
Сообщ.
#2
,
|
|
|
Скомпилируй программу, поставь брекпойнт на вызове функции, при останове переключись в просмотр сгенерированного asm-кода Alt-Ctrl-C
|
Сообщ.
#3
,
|
|
|
Цитата RusSun @ Нужно перевести функцию с delphi в delphi asm. А зачем? Можно последовать совету MBo и "содрать" asm-листинг функции из окна CPU. Вот только вопрос - зачем? Чем это будет лучше использования исходной функции на паскале (которая компилируется точно в такой же asm-код)? Проблема в том, что тут используется спец.встроенный тип данных string, причем не просто в режиме чтения, а в режиме наращивания длины функцией Insert, за которой на самом деле скрывается некая недокументированная "магическая" функция компилятора типа _UStrInsert (плюс еще пара аналогичных функций _UStrAddRef и _UStrAsg). Внимание, вопрос: зачем использовать асм-код с вызовом недокументированных магических функций, если компилятор сам сделает точно такой-же код из исходника функции PreXML, использующего нормальную документированную функцию Insert? Добавлено Подозреваю, что при обработке больших XML документов функция PreXML сильно тормозит, и ты наивно полагаешь, что если ее переписать на asm, то она будет работать "намного быстрее"? Это не верно. Тут весь тормоз в использовании функции Insert, которая во-первых, при каждой вставке вынуждена перемещать весь длинный хвост строки, во-вторых, через несколько вставок вынуждена увеличивать длину буфера строки (с вызовом ReallocMem - возможно с перемещением всей строки на другой адрес, если не удается увеличить размер буфера по тому же адресу). Поэтому для увеличения быстродействия нужно: 1) Не изменять исходную строку, а выделить новую строку под Result и копировать в нее куски исходной строки между символами '>' и добавлять после них разделители #13#10. (При этом символы нужно искать ф-ей PosEx, а не обычной Pos). 2) Для уменьшения кол-ва реаллокаций строки Result можно выделять память под нее (через SetLength) не точно на +2, а достаточно большими блоками (например по 256 байт). А в конце обработки урезать длину до реальной скопированной. (Также возможен вариант, когда за первый проход производится подсчет кол-ва символов '>', затем выделяется строка Result нужной длины, и за второй проход осуществляется копирование s в Result со вставкой разделителей). И никакой asm тут не нужен - и на паскале все будет работать намного быстрее. |
Сообщ.
#4
,
|
|
|
Могу предположить:
а) учебная задача б) проблемы с быстродействием Во втором случае проблемы из-за неверно выбранного алгоритма обработки - многократное перераспределение памяти при изменении длины строки (думаю, оно будет и при замене функции более кратким аналогом Stringreplace) Его можно избежать, выделив новую строку с запасом и переписывая куски и добавки в неё. |
Сообщ.
#5
,
|
|
|
Кстати, мне не так давно пришлось конвертер формата XML-файлов росреестра (КПТ) слепить на коленке по аналогичному принципу. Работает вообще "мгновенно", по сравнению с "обычными поделками", которые "жуют сопли" по несколько минут
(Если выходные данные должны записывать в файл, то размер выходного буфера лучше сразу брать не менее 1-2 Мб, и сбрасывать данные на диск при его "переполнении") |
Сообщ.
#6
,
|
|
|
Да, проблема с быстродействием. В какой-то степени, можно сказать, что учебная тоже.
Пишу с телефона поэтому мне не совсем удобно. Дополнительно напишу вечером. |
Сообщ.
#7
,
|
|
|
А StringReplace() не быстрее?
|
Сообщ.
#8
,
|
|||||||||||||||||||||||||||||||
|
Function PosEx(Const SubStr, S: String; Offset: Cardinal = 1): Integer; var I,X: Integer; Len, LenSubStr: Integer; begin If Offset = 1 Then Result := Pos(SubStr, S) Else begin I := Offset; LenSubStr := Length(SubStr); Len := Length(S) - LenSubStr + 1; While I <= Len Do begin If S[I] = SubStr[1] Then begin X := 1; While (X < LenSubStr) And (S[I + X] = SubStr[X + 1]) Do Inc(X); If (X = LenSubStr) Then begin Result := I; Exit; End; End; Inc(I); End; Result := 0; End; End; function PreXMLFast(S: string): string; var beforePos:integer;//позиция копирования sPos:integer; //начальная позиция str:string; sResult:string; begin sPos:= 1; str:= ''; sResult:= ''; beforePos:=1; repeat sPos:=PosEx('>',S,sPos); //#62 if sPos<>0 then begin inc(sPos); //str:= str +intTostr (beforePos) + ', '+intTostr (sPos) +' ; :' j; // найденные позиции sResult:=sResult+Copy(S, beforePos ,sPos-beforePos)+#13#10; beforePos:=sPos; inc (sPos); end; until sPos=0; //вертим цикл пока sPos не станет равным О //result;=str4#13#10+sResult: result:=sResult; end; На работе испытывал следующие 2 файла.
можно еще быстрее? |
Сообщ.
#9
,
|
|
|
Однобайтный=односимвольный вариант PosEx некачественно, но ускорит ещё несколько.
Добавлено А ещё несколько ускорит (не сильно и мало!) прямое вписывание пары байт #13#10 в нужное в хвосте место, вместо ГОСТ'овской приписки к строке. Т.к. она (приписка) наверняка лишний раз посканирует строку для поиска конца, а это излишне. |
Сообщ.
#10
,
|
|
|
Цитата А StringReplace() не быстрее? Код процедуры. Чтобы сделать тест.Спасибо |
Сообщ.
#11
,
|
|
|
RusSun
Я как понял ты ищешь #62 и если есть то вставляешь #13#10. Так ты сделай замену #62 на #62#13#10 через StringReplace(). Result:=StringReplace(s,#62, #62#13#10,[rfReplaceAll, rfIgnoreCase]); Какой ещё нужен код? |
Сообщ.
#12
,
|
|
|
Цитата ^D^ima @ А StringReplace() не быстрее? По сравнению с Insert быстрее, а по сравнению с PreXMLFast из #8 скорее всего медленнее. Например, в Delphi 7 точно медленнее, т.к. в ней используется не PosEx, а просто Pos (AnsiPos) и соотв-но после каждого поиска изменяется не только строка Result, но и строка поиска (обрезается спереди по найденному символу через ту же Copy). PS: Тем более для символа #62 = '>' не имеет смысла использовать опцию rfIgnoreCase, которая лишь добавляет тормозов на преобразование регистра исходной строки. |
Сообщ.
#13
,
|
|
|
Цитата RusSun @ можно еще быстрее? Можно (не знаю насколько). Из советов в #3-4 ты реализовал только копирование символов из исходной строки в новую строку Result. Но тормоза за счет многократного наращивания длины строки (размера буфера через ReallocMem) у тебя все равно остаются. Эти тормоза особенно могут/будут проявляться в ранних версиях дельфей (в частности в D7), не использующих менеджер памяти FastMM. Поэтому лучше не наращивать длину строки на каждом найденном символе Result:=Result+..., а сразу выделить длину строки с некоторым (достаточно большим) запасом через SetLength(Result,...) и затем копировать в нее куски исходной строки простым Move(S[beforePos],Result[i],N), где N=(sPos-beforePos)*SizeOf(S[1]). Примерно так: //поиск символа в AnsiString или в Utf8String (при условии, что Ord(C) < 128) function ScanChar(C:AnsiChar; const S:AnsiString; iStart:integer):integer; var p:pAnsiChar; begin Result:=0; if iStart > Length(S) then Exit; p:=@S[iStart]; while (p^ <> C) and (p^ <> #0) do inc(p); if p^ <> #0 then Result:=p-pAnsiChar(@S[1])+1; end; function PreXMLFast(const S: Utf8String): Utf8String; const LFrac = 8; //знаменатель дроби, на которую берется запас длины строки - L+(L div LFrac) var iStart,iEnd:integer;//нач. и конеч. позиции копирования iRes:integer; //позиция записи в Result L,LRes:integer; //длина исходной и результирующей строки N:integer; //кол-во копируемых символов begin L:=Length(S); LRes:=L + (L div LFrac); //длина рез.строки с запасом на (L div LFrac) SetLength(Result,LRes); iStart:=1; iRes:=1; repeat iEnd:=ScanChar('>',S,iStart); if iEnd > 0 then begin N:=iEnd-iStart+1; if iRes+N+2 > LRes then begin inc(LRes, (L-iStart)+((L-iStart) div LFrac)); SetLength(Result,LRes); end; Move(S[iStart],Result[iRes],N); inc(iRes,N+2); Result[iRes-2]:=#13; Result[iRes-1]:=#10; iStart:=iEnd+1; end; until iEnd = 0; SetLength(Result,iRes-1); //устанавливаем реальную длину строки end; PS: Тут лучше вместо string использовать AnsiString или Utf8String, а преобразование в unicode (если нужно), делать потом. |
Сообщ.
#14
,
|
|
|
Только надо не забывать, что ">" вполне себе легитимно может встречаться в значениях узлов, в комментариях, CDATA секциях и инструкциях.
|
Сообщ.
#15
,
|
|
|
Цитата Fr0sT @ Только надо не забывать, что ">" вполне себе легитимно может встречаться в значениях узлов, в комментариях, CDATA секциях и инструкциях. да, верно. но у меня частный случай:) |