-1

I've created a library for fun called GamblersDice. I'm trying to micro-optimize it but unsure if I'm doing it right. What I want is to use a reference to a global Random object when creating the die. The reason I believe it's not working is that testing GamblersDie(random, int) and GamblersDie(ref random, int) take nearly the same time over 10,000,000 iterations (test project is in the repo). If you don't want to scan through the repo, here are my constructors:

public class GamblersDie : Die
{
    private Random _rnd;

    public int[] Weight { get; private set; }

    public GamblersDie() : this(new Random()) { }

    public GamblersDie(int size) : this(new Random(), size) { }

    public GamblersDie(params int[] weights) : this(new Random(), weights) { }

    public GamblersDie(Random rnd) : this(ref rnd) { }

    public GamblersDie(Random rnd, int size) : this(ref rnd, size) { }

    public GamblersDie(Random rnd, params int[] weights) : this(ref rnd, weights) { }

    public GamblersDie(ref Random rnd) : this(ref rnd, 6) { }

    public GamblersDie(ref Random rnd, int size) {
        _rnd = rnd;
        Weight = new int[size];

        for (int i = 0; i < Weight.Length; i++)
        {
            Weight[i] = 1;
        }
    }

    public GamblersDie(ref Random rnd, params int[] weights) : this(ref rnd, weights.Length)
    {
        for (int i = 0; i < Weight.Length; i++)
        {
            Weight[i] = weights[i];
        }
    }
}

Like I said, this is just for fun. I want to micro-optimize it, just because it may be possible. Another question I have is about the constructor chaining. At first glance it may be confusing and I'm wonder if it's some kind of anti-pattern.

  • The resulting instructions are nearly the same, what do you expect? https://sharplab.io/#v2:C4LghgzgtgPgAgBgARwIwG4CwAoOBmFAJiQGEkBvHJalAuAFiQFkAKAJwFMAzJAJTAB2AEwD2UJGwCUVGpWw0FKVAE52AOgByHAB7AWkyVnk0AvjOrnF+FI1b9hYidOPU5immlVtNOvQaMKZtgmQA=== – thehennyy Jul 24 '18 at 14:16
  • 3
    Why do you want to pass it by ref? You're not assigning a new reference to the variable. – stuartd Jul 24 '18 at 14:18
  • Why do you think you would get a difference in timing? – PaulF Jul 24 '18 at 14:19
  • 2
    The only point of passing a reference to a class using `ref` is if you're going to *reassign* the parameter to a different reference and that should have an effect in the calling code. Since you never do `rnd = `, there's no point. – Damien_The_Unbeliever Jul 24 '18 at 14:19
  • @stuartd my reasoning is that using a reference to the Random would decrease CPU time by reducing the number of copies of the Random object used when creating large numbers of dice. – MrTarantula Jul 24 '18 at 14:28
  • The only confusing thing about your _"constructor chaining"_ is the fact you have _"GamblersDie(Random rnd, int size)"_ and _"GamblersDie(ref Random rnd, int size)"_ - looking at your coding they have the same effect as the _"rnd"_ parameter is not modified. – PaulF Jul 24 '18 at 14:28
  • @Damien_The_Unbeliever thanks for the info. I'm just trying to reduce the number of times the Random is copied when creating multiple dice. I see that ref is not the way to do so. – MrTarantula Jul 24 '18 at 14:30
  • @MrTarantula: You could also remove the need to define _"GamblersDie(Random rnd)"_ & _"GamblersDie(Random rnd, int size)"_ separately by using a default parameter : _"GamblersDie(Random rnd, int size=6)"_. Similarly _"GamblersDie(int size=6)"_ removes the need for _"GamblersDie()"_ – PaulF Jul 24 '18 at 14:49
  • Passing references by ref unnecessarily should be a few nanoseconds *slower*, not *faster*. The object is never copied; only a reference to the object is copied! Are you reasoning about C# as though it were C++? Imagine that every variable of reference type has an invisible `&` in front of it; does that help? What you are doing is adding *more* indirection, which is *not faster*. – Eric Lippert Jul 24 '18 at 23:19
  • @EricLippert Yes, that's exactly what I was doing, thinking about C# as if it were C++. I actually did notice a slight slowdown over several million iterations when using ref, which is what led me to ask the question. – MrTarantula Jul 26 '18 at 19:01
  • Quite fine. You might be interested in a new C# feature which allows you to pass large structs "by reference" but without making the alias a writable variable, unlike `ref` and `out`, which can pass structures by reference already. But you should never need to pass a *reference* by reference for performance reasons! It's already a reference-sized value. – Eric Lippert Jul 26 '18 at 19:07

1 Answers1

2

What do variables/parameters hold? They hold values. What is the nature of the values they store? For Value types (struct) they store the value itself.

But for Reference types (class), they store a reference; they don't store the object itself, that's located elsewhere.

It is these that are copied-in in the default pass-by-value parameter passing in C#. And copying a reference is not the same as creating a copy of the object to which it refers. So, even without ref, you're dealing with a single object both inside and outside the method. Any changes to the object will, obviously, be visible externally.

You only need ref if a) you're going to reassign the parameter and that reassignment should be externally visible or b) you're mutating a Value type and that mutation should be externally visible.


And, in case the above isn't clear enough, I will explicitly state it here - Random is a Reference type.


Bonus reading: The Truth About Value Types by Eric Lippert, which you may not be ready for but does help dispel some common myths that you may pick up along the way.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
  • In addition to the above answer - it should be noted that if you do not create a new instance of Random when creating each new GamblersDice object with _"GamblersDie(random, int)"_ or _"GamblersDie(ref random, int)"_ - then all of them will be reference the same instance (this may or may not be an issue for your application). – PaulF Jul 24 '18 at 14:37
  • @Damien_The_Unbeliever thanks for the concise explanation. – MrTarantula Jul 24 '18 at 14:41
  • @PaulF I want to give users (however few they may be) the option to use a global instance or have the constructor create a new instance, which the constructors without Random parameters will do. – MrTarantula Jul 24 '18 at 14:43