Referenced constants - are they embedded in the referencing code?

D

Dan Holmes

If i have two projects (A and B), will the use of a constant in A that was created in B cause a "get" method to be
created to get the constant or will have just have the value? I think the latter because i compiled the below into
different assemblies and then decompiled them with .netreflector.

The below is the IL. i am not good at reading IL but i don't see a method call in here anywhere. I guess this brings
up the point. If i change B's constant but don't recompile A, how does a get the new value? Maybe i am missing
something...

..class public auto ansi beforefieldinit SqlAuthenticate
extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: ldc.i4.0
L_0002: conv.i8
L_0003: stfld int64 Authenticate.SqlAuthenticate::_authenticationMode
L_0008: ldarg.0
L_0009: call instance void [mscorlib]System.Object::.ctor()
L_000e: nop
L_000f: nop
L_0010: ldarg.0
L_0011: ldc.i4.3
L_0012: conv.i8
L_0013: stfld int64 Authenticate.SqlAuthenticate::_authenticationMode
L_0018: nop
L_0019: ret
}


.field private int64 _authenticationMode

}





namespace StaticConstants
{
public static class SqlServerAutenticationModes
{
public const long SqlServer = 1;
public const long Windows = 2;
public const long Mixed = 3;
}
}

namespace Authenticate
{
public class SqlAuthenticate
{
private long _authenticationMode = 0;
public SqlAuthenticate()
{
_authenticationMode = StaticConstants.SqlServerAutenticationModes.Mixed;
}
}
}


dan
 
P

Peter Duniho

Dan said:
If i have two projects (A and B), will the use of a constant in A that
was created in B cause a "get" method to be created to get the constant
or will have just have the value? I think the latter because i compiled
the below into different assemblies and then decompiled them with
.netreflector.

Yes, just the latter.
The below is the IL. i am not good at reading IL but i don't see a
method call in here anywhere.

Well, you have a method call. But no, not one to retrieve your
constant. Constants, actually. You appear to be using two, though of
course one might just have been a hard-coded literal in the code. The
"ldc.i4" instruction is where your constants are used. The first one
the constant is 0, the second one it's 3.
I guess this brings up the point. If i
change B's constant but don't recompile A, how does a get the new
value? Maybe i am missing something...

Nope. You're not missing anything. Constants are compile-time. You
have to recompile to see the new value.

If you want some kind of thing that's like a constant but can be updated
dynamically, use a read-only field or, even better, just make a property
(probably backed by a read-only field, but it could just be hard-coded
into the getter if you like).

Pete
 
D

Dan Holmes

Yes, just the latter.


Well, you have a method call. But no, not one to retrieve your constant.
Constants, actually. You appear to be using two, though of course one
might just have been a hard-coded literal in the code. The "ldc.i4"
instruction is where your constants are used. The first one the constant
is 0, the second one it's 3.


Nope. You're not missing anything. Constants are compile-time. You have
to recompile to see the new value.

If you want some kind of thing that's like a constant but can be updated
dynamically, use a read-only field or, even better, just make a property
(probably backed by a read-only field, but it could just be hard-coded
into the getter if you like).

Pete

The next question might be "What does that do to performance?" Well...at a million iterations in a for loop.

All the code is here so if i did something wrong in the timings it can be pointed out.

D:\Sandbox\StaticConstants\bin\Debug>TimingConstants.exe
Constant from static class assignment time 7ms 27175ticks
Readonly field from static class assignment time 7ms 27032ticks
Readonly property from static class assignment time 25ms 92980ticks
Constant from propget in non-static class assignment time 23ms 83494ticks

