0

A couple of readonly variables will be used. Almost all of the classes will be using those variables. Either I place all of the variables in a separate internal static class or I pass them on repeatedly in all the classes' constructor parameters. What would be the better of the 2? Performance-wise only please.

Gwapo
  • 29
  • 3
  • 5
    Performance-wise, it most likely won't make any difference. – Robert Harvey May 22 '18 at 17:42
  • @RobertHarvey Really? Even just the tiniest bit of difference? – Gwapo May 22 '18 at 18:09
  • 1
    If there is a difference, it's almost certainly not a large enough difference to be worth your time considering. – Robert Harvey May 22 '18 at 18:16
  • 3
    Profile it and find out for yourself. Don't forget to run the test repeatedly, calculating the standard deviation for both sets to help give you a "rule of thumb" measure of whether the results are significantly different (ie are different due to performance; not chance). Unless you are good at statistics of course, then you'll know how to properly compare the results to give a more precise measure of any differences being due to chance alone. – David Arno May 22 '18 at 18:21
  • 1
    Possible duplicate of [Is micro-optimisation important when coding?](https://softwareengineering.stackexchange.com/questions/99445/is-micro-optimisation-important-when-coding) – gnat May 22 '18 at 20:09
  • Are you including developer time in the performance metric? – Caleth May 23 '18 at 09:47
  • @Gwapo you've already wasted way more time posting this question, and having us read it, than the performance difference over the next 20 years. My only regret is that I have only one down vote to give to this question. – user949300 May 24 '18 at 01:01
  • No developer time – Gwapo May 24 '18 at 15:21

3 Answers3

12

Performance-wise only please.

You have already made the wrong decision, right there. This is called "premature optimisation" and it's frowned upon, for good reasons.

The correct question to be asking is what effect will the two approaches have on the ease of reading, testing, reasoning and maintaining the code. And the answer to that comes down to just how immutable are these "variables". How are they set? What do you mean by they are read-only? Are they completely immutable, or simply read-only from an external perspective, but can change internally?

It's all about trade-offs. Having global immutable values can improve the readability of your code and reduce the amount of parameter passing you have to do. But it increases coupling and can make testing more difficult. And if they can change in any way, then they are a huge evil global variable anti-pattern and you should run screaming from even considering them.

David Arno
  • 38,972
  • 9
  • 88
  • 121
  • The variables are completely unchangeable. I know it's wrong but I have bad OCD. I need to micro-optimize everything. – Gwapo May 22 '18 at 18:10
  • 7
    @Gwapo, the problem with micro-optimising is that all too often, you'll get it wrong. At best, you'll just make the code slower. At worst, you'll make the code slower and will introduce a bug. Very occasionally you'll get lucky and will make the code noticeably faster. But that won't happen often. The correct way to optimise is to profile the application and to improve the performance bottlenecks. Whether you have OCD or not is irrelevant. Do you want to do things properly? you'll do want to do things properly, so you'll stop micro-optimising... – David Arno May 22 '18 at 18:17
3

Do both, using IoC

You should do both, using an Inversion of Control container, which along with the LSP will provide clean separation of concerns and greatly ease automated unit testing.

The idea is that you define a "static" (not really) class and inject it into the constructor everywhere it is needed. The container deals with the instancing for you, and you can set your settings to be "singleton" or "single instance," which essentially makes it like a static class, only you can stub and mock its interface. You wouldn't be able to do that with a normal static class.

In this example I'll use a common IoC container known as Autofac (which is free, and available on NuGet. There are many others too.) First we define an interface that exposes the settings we need:

public interface ISettings
{
    string Setting1 { get; }
    string Setting2 { get; }
}

Then we define a concrete implementation.

public class GlobalSettings : ISettings
{
    //These are just examples. In production code, perhaps they are database calls.
    public string Setting1 { get { return "Foo"; } }
    public string Setting2 { get { return "Bar"; } }
}

We then add our settings to the composition root, along with our application:

public static IContainer CompositionRoot()
{
    var builder = new ContainerBuilder();
    builder.RegisterType<GlobalSettings>().As<ISettings>().SingleInstance();
    builder.RegisterType<Application>();
    return builder.Build();
}

Oh by the way here's the application. This one is just a dummy example:

public class Application
{
    private readonly ISettings _settings;

    public Application(ISettings settings)  //Injected automatically
    {
        _settings = settings;
    }

    public void Run()
    {
        Console.WriteLine("Setting1 = '{0}'", _settings.Setting1);
        Console.WriteLine("Setting2 = '{0}'", _settings.Setting2);
    }
}

Now to execute it, we call the composition root to compose the object graph, ask Autofac to give us an Application, and call Run(). The container will inject the settings automatically, and it will always inject the same instance. It's very easy. Ends up being one line of code:

public static void Main()
{
    CompositionRoot().Resolve<Application>().Run();
}

And here's the output from Application:

Setting1 = 'Foo'
Setting2 = 'Bar'

Here is a full working example on DotNetFiddle.

The above is a very common pattern and I highly recommend you learn it. Also, it will definitely satisfy your OCD tendencies, as it keeps everything very organized.

Performance

The means by which you supply variables where they are needed are not likely to move the needle with regards to performance. However, structuring the code in this manner will do two things to help you with performance:

  1. By having well structured code that is simpler to maintain, your development team will have more time to focus on performance-related matters.

  2. The ability to substitute mocks and stubs means you can isolate pieces of code and test them independently, which may help you determine where performance problems come from. For example, if you isolate away the database client and things speed up, it's likely that data access is the bottleneck.

For example, let's say we discover that our application is running terribly slow and we suspect that the data retrieval logic in our GlobalSettings class is causing the problem. We can easily create a mockup:

public class MockSettings : ISettings
{
    //Dummy implementation without database calls
}

and register it instead of the GlobalSettings:

builder.RegisterType<MockSettings>().As<ISettings>().SingleInstance();

And now when we run the application, the database calls don't happen, and we can tell what portion of the performance problem is isolated away, and what remains.

John Wu
  • 26,032
  • 10
  • 63
  • 84
  • Added a section on performance to address the OP's concerns. Would appreciate a comment indicating the reason for the downvote so I can further improve my answer. – John Wu May 22 '18 at 19:22
  • 1
    Apologies for the downvote. I skimmed your answer and read it as advising that the composition root/container could be injected everywhere. But I then read it properly and realised my mistake, so changed it to an upvote as it's a good answer. – David Arno May 22 '18 at 19:26
  • This is nice but I kinda already use IoC. – Gwapo May 24 '18 at 15:27
  • In that case I am confused as to why that wasn't one of the options in your original post. – John Wu May 24 '18 at 15:59
0

Either I place all of the variables in a separate internal static class or I pass them on repeatedly in all the classes' constructor parameters.  What would be the better of the 2? Performance-wise only please.

As the commenters have noted there is going to be mostly negligible difference from a speed performance perspective.

The only way you're going to find a meaningful difference is if you have data sets (instance object counts) that are extremely large — as there you may notice a space usage difference, which at extreme scale will result in a speed difference as well.

However, this assumes some details that I'm inferring from your question, and these assumptions may not hold..

What I'm getting at is that the "static class with static members" approach uses tight coupling of the code in some classes to the static data in the other class.  Of course, this approach will not allow different instances to have different contexts, but then we're not taking maintenance and/or usability merits, just performance.  As a result this will save on some instance data over some of other approaches.

In the "pass them on repeatedly ... classes' constructor parameters" approach, I'm now assuming that the constructors store/forward these formal parameters into instance variable in their respective classes — and these instances then represent larger instances than we would have with the more tightly coupled approach.

Yet still, instead of providing multiple parameters over and over, we could provide a single (shareable) context object holding multiple values as constructor parameter to be stored in the instances.  While this represents fewer parameters to pass and then store, it is now a reference, which is 64-bits on a 64-bit architecture (where as two 32-bit ints or several booleans would have been no larger).  So, this is a space win only if the number and size of the parameters is larger than pointer size.

If on the other hand, your constructors are computing with these parameters and are not storing them in the instance for the instance object's later (methods to) use, then there is no space penalty, and thus we're back to negligible performance differences.

In short, if there is a space cost to one approach over another, it can show up when there are very high volumes of instances, and usually the more space used, then, the slower the code due to cache efficiency issues (with other things being equal, like the overall algorithm).

Erik Eidt
  • 33,282
  • 5
  • 57
  • 91