代码之家  ›  专栏  ›  技术社区  ›  lkessler

我能消除额外的Unicode字符串调用吗(Delphi)

  •  2
  • lkessler  · 技术社区  · 15 年前

    我使用的是Delphi2009,在我的程序中,我一直在努力优化我所有的Delphi代码的速度和内存使用,特别是我的Unicode字符串处理。

    我有以下声明:

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

    当我调试该行时,从GetFirstLastName函数返回时,它会跟踪到系统单元中的例程UStrArrayClr:

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

    这将调用\u 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}
    

    并为每个字符在循环中运行一次,从循环中退出时,它将调用\u 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;
    

    我的“Result”是一个字符串,因此是Unicode。我的GetFirstLastName返回一个Unicode字符串。不需要转换字符集。

    他们在做什么?有必要吗?如果它们不是必需的,如何防止编译器调用这些例程?

    4 回复  |  直到 15 年前
        1
  •  8
  •   Mason Wheeler    15 年前

    LStrArrayClear不是每个字符运行一次循环;它对数组中的每个字符串运行一次,以减少ref计数,并在字符串达到0时释放该字符串。编译器插入此命令以清除任何分配为局部变量的字符串,或它创建的任何临时字符串,以保存两个串接的结果。

    UStrCat是字符串连接例程。是什么 string1 + string2 翻译成引擎盖下。编译器确定它应该产生一个Unicode字符串,因此它接受两个输入字符串,测试它们是否都是Unicode本身,如果它们不是(但你的是,因此转换被跳过),然后设置结果的大小并复制数据。

    这是必要的,你也无能为力。LStrArrayClear是事情变得有点模糊的地方。当您创建一个处理字符串的例程时,编译器必须分配足够的临时字符串来处理您可以在其中执行的所有操作,无论您是否执行过。然后它必须在事后清除它们。因此,通过将不常见的任务移动到其他函数来减少不必要的字符串操作会有所帮助,特别是在紧密循环中。

    例如,你多久会看到这样的事情?

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

    此错误消息包含5个不同的子字符串。即使它能够通过重用来优化它们,它仍然需要分配至少两个临时字符串来实现这一点。假设这是在一个紧密的循环中发生的,您不希望这种错误频繁发生,如果有的话。您可以通过将字符串连接卸载到格式调用中来消除临时字符串。这是一个非常方便的优化,事实上,它内置于 Exception .

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

        2
  •  6
  •   Thomas Ahle    15 年前

        4
  •  2
  •   Alex    15 年前