2

let's say i have a class with: a const

private const decimal x = 2.0m;

a readonly field

private readonly decimal y = 2.0m;

a method with this signature

void Method1(in decimal x)

if i invoke the Method1 with the const x Method1(x) i assume the value of x will passed by value, instead when i use the Method1 with the readonly y Method1(in y) the value will be passed by readonly reference. So my expectations are that the more performant is the readonly field used as a "fake" const. I'm wrong? i have some dubt that internally the const will be used, by the compilator or optimized by the clr, to be performant as the readonly field passed by reference.

Marco Levarato
  • 203
  • 4
  • 16
  • 1
    "the more performant" Why not simply measure it? But do a benchmark-test, this is execute it in a loop some millions of times. Doing it only once won´t show you realistic results. Anyway I think the difference - if any at all - is neglectable. – MakePeaceGreatAgain Aug 01 '18 at 12:19
  • 1
    The statements are *not* equivalent. A const will disappear during compilation. The compiler can and *will* replace any reference to it with its actual value. In fact that's one of the gotchas - if *any* assembly depends on that constant, *all* of them will have to be recompiled or end up using different values. `Constant` is supposed to be constant for good – Panagiotis Kanavos Aug 01 '18 at 12:22
  • 1
    @PanagiotisKanavos in fact if you actually decompile the above situation you will see that using the in keyword will make a difference. So it only kind of disappears but have a look: [sharplab](https://sharplab.io/#v2:EYLgHgbALANAJiA1AHwK4GcCWA7A5gAgGUBPdAFwFMBbAbgFgAoDHAk86gOgBUALAJwoBDOC26D0Aa3T0GjbIKoV0AB0EBjCkTJ9UasoUwAvClyVl8jAN6N8t/GoA249PgAKfAPa4+Ciwzv41v4BdsqowA6YavgAbh6YcPgAsoI4ABQAlDYhtkE5OQBmaWAZMvkhRcSl2TkAvjUhDQHKfJgxgpT45B1RsfGJRTj4cBRqmFSCDrGTWcE5eeV2TXb1c41rza3tnQLCHtgOxMOj45P4YPgAvPgATBwADLTLti1tHZpq++THYxNTR9c7o8yitGLUgA==) – Robin B Aug 01 '18 at 12:24
  • Have a look at this answer: https://stackoverflow.com/questions/410723/is-there-a-difference-between-private-const-and-private-readonly-variables-in-c – Ruud Kobes Aug 01 '18 at 12:24
  • Check the related [What is the difference between const and readonly?](https://stackoverflow.com/questions/55984/what-is-the-difference-between-const-and-readonly). – Panagiotis Kanavos Aug 01 '18 at 12:24
  • 1
    Bear in mind [`const decimal` is a lie anyway](https://stackoverflow.com/q/51607247/15498) – Damien_The_Unbeliever Aug 01 '18 at 12:28
  • 2
    @RobinB That copy is one of the well-known gotchas of using `in` and `ref`. It's not `in` that creates the copy here. The *compiler* isn't sure that the value won't change so it *copies* it to ensure it won't. There's at least one Roslyn analyzer that looks for such problems – Panagiotis Kanavos Aug 01 '18 at 12:29
  • 2
    @RobinB found it : [ErrorProne.NET](https://blogs.msdn.microsoft.com/seteplia/2018/05/03/avoiding-struct-and-readonly-reference-performance-pitfalls-with-errorprone-net/) – Panagiotis Kanavos Aug 01 '18 at 12:30
  • i know the difference between const and readonly field, my question was wich is the more performant to use. BTW i have do a test now, i will send a response asap – Marco Levarato Aug 01 '18 at 12:30
  • @PanagiotisKanavos well thats interesting thanks – Robin B Aug 01 '18 at 12:32
  • @MarcoLevarato instead of testing check Robin's link to Sharplab. The code has a problem unrelated to performance - it creates a *local copy* of the value, even with `const`, to ensure it won't change – Panagiotis Kanavos Aug 01 '18 at 12:32
  • @MarcoLevarato Sergey Teplyakov wrote several articles that explain the perf pitfals, and even wrote a [Roslyn Analyzer](https://blogs.msdn.microsoft.com/seteplia/2018/05/03/avoiding-struct-and-readonly-reference-performance-pitfalls-with-errorprone-net/) for this – Panagiotis Kanavos Aug 01 '18 at 12:33

2 Answers2

3

Yes, probably the readonly option is more performant, but its debatable as to wether this performance gain is relevant or not.

Why?

The const version will actually run the decimal constructor every time the method is invoked, while the readonly / in version will simply copy a reference to a previously created decimal instance. The latter is obviously faster.

You can verify this by simply inspecting the IL:

  • Const version:

    IL_0001: ldc.i4.s 20
    IL_0003: ldc.i4.0
    IL_0004: ldc.i4.0
    IL_0005: ldc.i4.0
    IL_0006: ldc.i4.1
    IL_0007: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32, int32, bool, uint8)
    IL_000c: call instance bool C::Method2(valuetype [mscorlib]System.Decimal)
    
  • The readonly/in version:

    IL_0002: ldflda valuetype [mscorlib]System.Decimal C::dd
    IL_0007: call instance bool C::Method1(valuetype [mscorlib]System.Decimal&)
    IL_000c: pop
    

That said, I'm not altogether sure why you tie const with performance. A const is a way to convey in your code that a given value will not change ever. const does not mean, this value stored as a constant will be more performant when used.

Unless you really have an emprically demonstrated performance issue that is solved by this marginal (at best) optimization, a value that is logically a constant, should be a const, and a variable that is logically a read only value should be readonly and all other considerations should be ignored. If your case is the former then document it clearly in your code why a logically constant value is implemented as a read only field.

InBetween
  • 32,319
  • 3
  • 50
  • 90
  • all is relevant in some conext as the my. I'm writing a HFT bot – Marco Levarato Aug 01 '18 at 12:38
  • @MarcoLevarato then you are looking for the wrong thing. A `decimal` is already read-only. And ultrapremature optimizations like this can lead to perf degradation, as the answers here show. Better sorting, grouping algorithms will lead to *far* better perf for HFT. Better containers, avoiding wasted operations, better algorithms etc – Panagiotis Kanavos Aug 01 '18 at 12:40
  • @MarcoLevarato Of course all is relevant to some extent. But if this issue is a real and measured performance concern then maybe the issue here is that you are not using the most adequate framework... maybe a non managed environment is the better choice? – InBetween Aug 01 '18 at 12:41
  • @MarcoLevarato remember someone trying to find the last value above a threashold in an HFT application. Wrote it in C++ for perf. Then started iterating from the *start* instead of the *end* to find the *last* value. Replacing these with reverse iterators, adding maps (dictionaries), using the proper containers and algorithms resulted in 10x improvement at least. No pointer arithmetic could do that – Panagiotis Kanavos Aug 01 '18 at 12:43
2

I have do a test and the winner is the readonly field passed with in, this is the code executed with a i7 8700k processor

class Test
{
    public const decimal x = 0.2m;
    public readonly decimal y = 0.2m;

    public void Method1(in decimal x)
    {
        Thread.MemoryBarrier();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var t = new Test();

        var sw = new Stopwatch();
        sw.Start();
        for (var i = 0; i < 100000000; i++)
        {
            t.Method1(in t.y);
        }
        sw.Stop();
        Console.WriteLine(sw.ElapsedTicks);
        sw.Restart();
        for (var i = 0; i < 100000000; i++)
        {
            t.Method1(Test.x);
        }
        sw.Stop();
        Console.WriteLine(sw.ElapsedTicks);
        Console.ReadLine();
     } 
}

The method invoked as t.Method1(in t.y); have consumed 11606428 ticks, The method invoked as t.Method1(Test.x); have consumed 16963941 ticks

So there are not optimizations under the hood for the const in this case

Marco Levarato
  • 203
  • 4
  • 16
  • There's nothing to optimize - in both cases you asked for a ref to something. Something has to exist then, whether as a field or local copy. If the parameterd wasn't a `ref`, you'd see that the constant value was inlined to ` f(2m);` – Panagiotis Kanavos Aug 01 '18 at 12:38