16 марта 2010

Delphi 2010: TValue - "тормоз"!

Перевод. Оригинал "TValue is very slow" (© TURBU Tech) дополнен моими тестами и комментариями.

   Справочная система Delphi 2010 описывает тип TValue, используемый модулем RTTI для хранения значений произвольных типов, как "облегченная версия типа Variant". Увидев это, я задался вопросом, насколько он легковеснее? Как быстро работает TValue?
   К счастью, среди известных мне новых возможностей Delphi 2010 – модуль диагностики (Diagnostics), который предоставляет нам объект TStopwatch – простой таймер позволяющий засечь время выполнения операций и тем самым облегчить написание простого теста скорости.
   Я ожидал, что скорость работы TValue будет сравнима со скоростью Variant, или возможно немного больше. Для проверки, я написал следующую программку:

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils, rtti, diagnostics;

const
  HUNDRED_MILLION = 100000000;

procedure tryTValue;
var
  i: integer;
  j: TValue;
  value: integer;
begin
  for I := 1 to HUNDRED_MILLION do
    begin
      j := i;
      value := j.AsInteger;
    end;
end;

procedure tryVariants;
var
  i: integer;
  j: variant;
  value: integer;
begin
  for I := 1 to HUNDRED_MILLION do
    begin
      j := i;
      value := j;
    end;
end;

var
  stopwatch: TStopWatch;
begin
  try
    stopwatch := TStopWatch.StartNew;
    tryVariants;
    stopwatch.Stop;
    writeln('Variants: ', stopwatch.ElapsedMilliseconds);

    stopwatch := TStopWatch.StartNew;
    tryTValue;
    stopwatch.Stop;
    writeln('TValues: ', stopwatch.ElapsedMilliseconds);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  readln;
end.

   Конечно, этот тест - не исчерпывающая проверка возможностей TValue, но результаты поучительны. Когда я запустил его на своем рабочем компьютере (высокопроизводительный ноутбук Alienware), тест Variant выполнился почти мгновенно, а тест TValue выполнялся так долго, что я решил, что он завис и остановил его.
   Затем я запустил тест снова и получил следующие результаты (в миллисекундах):
Variants: 717
TValues: 31131
   По крайней мере, для этой конкретной операции, TValue в 43.52167832167832 раза медленнее, чем Variant!

От меня.

   Вообще то 31131 поделить на 717 будет равно 43.41841004 ;)
   Что бы проверить результат, я запустил несколько тестов на своей домашней "dev machine".

Тест #1. Запустил исходный тест и получил такую же разницу скорости - в 43.17657992 раза:
Variants: 538
TValues: 23229.
Тест #2. Переставил местами вызов tryTValue и tryVariants и снова получил 43.376404494:
TValues: 23163
Variants: 534.
Тест #3. Закомментировав обратное присвоение TValue и Variant целой переменной ("value := j.AsInteger" и "value := j") я получил для TValue более "веселый" результат:
Variants: 535
TValues: 5168
Присвоение целого значения TValue медленнее присвоения целого значения Variant всего в 9.65981308 раза. А значит основное падение скорости вызвано AsInteger.

Тест #4. В процедуре tryTValue я заменил "value := j.AsInteger" на "value := j.AsOrdinal"
Variants: 536
TValues: 5862
В результате общее падение скорости всего в 10.93656716 раза!

Тест #5. AsOrdinal возвращает значение типа Int64, поэтому в процедуре tryTValue я заменил "value := j.AsInteger" на "value := j.AsInt64" и получил падение скорости в 54,25981308 раза!!!
Variants: 535
TValues: 29029
Вывод: "value := j.AsOrdinal" у TValue работает почти так же быстро, как и "value := j" для Variant. А методы AsInteger и AsInt64 – лучше не использовать. Но все равно, главный вывод: TValue – "тормоз"!

   Напоследок, я проверил с помощью функции SizeOf число байт, которые занимали переменные: переменная типа Variant занимала – 24 байта, а TValue – всего 16. Может в этом проявляется "облегченность" типа TValue? Тогда, храните числа в integer – они будут занимать 4 байта ;)

4 комментария:

  1. Также, очень интересное исследование провёл автр библиотеки OmniThreadLibrary(
    http://17slon.com/blogs/gabr/2010/03/..d-comparison-variant-tvalue-and.html).
    Всё не так плохо, просто нужно помнить несколько простых правил при работе с такими типами и использовать их к месту.

    ОтветитьУдалить
  2. Анонимный18 марта, 2010 12:51

    Автору неплохо было бы вспомнить, как определяется точность результата деления и что такое значащие разряды

    ОтветитьУдалить
  3. Анонимный30 июля, 2010 16:15

    >Напоследок, я проверил с помощью функции SizeOf число байт, которые занимали переменные: переменная типа Variant занимала – 24 байта, а TValue – всего 16.

    Хм, у меня с точностью наоборот

    ОтветитьУдалить