Могу ли я устранить лишние строковые вызовы Unicode (Delphi) - PullRequest
2 голосов
/ 29 июня 2010

Я использую Delphi 2009. В своей программе я очень усердно работал, чтобы оптимизировать весь мой код Delphi для скорости и использования памяти, особенно для обработки строк Unicode.

У меня есть следующее утверждение:

    Result := Result + GetFirstLastName(IndiID, 1);

Когда я отлаживаю эту строку, по возвращении из функции GetFirstLastName она прослеживает подпрограмму _UStrArrayClr в системном блоке:

procedure _UStrArrayClr(var StrArray; Count: Integer);
asm
        JMP     _LStrArrayClr
end;

Это вызывает _LStrArrayClr:

procedure       _LStrArrayClr(var StrArray; cnt: longint);
{$IFDEF PUREPASCAL}
var
  P: Pointer;
begin
  P := @StrArray;
  while cnt > 0 do
  begin
    _LStrClr(P^);
    Dec(cnt);
    Inc(Integer(P), sizeof(Pointer));
  end;
end;
{$ELSE}
asm
        { ->    EAX pointer to str      }
        {       EDX cnt         }

        PUSH    EBX
        PUSH    ESI
        MOV     EBX,EAX
        MOV     ESI,EDX

@@loop:
        MOV     EDX,[EBX]                       { fetch str                     }
        TEST    EDX,EDX                         { if nil, nothing to do         }
        JE      @@doneEntry
        MOV     dword ptr [EBX],0               { clear str                     }
        MOV     ECX,[EDX-skew].StrRec.refCnt    { fetch refCnt                  }
        DEC     ECX                             { if < 0: literal str           }
        JL      @@doneEntry
   LOCK DEC     [EDX-skew].StrRec.refCnt        { threadsafe dec refCount       }
        JNE     @@doneEntry
        LEA     EAX,[EDX-skew].StrRec.codePage  { if refCnt now zero, deallocate}
        CALL    _FreeMem
@@doneEntry:
        ADD     EBX,4
        DEC     ESI
        JNE     @@loop

        POP     ESI
        POP     EBX
end;
{$ENDIF}

и проходит через цикл один раз для каждого символа, а при выходе оттуда он вызывает _UStrCat:

procedure _UStrCat(var Dest: UnicodeString; const Source: UnicodeString);
asm
        { ->    EAX     pointer to dest }
        {       EDX source              }

        TEST    EDX,EDX       // Source empty, nop.
        JE      @@exit

        MOV     ECX,[EAX]     // ECX := Dest
        TEST    ECX,ECX       // Nil source => assignment
        JE      _UStrAsg

        PUSH    EBX
        PUSH    ESI
        PUSH    EDI
        MOV     EBX,EAX         // EBX := @Dest
        MOV     ESI,EDX         // ESI := Source
        CMP     ESI,ECX
        JE      @@appendSelf

        CMP     [ECX-skew].StrRec.elemSize,2
        JE      @@destIsUnicode
        CALL    _EnsureUnicodeString
        MOV     EDI,EAX
        MOV     ECX,EAX

@@destIsUnicode:
        PUSH    0
        CMP     [ESI-skew].StrRec.elemSize,2
        JE      @@sourceIsUnicode

        MOV     EDI,ECX
        MOV     EAX,ESI
        MOV     [ESP],ESI
        CALL    _UStrAddRef
        MOV     EAX,ESP
        CALL    _EnsureUnicodeString
        MOV     ESI,[ESP]
        MOV     ECX,EDI

@@sourceIsUnicode:
        MOV     EDI,[ECX-skew].StrRec.length  // EDI := Length(Dest)
        MOV     EDX,[ESI-skew].StrRec.length  // EDX := Length(Source)
        ADD     EDX,EDI         // EDX := (Length(Source) + Length(Dest)) * 2
        TEST    EDX,$C0000000
        JNZ     @@lengthOverflow

        MOV     EAX,EBX
        CALL    _UStrSetLength  // Set length of Dest
        MOV     EAX,ESI         // EAX := Source
        MOV     ECX,[ESI-skew].StrRec.length // ECX := Length(Source)

