2

I have an array inside a class:

class MatchNode
{
    public short X;
    public short Y;
    public NodeVal[] ControlPoints;

    private MatchNode()
    {
        ControlPoints = new NodeVal[4];
    }
}

The NodeVal is:

struct NodeVal
{
    public readonly short X;
    public readonly short Y;

    public NodeVal(short x, short y)
    {
        X = x;
        Y = y;
    }
}

Now what if we wanted to take performance to next level and avoid having a separate object for the array. Actually it doesn't have to have an array. The only restriction is that the client code should be able to access NodeVal by index like:

matchNode.ControlPoints[i]

OR

matchNode[i]

and of course the solution should be faster or as fast as array access since it's supposed to be an optimization.

EDIT: As Ryan suggested it seems I should explain more about the motivation:

The MatchNode class is used heavily in the project. Millions of them are used in the project and each are accessed hundreds of times so having them as compact and concise as possible can lead to less cache misses and overall performance.

Let's consider a 64bit machine. In the current implementation the class the array takes 8 bytes for the ControlPoints reference and the size of the array object would be at least 16 bytes of object overhead (for method table and sync block) and 16 byte for the actual byte. So we have at least 24 overhead bytes beside 16 bytes of actual data.

These objects are used in bottlenecks of the project so it matters if we could optimize them more.

Of course we could just have a super big array of NodeVal and just save an index in MatchNode that would locate the actual data but again it will change every client codes that uses the MatchNodes, let alone be a dirty non-object oriented solution.

It is okay to have a messy MatchNode that uses every kind of nasty trick like unsafe or static cache code. It is not okay to leak these optimizations out to the client code.

morteza khosravi
  • 1,599
  • 1
  • 20
  • 36
  • 2
    Can you explain what is wrong with what you have already? What *specifically* is your concern? Granted an array is not ideal when there are much better collections available, like `List`, but what makes you think your code is suffering from some performance problem? – rory.ap May 27 '16 at 11:42
  • 2
    Do you really care for such micro-optimization (I even doubt it *will* have an effect on performance)? You should definitly take care for actual performance-problems identified by a profiling tool (DotTrace e.g.). – MakePeaceGreatAgain May 27 '16 at 11:42
  • 3
    Use an [indexer](https://msdn.microsoft.com/en-GB/library/6x16t2tx.aspx)? That would probably be slower than just exposing an array though! – Matthew Watson May 27 '16 at 11:42
  • 1
    You can expose the values from your class with an indexed property, but you still need an underlying data structure, whether it's an array, list, or linked list would be based on your exact requirements. – juharr May 27 '16 at 11:42
  • Just another example of over-optimization: you ain´t gonna need it (YAGNI). There seem to be more other problems than this one in your code if you encounter that your software runs slowly. – MakePeaceGreatAgain May 27 '16 at 11:44
  • @roryap It's an optimization. The code runs fine but having a an array means 8 bytes for reference + 16 byes of array object overhead =24 bytes of overhead when the actual memory requirement is 4*4=16 – morteza khosravi May 27 '16 at 11:47
  • 1
    But *why* are you electing to do this? Have you identified a problem the answer to which is to attempt to optimize? Why are you undertaking this optimization? Everything can be optimized, theoretically, so why do this instead of something else or instead of moving forward with new code/features? – rory.ap May 27 '16 at 11:49
  • @HimBromBeere It's a part of a million member array that is traversed hundreds of times so it matters. Yes I use dottrace. – morteza khosravi May 27 '16 at 11:49
  • @MatthewWatson that's the exact issue. Having If in indexer is much worse than having array in the first place:D – morteza khosravi May 27 '16 at 11:50
  • @roryap It's kind of a bottleneck in my project so either I have to solve it in some way or pull the array out and have some super big array that has the information for all MatchNodes. – morteza khosravi May 27 '16 at 11:54
  • 2
    Your comment answers make this post much more clear ... and intriguing. You should consider editing the post and adding this information. – Ryan Peters May 27 '16 at 12:00
  • @RyanPeters Thanks for pointing that out. I updated the OP. – morteza khosravi May 27 '16 at 12:26
  • 1
    Have you tested/profiled your "super big array" option to check if it gives you a significant performance improvement? If it doesn't help, then there's no need to worry about coming up with a clean way to do the same thing – Ben Aaronson May 27 '16 at 12:40
  • There was a recent post about Singleton implementation and laziness. Jon Skeet provides some insights. May or may not help: http://stackoverflow.com/a/37416250/4846465 and http://stackoverflow.com/a/610837/4846465 – Ryan Peters May 27 '16 at 12:48
  • 1
    Also is the number of elements in the array always 4? – Ben Aaronson May 27 '16 at 12:51
  • @BenAaronson No I didn't test that. I'm thinking of it as a last resort since it needs me to change various portions of my code and have some pooling solution for it. I'll test that if everything else fails. – morteza khosravi May 27 '16 at 12:52
  • @BenAaronson Yes it is – morteza khosravi May 27 '16 at 12:52

1 Answers1

0

You´re looking for indexers:

class MatchNode
{
    public short X;
    public short Y;
    private NodeVal[] myField;
    public NodeVal this[int i] { get { return myField[i]; } set { myField[i] = value; } }
    public MatchNode(int size) { this.myField = new NodeVal[size]; }
}

Now you can simply use this:

var m = new MatchNode(10);
m[0] = new NodeVal();

However I doubt this will affect performance (at least in means of speed) in any way and you should consider the actual problems using a profiling tool (dotTrace for instance). Furthermore this approach will also create a private backing-field which will produce the same memory-footprint.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • This version also has an array in the class. The whole point is to remove the array and the memory and dereference overhead associated with it – morteza khosravi May 27 '16 at 12:02