Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 895771
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 15, 20262026-05-15T14:36:46+00:00 2026-05-15T14:36:46+00:00

I’m using Delphi 2009. In my program, I have been working very hard to

  • 0

I’m using Delphi 2009. In my program, I have been working very hard to optimize all my Delphi code for speed and memory use, especially my Unicode string handling.

I have the following statement:

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

When I debug that line, upon return from the GetFirstLastName function, it traces into the routine _UStrArrayClr in the System unit:

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

This calls _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}

and runs through the loop once for each character, and on exit from there it calls _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;

and runs through the whole of that routine.

My “Result” is a string and is thus Unicode. And my GetFirstLastName returns a string which is Unicode. No conversion of character set should be needed.

I can’t really tell what these System procedures are doing, but they are adding a lot of overhead to my routine.

What are they doing? Are they necessary? If they aren’t necessary, how can I prevent the compiler from calling those routines?

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-15T14:36:47+00:00Added an answer on May 15, 2026 at 2:36 pm

    LStrArrayClear isn’t running over a loop once per character; it’s running once per string in the array, to decrement the ref count and free the string if it hits 0. This is inserted by the compiler to clean up any strings allocated as local variables, or any temporary strings it creates to hold the results of two strings being concatenated.

    UStrCat is the string concatenation routine. It’s what string1 + string2 translates to under the hood. The compiler determines that it’s supposed to result in a Unicode string, so it takes the two input strings, tests both of them to see if they’re Unicode themselves, converts them if they’re not (but yours are, so the conversion gets skipped,) then sets the size of the result and copies the data.

    UStrCat is necessary, and there’s not much you can do about it. LStrArrayClear is where things get a bit fuzzier. When you create a routine that works with strings, the compiler has to allocate enough temporary strings to handle everything you could do in there, whether or not you ever do it. And then it has to clear them afterwards. So cutting down on unnecessary string manipulation by moving uncommon tasks to other functions can help, especially in a tight loop.

    For example, how often do you see something like this?

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

    This error message contains 5 different substrings. Even if it manages to optimize things by reusing them, it still needs to allocate at least two temporary strings to make this work. Let’s say this is taking place inside a tight loop and you don’t expect this error to happen frequently, if at all. You can eliminate the temporary strings by offloading the string concatenation into a Format call. That’s such a convenient optimization, in fact, that it’s built into Exception.

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

    Yes, a call to Format will run significantly slower than straight concatenation, but if something goes wrong, it only runs once and performance is the least of your worries anyway.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.