Yeah, there was a 3x slowdown, but saving 18 ms is usually something none of us are concerned with (unless you are in
realtime scenarios - in which case you likely aren't using c#).

A good spot between safety an performance seems to be readonly fields.

namespace TimingConstants
{
class Program
{
static void Main(string[] args)
{
long j = StaticConstants.StaticSqlServerAuthenticationModes.Mixed;
long iterations = 1000000;
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
for (int i = 0; i < iterations; i++)
{
j = StaticConstants.StaticSqlServerAuthenticationModes.Mixed;
}
sw.Stop();
Console.WriteLine("Constant from static class assignment time {0}ms {1}ticks", sw.ElapsedMilliseconds,
sw.ElapsedTicks);

sw.Reset();
sw.Start();
for (int i = 0; i < iterations; i++)
{
j = StaticConstants.StaticSqlServerAuthenticationModes.MixedField;
}
sw.Stop();
Console.WriteLine("Readonly field from static class assignment time {0}ms {1}ticks",
sw.ElapsedMilliseconds, sw.ElapsedTicks);

sw.Reset();
sw.Start();
for (int i = 0; i < iterations; i++)
{
j = StaticConstants.StaticSqlServerAuthenticationModes.MixedProperty;
}
sw.Stop();
Console.WriteLine("Readonly property from static class assignment time {0}ms {1}ticks",
sw.ElapsedMilliseconds, sw.ElapsedTicks);

sw.Reset();
sw.Start();
StaticConstants.NonStaticSqlServerAuthenticationModes sm = new
StaticConstants.NonStaticSqlServerAuthenticationModes();
for (int i = 0; i < iterations; i++)
{
j = sm.Mixed;
}
sw.Stop();
Console.WriteLine("Constant from propget in non-static class assignment time {0}ms {1}ticks",
sw.ElapsedMilliseconds, sw.ElapsedTicks);
}
}
}

namespace StaticConstants
{
public static class StaticSqlServerAuthenticationModes
{
public const long SqlServer = 1;
public const long Windows = 2;
public const long Mixed = 3;

public static readonly long SqlServerField = SqlServer;
public static readonly long WindowsField = Windows;
public static readonly long MixedField = Mixed;

public static long MixedProperty
{
get { return Mixed; }
}
}
public class NonStaticSqlServerAuthenticationModes
{

public long SqlServer
{
get { return 1; }
}
public long Mixed
{
get { return 3; }
}
public long Windows
{
get { return 2; }
}
}
}
 
P

Peter Duniho

Dan said:
[...]
If you want some kind of thing that's like a constant but can be updated
dynamically, use a read-only field or, even better, just make a property
(probably backed by a read-only field, but it could just be hard-coded
into the getter if you like).

The next question might be "What does that do to performance?"
Well...at a million iterations in a for loop.

Practically nothing. But really, that question isn't very relevant. In
general, constants should be just that: constant. On the other hand, if
you have some value that has to be changeable according to version of a
DLL, you have no choice but to make it changeable. The implementation
would be wrong otherwise.

And as always: slow, correct code beats fast, incorrect code any day. :)
All the code is here so if i did something wrong in the timings it can
be pointed out.

Main problem is that benchmarking code needs to have a warm-up section,
so that you can make sure code's been JITted, cache lines have been
warmed up, etc.

Typically, one might put a short loop of about 100-500 iterations before
the loop where the actual measurement is done.
D:\Sandbox\StaticConstants\bin\Debug>TimingConstants.exe
Constant from static class assignment time 7ms 27175ticks
Readonly field from static class assignment time 7ms 27032ticks
Readonly property from static class assignment time 25ms 92980ticks
Constant from propget in non-static class assignment time 23ms 83494ticks

Yeah, there was a 3x slowdown, but saving 18 ms is usually something
none of us are concerned with (unless you are in realtime scenarios - in
which case you likely aren't using c#).

Even in realtime scenarios, the savings expressed here is unlikely to be
important. That's 18ms for 1 million iterations. So for a single
operation, 18 nanoseconds. No matter what, _whatever_ the code does
next is going to completely swamp that cost, making it imperceptible.

I'm also wondering if you tested the performance with a Debug or Release
build. Inlining of the property getter should actually have made the
property basically the same as the constant or field access, I would've
thought. In a Release build, there should not need to be a method call
at all, once the code's been JITted.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top