26 сентября 2019

Глобальная переменная - тормоз!

    В начале статьи Александра Алексеева "Что плохого в глобальных переменных?" меня зацепила фраза "быстрее в работе". Я хочу поспорить с ней и поддержать Александра в аргументах против глобальных переменных.
    Я давно заметил, одну маленькую неприятность с глобальными переменными. Проведем небольшой тест на создание, заполнение и чтение TStringList. Для начала выполним программу, в которой есть процедура с локальными переменными:
program lv;
{$APPTYPE CONSOLE}

uses
  System.Classes,
  System.SysUtils,
  System.Diagnostics;

const
  ciMaxIndex = 99999999;

procedure TestProc;
var
  sl: TStringList;
  st: String;
  t : TStopwatch;
  i, j: Integer;
begin
  t := TStopwatch.StartNew;
  for j := 1 to 10 do
    begin
      sl := TStringList.Create;
      try
        for i := 0 to ciMaxIndex do
          sl.Add(IntToStr(i));
      finally
        sl.Free;
      end;
    end;
  t.Stop;
  WriteLn('create: ', FormatFloat('0.0', t.ElapsedMilliseconds / 10));

  sl := TStringList.Create;
  try
    for i := 0 to ciMaxIndex do
      sl.Add(IntToStr(i));

    t := TStopwatch.StartNew;
    for j := 1 to 10 do
      for i := 0 to ciMaxIndex do
        st := sl[i];
    t.Stop;
    WriteLn('get: ', FormatFloat('0.0', t.ElapsedMilliseconds / 10));
  finally
    sl.Free;
  end;
end;

begin
  TestProc;
end.
Результат:
create: 8972,9
get: 1442,0
Теперь та же программа, но процедура в ней работает с использованием только глобальных переменных:
...
var
  sl: TStringList;
  st: String;
  t : TStopwatch;
  i, j: Integer;
procedure TestProc;
begin
...
end;
Результат:
create: 8979,0
get: 2710,2
Видно, что создание и заполнение TStringList прошло примерно с одинаковой скоростью. А вот чтение при использовании глобальных переменных почти в два раза медленнее!

Переменные цикла i и j перенесем в процедуру, что бы сделать их локальными переменными:
...
var
  sl: TStringList;
  st: String;
  t : TStopwatch;
procedure TestProc;
var
  i, j: Integer;
begin
...
end;
Результаты чтение лучше, но не на много:
create: 8978,9
get: 2585,4
Вывод: глобальные переменные - это палки в колеса производительности программы.

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

  1. Попробуйте оставить Stringlist глобальным, а st сделать локальным и не увидите падения скорости.
    Ваш второй пример тормозит не на извлечении а на записи в глобальную переменную.
    Может быть потому, что так вы заставляете не просто перекидывать указатель строки st на одну из строк StringList-а, а заставляете действительно копировать содержимое строки в другую область памяти.
    Ваш второй код просто делает другую работу, которая занимает другое время.

    ОтветитьУдалить
  2. Сделать переменную st локальной? Тогда о чем было бы писать? Вы сделаете переменную st локальной, я сделаю переменной st локальной. А Вася Пупкин сделает ее глобальной, как и все остальные переменные, потом весь код запихнет в один "begin-end." и расскажет всем, что его программа тормозит, т.к. ее нужно переписать на более "правильном языке", чем Delphi

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