@@noTemp:
        MOV     EDX,[EBX]       // EDX := Dest
        SHL     EDI,1           // EDI to bytes (Length(Dest) * 2)
        ADD     EDX,EDI         // Offset EDX for destination of move
        SHL     ECX,1           // convert Length(Source) to bytes
        CALL    Move            // Move(Source, Dest + Length(Dest)*2, Length(Source)*2)
        MOV     EAX,ESP         // Need to clear out the temp we may have created above
        MOV     EDX,[EAX]
        TEST    EDX,EDX
        JE      @@tempEmpty

        CALL    _LStrClr

@@tempEmpty:
        POP     EAX
        POP     EDI
        POP     ESI
        POP     EBX
        RET

@@appendSelf:
        CMP     [ECX-skew].StrRec.elemSize,2
        JE      @@selfIsUnicode
        MOV     EAX,EBX
        XOR     EDX,EDX
        CALL    _EnsureUnicodeString
        MOV     ECX,EAX
        MOV     EAX,EBX

@@selfIsUnicode:
        MOV     EDI,[ECX-skew].StrRec.length
        MOV     EDX,EDI
        SHL     EDX,1
        TEST    EDX,$C0000000
        JNZ     @@lengthOverflow
        CALL    _UStrSetLength
        MOV     EAX,[EBX]
        MOV     ECX,EDI
        PUSH    0
        JMP     @@noTemp

@@lengthOverflow:
        JMP     _IntOver

@@exit:
end;

и проходит через всю эту рутину.

Мой "Результат" является строкой и, следовательно, Unicode. И мой GetFirstLastName возвращает строку, которая является Unicode. Преобразование набора символов не требуется.

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

Что они делают? Они необходимы? Если они не нужны, как я могу предотвратить вызов этих подпрограмм компилятором?

Ответы [ 4 ]

8 голосов
/ 29 июня 2010

LStrArrayClear не выполняется по циклу один раз на символ; он запускается один раз для каждой строки в массиве, чтобы уменьшить счетчик ссылок и освободить строку, если она достигнет 0. Он вставляется компилятором для очистки любых строк, выделенных как локальные переменные, или любых временных строк, которые он создает для хранения результатов две строки объединяются.

UStrCat - это процедура объединения строк. Это то, что string1 + string2 переводится под капотом. Компилятор определяет, что это должно привести к строке Unicode, поэтому он берет две входные строки, проверяет обе из них, чтобы увидеть, являются ли они самими Unicode, преобразует их, если они не (но у вас, так что преобразование получает пропущен,) затем устанавливает размер результата и копирует данные.

UStrCat необходим, и вы ничего не можете с этим поделать. LStrArrayClear - то, где все становится немного размытым. Когда вы создаете подпрограмму, которая работает со строками, компилятор должен выделить достаточно временных строк, чтобы обрабатывать все, что вы могли бы там делать, независимо от того, делали ли вы это когда-либо. А потом он должен очистить их потом. Таким образом, сокращение ненужных манипуляций со строками путем перемещения необычных задач в другие функции может помочь, особенно в тесном цикле.

Например, как часто вы видите что-то подобное?

if SomethingIsVeryWrong then
   raise ETimeToPanic.Create('Everybody panic! File ' + filename + ' is corrupt at address ' + intToStr(FailureAddress) + '!!!');

Это сообщение об ошибке содержит 5 различных подстрок. Даже если ему удастся оптимизировать вещи за счет их повторного использования, ему все равно нужно выделить как минимум две временные строки для этой работы. Допустим, это происходит внутри замкнутого цикла, и вы не ожидаете, что эта ошибка будет происходить часто, если она вообще возникает. Вы можете исключить временные строки путем разгрузки конкатенации строк в вызове Format. На самом деле это такая удобная оптимизация, что она встроена в Exception.

if SomethingIsVeryWrong then
   raise ETimeToPanic.CreateFmt('Everybody panic! File %s is corrupt at address %d!!!', [filename, FailureAddress]);

Да, вызов Format будет выполняться значительно медленнее, чем прямая конкатенация, но если что-то пойдет не так, он запускается только один раз, и производительность в любом случае меньше всего вас беспокоит.

6 голосов
/ 29 июня 2010

Компилятор часто создает временные файлы для хранения промежуточных значений выражений. Эти временные фильтры должны быть «доработаны» или очищены. Поскольку компилятор не знает, действительно ли использовался определенный темп (он пропустит финализацию, если увидит, что переменная по-прежнему равна nil), он всегда будет пытаться выполнить очистку.

2 голосов
/ 29 июня 2010

Взгляните на Класс TStringBuilder .

2 голосов
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...