Introduction
A potentially significant amount of performance gain and code size reduction can be achieved by making use of internal fields rather than designing applications with a blanket use of public properties. Public properties are frequently blindly considered a “good programming practice” without giving thought to whether that practice is applicable.
Optimization removes much, but not all, of the penalty of properties in the .NET environment. We present some of the philosophical arguments in the conclusion.
Arguments for Public Properties
When a value requires exposure to other developer’s assemblies, defining it as a property has merit. For example, in version 1 of our assembly we define the following property and field:
public int MyProperty { get; set; }
public int MyField;
In time, others are now using our assembly and are dependent on it. Later we find an issue that we must address, such as the values must always be positive. We can change our property as below to be tolerant to consumers of our assembly passing negative values.
public int MyProperty
{
get { return this.myProperty; }
set { this.myProperty = value >= 0 ? value : 0; }
}
private int myProperty;
Users of our assembly do not have to recompile to make use of our update. However, we cannot do the same to our field. To add the value checking code to our field, we must first turn it into a property, and if we do so, we break compatibility with previous versions. This would make public fields not a “good programming practice.”
Mechanics of Properties and Fields
There is a cost to using a property rather than a field. A field is essentially a location in memory that we can get and set values with a single instruction to the CPU (“mov” in the case of the X86). However a property is a way of auto-generating code. The two methods below are the resulting code:
public int get_MyProperty()
{
return this.myProperty;
}
public void set_MyProperty(int value)
{
this.myProperty = value >= 0 ? value : 0;
}
private int myProperty;
We are no longer using a single CPU instruction to access our value, but are calling methods to get or set the value. The CPU must now execute a call instruction to the method and the method must do the “mov” instruction and then a return instruction. Adding to the additional instructions are the pushing and popping of the instruction pointer to the stack. The result is at least five times the amount of work for a property as compared to a field. There are many techniques used in CPU hardware to increase efficiency, such as doing the pushing and popping in parallel, but that is beyond our scope.
Internal Fields
We see that using properties for public values has merit and a cost that we must bear. However, this weight does not have to be applied to values that are used internally within our assembly. If a given value, at least for the current version, is used only within our assembly, we should not expose it publicly, but rather keep it internal as below.
internal int MyValue;
The overhead of properties is eliminated by using a field. If we are now coding version 2 of our assembly and find that we have to add our validation logic, we can simply change our field into a property and recompile. Typically, no other parts of our assembly are impacted, and no compatibility issues arise.
internal int MyValue
{
get { return this. myValue; }
set { this. myValue = value >= 0 ? value : 0; }
}
private int myValue;
Accessing Public Properties as Internal Fields
We can gain some internal efficiency with public properties by internally exposing the underlying values. This allows our assembly to make use of the value as a field while allowing for updates in future versions.
public int MyProperty
{
get { return this.myProperty; }
set { this.myProperty = value; }
}
internal int myProperty;
If we find we have to add our validation logic, we can do a search on all internal references to our field and change them as appropriate to using the public property. Since the changes we are making impact only our assembly, there is no impact on other users.
Public Fields When We Own the Solution
When multiple assemblies are used in our solution, but those assemblies are not shared outside of our solution, we can make use of public fields in the same way we have described for internal fields.
Even when we have a large team working on a single solution, we can expose values as public fields, as the “MyValue
” example above. Anyone can change a field into a property when necessary without disrupting the efforts of the other members of the team. By employing this policy, we can achieve a balance of efficiency and updatability.
Optimization Removes Much of the Weight
The compilers in the Microsoft .NET Framework offer significant optimization. The “debug” version performs as described above, but the “release” code removes the call to the getter and setter and accesses the backing field directly.
This optimization performs remarkably, but is not complete. Incrementing a field is a single instruction, while incrementing a property remains three. This optimization may also be present in future versions of the compiler.
libraryClass.MyField++;
inc dword ptr [esi+8]
libraryClass.MyProperty++;
mov eax,dword ptr [esi+4]
inc eax
mov dword ptr [esi+4],eax
Note: To view the disassembly of optimized code in Visual Studio 2008, you must change some of the default options. Click "Tools" then "Options".
In the "Debugging General" uncheck "Enable Just My Code (Managed Only)" and "Suppress JIT optimization on module Load (Managed Only)".
However, a property’s getter and setter definitions remain and the JIT compiler must perform the optimization each time the application loads. Fields are, in essence, optimized before the JIT compiler starts, and require one entry into the assembly’s manifest which results in a smaller file.
Philosophical Arguments
Some will argue that it is simplest to use properties always and let the compiler make the decisions. We suggest that when you write code, you should write what you intend to have executed. Properties sometimes execute as fields, whereas fields always execute as fields.
If you write for more than one environment, then you cannot assume the optimizations will carry forward. The concepts of properties and fields are essentially universal, even with 8-bit embedded processors, but optimization is not.
In many cases, such as many data binding scenarios, properties are required and fields are not an option.
Many times, especially in internal applications, the debug version is the version that goes into production use. When this is the case, the optimization advantage is gone.
Compile time during application development, application start time, and file sizes are all impacted by the choice of properties versus fields. For one instance, the impact is insignificant. For complex applications, the impact is real.
Conclusion
The great value of public properties comes into play when we are developing assemblies that are used by others outside of our solution. When we own the solution and make blind use of properties, we add significant, unnecessary overhead to our development effort and impair the application’s performance.
For a solo programmer or a large team, a refined policy of using fields and properties wisely can result in a performance gain and code size reduction.