Delphi; производительность передачи строк const по сравнению с передачей строк var

Быстрый один; Правильно ли я считаю, что передача строки методу "как CONST" требует больше затрат, чем передача строки как "VAR"? Компилятор заставит Delphi сделать копию строки и затем передать копию, если строковый параметр объявлен как CONST, верно?

Причина вопроса немного утомительна; у нас есть устаревшая утилита Delphi 5, дни которой действительно сочтены (замена находится в стадии разработки). Он выполняет большой объем обработки строк, часто передавая строки размером 1-2 Кб между различными функциями и процедурами. Во всем коде соблюдается "правильное" наблюдение за использованием CONST или VAR для передачи параметров (в зависимости от выполняемой работы). Мы просто ищем несколько "быстрых побед", которые могли бы сократить время выполнения на несколько микросекунд, чтобы отодвинуть нас, пока не будет готова новая версия. Мы подумали об изменении диспетчера памяти с Delphi 5 по умолчанию на FastMM, и мы также подумали, стоит ли менять способ передачи строк - поскольку код работает нормально, когда строки передаются как const, мы не увидеть проблему, если мы изменили эти объявления на var - код в этом методе не будет менять строку.

Но действительно ли это будет иметь значение в реальном выражении? (На самом деле программа просто выполняет большой объем обработки этих строк размером 1 КБ + несколько сотен строк в минуту в часы пиковой нагрузки). При переписывании эти строки хранятся в переменных объекта / класса, поэтому они на самом деле вообще не копируются / не передаются одинаково, но в унаследованном коде это очень похоже на "старый школьный" паскаль.

Естественно, мы профилируем общий прогон программы, чтобы увидеть, что мы изменили, но нет смысла пытаться сделать это, если мы категорически не правы относительно того, как передача строк работает в первом случае!

5 ответов

Решение

Нет, не должно быть никакой разницы в производительности между const или же var в твоем случае. В обоих случаях указатель на строку передается в качестве параметра. Если параметр const компилятор просто запрещает любые изменения в нем. Обратите внимание, что это не исключает внесения изменений в строку, если вам сложно:

procedure TForm1.Button1Click(Sender: TObject);
var
  s: string;
begin
  s := 'foo bar baz';
  UniqueString(s);
  SetConstCaption(s);
  Caption := s;
end;

procedure TForm1.SetConstCaption(const AValue: string);
var
  P: PChar;
begin
  P := PChar(AValue);
  P[3] := '?';
  Caption := AValue;
end;

Это фактически изменит локальную строковую переменную в вызывающем методе, доказывая, что передается только указатель на нее.

Но обязательно используйте FastMM4, это должно оказать гораздо большее влияние на производительность.

const для параметров в Delphi, по сути, означает "я не собираюсь изменять это, и мне также все равно, будет ли это передано по значению или по ссылке, - то, что наиболее эффективно, мне подходит". Жирная часть важна, потому что она на самом деле наблюдаема. Рассмотрим этот код:

type TFoo =
  record
    x: integer;
    //dummy: array[1..10] of integer;
  end;

procedure Foo(var x1: TFoo; const x2: TFoo);
begin
  WriteLn(x1.x);
  WriteLn(x2.x);

  Inc(x1.x);
  WriteLn;

  WriteLn(x1.x);
  WriteLn(x2.x);
end;

var
  x: TFoo;
begin
  Foo(x, x);
  ReadLn;
end.

Хитрость в том, что мы передаем одну и ту же переменную как var и в качестве const, так что наша функция может мутировать через один аргумент, и посмотреть, влияет ли это на другой. Если вы попробуете это с кодом выше, вы увидите, что увеличение x1.x внутри Foo не меняется x2.x, так x2 был передан по значению. Но попробуйте раскомментировать объявление массива в TFoo, так что его размер становится больше, и запустить его снова - и вы увидите, как x2.x теперь псевдонимы x1.xтак что у нас есть переход по ссылке x2 сейчас!

Подвести итог, const всегда является наиболее эффективным способом передачи параметра любого типа, но вы не должны делать никаких предположений о том, есть ли у вас копия значения, которое было передано вызывающей стороной, или ссылка на какое-либо (потенциально мутировавшее другим кодом, который вы можете звоните) местоположение.

Это действительно комментарий, но длинный, так что терпите меня.

О передаче так называемой строки по значению

Delphi всегда проходит string а также ansistring (WideStrings и ShortStrings исключены) по ссылке, как указатель.
Поэтому строки никогда не передаются по значению.
Это можно легко проверить, передав 100-мегабайтные строки.

До тех пор, пока вы не измените их внутри тела вызываемой обычной строки, передача занимает O(1) времени (и с небольшой константой при этом)

Однако при передаче строки без var или же const оговорка, Delphi делает три вещи.

  1. Увеличьте счетчик ссылок на строку.
  2. поместите неявный блок try-finally вокруг процедуры, чтобы уменьшить количество ссылок на строковый параметр.
  3. Когда строка изменяется (и только потом), Delphi создает копию строки, уменьшает количество ссылок на переданную строку и использует копию в оставшейся части процедуры.
    Это подделки pass by value при этом.

О передаче по ссылке (указателю)

Когда строка передается как const или же varDelphi также передает ссылку (указатель), однако:

  1. Счетчик ссылок строки не увеличивается. (крошечное, крошечное увеличение скорости)
  2. Никакая неявная попытка / наконец-то не включается в процедуру, потому что она не нужна. Это часть 1 почему const/var строковые параметры выполняются быстрее.
  3. Когда строка изменяется внутри подпрограммы, никакая копия не делает фактическую строку измененной. За const параметры компилятор запрещает чередование строк. Это вторая часть почему var/const строковые параметры работают быстрее.
  4. Однако,если вам нужно создать локальную переменную для присвоения строки; Delphi копирует строку:-) и помещает неявный блок try/finally, устраняя 99%+ прироста скорости const строковый параметр.

Надеюсь, что это проливает некоторый свет на проблему.
Отказ от ответственности: большая часть этой информации приходит отсюда, здесь и здесь

Компилятор не будет делать копию строки при использовании const afaik. Использование const избавляет вас от необходимости увеличивать / уменьшать refcounter для строки, которую вы используете.

Вы получите больший прирост производительности, обновив диспетчер памяти до FastMM, и, поскольку вы много делаете со строками, подумайте об использовании библиотеки FastCode.

Const это уже самый эффективный способ передачи параметров в функцию. Это позволяет избежать создания копии (по умолчанию, по значению) или даже передачи указателя (var, по ссылке).
Это особенно верно для струнных и действительно было способом пойти, когда вычислительная мощность была ограничена и не была потрачена впустую (отсюда и название "старой школы").

ИМО, const должен был быть соглашением по умолчанию, так как программист мог изменить его, когда это действительно необходимо, по значению или по var. Это было бы больше в соответствии с общей безопасностью Паскаля (как, например, ограничение возможности стрельбы в ногу).

Мой 2¢...

Другие вопросы по тегам