Question

I need to read the length of a string s really often in my program. I set the s string once so I could memorize its size in a variable (NoOfChars) like this:

type
  MyClass= class
    Public
     s: string;
     NoOfchars: integer;  
  end;

procedure MyClass.SetS(const MyString: String); 
begin
  s:= MyString;
  NoOfchars := Length(s); // <-- store it once, read it often
end;

I think I read somewhere that Length(s) is as fast as reading a constant since it only accesses the length of the string stored in the "hidden" field in the front of the string. It makes sense but since we don't have the source code, we can only guess. Maybe there are also other computations involved?

So, should I use Length(s) or should I memorize the length in a variable?

Was it helpful?

Solution 2

FWIW you can easily get a feeling for how much faster the variable is over using Length with a simple test. Of course this does not test all the circumstances and eventualities in your production code but you get a rough estimate.

program Project1;

{$APPTYPE CONSOLE}

uses
  Diagnostics;

var
  sw: TStopwatch;
  s: string;
  len, i, x: Integer;
begin
  s := 'somestring';
  len := Length(s);
  sw := TStopwatch.StartNew;
  for i := 0 to MaxInt do
  begin
    x := len;
    if x = 0 then
  end;
  Writeln('using variable: ', sw.ElapsedMilliseconds);

  sw := TStopwatch.StartNew;
  for i := 0 to MaxInt do
  begin
    x := Length(s);
    if x = 0 then
  end;
    Writeln('using Length(s): ', sw.ElapsedMilliseconds);
  Readln;
end.

In a VM I get numbers like this (release config):

using variable: 587
using Length(s): 1255

OTHER TIPS

You do have the source code. It's in the System unit.

function _UStrLen(const S: UnicodeString): Integer;
begin
  Result := Longint(S);
  if Result <> 0 then                // PStrRec should be used here, but
    Result := PLongint(Result - 4)^; // a private symbol can't be inlined
end;

This helper function is marked as inline and indeed the compiler does inline it. This program:

{$APPTYPE CONSOLE}

var
  i: Integer;
  s: string;

begin
  i := Length(s);
  Writeln(i);
end.

is compiled to the following:

....
Project1.dpr.10: i := Length(s);
004060E3 A19CAB4000       mov eax,[$0040ab9c]   // $0040ab9c holds the variable s
004060E8 85C0             test eax,eax          // test for nil
004060EA 7405             jz $004060f1          
004060EC 83E804           sub eax,$04           // offset to length
004060EF 8B00             mov eax,[eax]         // read length into eax
004060F1 8BD8             mov ebx,eax           // compiler optimises i into ebx
....

The code is pretty simple. The string variable is checked for being nil. If so the answer is zero. Otherwise the length is read from the appropriate offset from the string.

This is certainly a pretty quick function. However, it won't be as quick to call Length, even inlined as it is, as it is to read from a local variable. Indeed the compiler may well optimise a local variable into a register.

So if performance matters, reading into a local variable could improve performance. But you must time the real world code to know whether or not the performance difference actually matters.

Personally I tend to store string length in a local variable for reasons of readability if I need to refer to the length more than once. If I know the value is the same every time I call the function, it is clear to express that by storing the value in a local.

When reading the length of a string, Delphi reads the hidden length field of the string. But reading that hidden fields needs a few more cpu instructions than reading an Integer. So Length() is fast, but not as fast as reading your NoOfChars field.

In theory the compiler might be able to further optimize reading the length depending on the context. So I'd recommend that you do some speed measurements in the real word application.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top