Я бы сделал следующие наблюдения:
- Решение о том, использовать или нет сеттер, должно основываться на таких факторах, как поддержание кода, правильность, удобочитаемость, а не производительность.
- Ваш эталон совершенно необоснован, так как операторы
if
оценивают каждый раз как False
. Реальный код, который устанавливает свойства, вероятно, изменит свойства за разумную долю времени, которое запускает установщик.
- Я ожидаю, что для многих примеров из реальной жизни сеттер будет работать быстрее без теста на равенство. Если бы этот тест оценивался как
True
каждый раз, тогда, очевидно, код был бы быстрее без него.
- Целочисленный установщик практически свободен, и на самом деле он медленнее, чем прямой доступ к полю.
- Время тратится в свойстве string. Здесь есть реальный выигрыш в производительности благодаря оптимизации теста
if
, который по возможности избегает кода назначения строки.
- Установщики будут работать быстрее, если вы добавите их, но не на значительную сумму.
Я верю, что любой код реального мира никогда не сможет обнаружить какие-либо из этих различий в производительности. В действительности узким местом будет получение значений, переданных сеттерам, а не времени, потраченного на сеттеры.
Основная ситуация, в которой такая защита if
является ценной, заключается в том, что изменение свойства стоит дорого. Например, возможно, это связано с отправкой сообщения Windows или попаданием в базу данных. Для свойства, обеспеченного полем, вы, вероятно, можете его взять или оставить.
В болтовне в комментариях преждевременная оптимизация задается вопросом, почему сравнение if FStringProp <> Value
происходит быстрее, чем задание FStringProp := Value
. Я исследовал немного дальше, и это было не совсем так, как я первоначально думал.
Оказывается, в if FStringProp <> Value
преобладает вызов System._UStrEqual
. Две переданные строки на самом деле не являются одной и той же ссылкой, поэтому необходимо сравнивать каждый символ. Однако этот код сильно оптимизирован, и, что самое важное, для сравнения есть всего 5 символов.
Вызов FStringProp := Value
переходит к System._UStrAsg
, и, поскольку Value
является литералом с отрицательным счетчиком ссылок, необходимо сделать новую строку. Версия кода на Pascal выглядит следующим образом:
procedure _UStrAsg(var Dest: UnicodeString; const Source: UnicodeString); // globals (need copy)
var
S, D: Pointer;
P: PStrRec;
Len: LongInt;
begin
S := Pointer(Source);
if S <> nil then
begin
if __StringRefCnt(Source) < 0 then // make copy of string literal
begin
Len := __StringLength(Source);
S := _NewUnicodeString(Len);
Move(Pointer(Source)^, S^, Len * SizeOf(WideChar));
end else
begin
P := PStrRec(PByte(S) - SizeOf(StrRec));
InterlockedIncrement(P.refCnt);
end;
end;
D := Pointer(Dest);
Pointer(Dest) := S;
_UStrClr(D);
end;
Ключевой частью этого является вызов _NewUnicodeString
, который, конечно, вызывает GetMem
. Меня нисколько не удивляет, что выделение кучи происходит значительно медленнее, чем сравнение 5 символов.