Friday, March 2, 2012

Speeding Intraweb up again

A few weeks ago I wrote about how to hack Intraweb classes to speed it up a little. I've found another method that could be faster, using a similar approach: This method is TextToHTMLStringLiteral, from TIWBaseForm class, declared in IWBaseForm.pas. This method is used inside the TIWForm.GenerateForm method, once for each hidden field:
class function TIWBaseForm.TextToHTMLStringLiteral(aText: string): string;
var
  f : integer;
begin
  Result := '';
  for f := 1 to Length(aText) do begin
    if (AnsiChar(aText[f]) in ['A'..'Z', 'a'..'z', '0'..'9', ',', '.', '-', '_']) then  begin
      Result := Result + aText[f];
    end else begin
      Result := Format('%s&#%d;', [Result, Ord(aText[f])]);
    end;
  end;
end;
Here we can see the same pattern we found in the previous modified methods. We will use the same kind of construction - and the same runtime patching technique - to replace this method with a faster one, during execution. The new TextToHTMLStringLiteral is shown below:
class function TIWBaseForm.TextToHTMLStringLiteral(aText: string): string;
const
  NoConversion = ['A'..'Z', 'a'..'z', '0'..'9', ',', '.', '-', '_'];
var
  Sp, Rp: PChar;
begin
  SetLength(Result, Length(AText) * 8);
  Sp := PAnsiChar(AText);
  Rp := PAnsiChar(Result);
  while Sp^ <> #0 do
  begin
    if (Sp^ in NoConversion) then
    begin
      Rp^ := Sp^;
    end else
    begin
      Inc(Rp, FormatBuf(Rp^, 7, '&#%.4d;', 7, [Ord(Sp^)]) - 1);
    end;
    Inc(Rp);
    Inc(Sp);
  end;
  SetLength(Result, Rp - PAnsiChar(Result));
end;
I will not enter in much detail about the implementation, but I think it is quite simple, and it uses the same approach used in the previous post.

The results

Again, I did a simple benchmark application so I could see if there is some gain using this new method.

Original code: 5.10 seconds

Modified code: 0.82 seconds


for 1,000,000 function calls. Quite impressive, isn't it? Of course, this benchmark used a specific string, and a different string will give different results.

This time, the new method is more than 6 times faster than the original method, in a single thread. I suspect, based on my previous post about String concatenation in Delphi, that the new code can be even faster - and more multi-core friendly - in a multi-threaded application (like IW applications). I will create this benchmark application and write a blog post about the results soon.
Checking one of my IW applications, I've found forms with more than 100 hidden fields in it. The time gain will be insignificant (less than 1 millisecond), but this is not the point. The main point when I try to make my applications faster, specially Intraweb applications, is: make it run as fast as you can AND make it more multi-core friendly.

The downloads

You can download an updated IntrawebPatch.pas unit containing this new patch (and the old ones) here.

Enjoy!

3 comments:

Danilo Casa said...

I Alexander, very interesting article!
I have tried your code, but i can't compile my Intraweb project including these units...
(I have IW10, with Delphi 2010).

[RtlVclOptimize.pas] - error at this line:

LocaleOverride[sizeof(LocaleOverride) - 1] := #0;

[DCC Error] RtlVclOptimize.pas(3298): E1012 Constant expression violates subrange bounds

-----------

[IntrawebPatch.pas] - error at this line

Function TIWBaseFormHack.TextToHTMLStringLiteral(aText: string): string;
-->
[DCC Error] IntrawebPatch.pas(171): E2010 Incompatible types: 'AnsiChar' and 'Char'

Any suggestions?

Alexandre Machado said...

Hi Danilo,

I didn't check Delphi 2010 compatibility before releasing the code, sorry. I saw today that Andreas Hausladen's RtlVclOptimize.pas has various incompatibilities with more recent versions of Delphi. I'm creating a new patch, independent from RtlVclOptimize.pas. I will post the new code today.

Best regards

Danilo Casa said...

Hi, and thank you very much for the quick response!
There are really interesting ideas in this blog!