I had no intentions to write any additional posts about TAnyValue. I thought it was more then enough and in my mind closed the subject. But then Stefan Glienke jumped in with great comments and test of his own. And as I love such discussions and challenges, I had to reopen the case. It seemed that under XE, XE2 and XE3 TValue was a lot faster. I already mentioned that. But now it seemed it was actually faster then TAnyValue for integer and float types. Something was not right and with Stefans help, I upgraded and tweaked the unit, until I got something I feel is truly fast now and uses little memory out of the box. The comments and previous post can be found here. What we did was:

  • Conditional defines were changed. Now out of the box TAnyValue uses very little memory (less then variants for instance and same as TOmniValue), way less then TValue. It trades a little bit of speed for that, but very little indeed as you will see in the tests. You can enable speed boost by defining “AnyValue_UseLargeNumbers” which uses more memory but is faster. It is advisable to enable this if you use mainly Int64, TDateTime or Extended types. You can disable interfaces as before and so save even more memory.
  • Stefan proposed removing inline from implicit class operators
  • Instead of using Move for copying Extended and Int64 values to byte array Stefan proposed following solution
procedure TAnyValue.SetAsFloat(const Value: Extended);
begin
  FValueType := avtFloat;
{$IFDEF AnyValue_UseLargeNumbers}
  FSimpleData.VExtended := Value;
{$ELSE}
  if Length(FComplexData) <> SizeOf(Extended) then
    SetLength(FComplexData, SizeOf(Extended));
  PExtended(@FComplexData[0])^ := Value;
{$ENDIF}
end;
 
function TAnyValue.GetAsFloat: Extended;
begin
  case FValueType of
    avtInt64: Result := GetAsInt64;
    avtInteger: Result := GetAsInteger;
    avtCardinal: Result := GetAsCardinal;
    avtBoolean: Result := Integer(GetAsBoolean);
    avtString: Result := StrToFloat(GetAsString);
    avtAnsiString: Result := StrToFloat(string(GetAsAnsiString));
    avtWideString: Result := StrToFloat(string(GetAsWideString));
    avtFloat:
      begin
        {$IFDEF AnyValue_UseLargeNumbers}
          Result := FSimpleData.VExtended;
        {$ELSE}
          Result := PExtended(@FComplexData[0])^;
        {$ENDIF}
      end
    else
      raise Exception.Create('Value cannot be converted to Extended');
  end;
end;

Doing all that, the speed was still considerably slower. Stefan claimed he got down to 200ms on the Extended tests, while I could not drop bellow 700ms. Something smelled there, so I asked him to send me the exact unit he used. When I got the unit I compared it with mine via Kdiff3 (great tool by the way). I noticed he removed inline only from the following class operator

  class operator Implicit(const Value: Extended): TAnyValue;

but left in on this one

  class operator Implicit(const Value: TAnyValue): Extended; inline;

I did the same and sure enough time dropped down to 200ms. I have no idea why such a difference with or without inline at that particular spot. Maybe someone more at home with assembler and inline mechanism can cast some light onto the matter. When I saw the impact I removed other inline directives and gained a lot for strings also. So without further delay, here are the complete tests for 2010 and XE3. It is also worth mentioning that Stefan made a wrapper for 2010 TValue implementation to bring it on par with XE3 speed. Maybe he will share it for those who have to use 2010 and TValue.

Delphi 2010 test (times are in ms for 10000000 operations):

Type Variants TValue TAnyValue TOmniValue TVariableRec
j := I 157 3176 69 82 65
j := I/5 184 3308 214 3410 190
j := IntToStr(I) 4562 10610 3857 6856 2917
ALL 5471 18915 5025 10640 3167

Delphi XE3 test (times are in ms for 10000000 operations):

Type Variants TValue TAnyValue TOmniValue TVariableRec
j := I 166 176 81 412 62
j := I/5 302 450 235 4007 328
j := IntToStr(I) 3429 5184 1701 6082 1945
ALL 4063 5731 3016 11407 2083

It is visible how much did I gain under XE3 for strings removing that inline. Why I don’t know as I already told. I will wrap it up here. I already bothered you to much with details and myself gave to much time into it. But hey it was worth it.

The test application can be downloaded here.