Mini-git class performance issue I want to build Git class where i can store integers. FilesSet is where I store current state of integers. Commits' key is basically commit number so I can checkout pretty fast. Hashes is used for quick search of hashes that I need in commit part.

Update is for updating current state of integers.

In Commit I want to save a copy of current state and do it fast. First of all I don't want to compare arrays of integers, so I calculate hash of current state. Then if Hashes already has that hash as a key, I add to Commits Pair of commitNumber and  integer array that corresponds to that hash so I won't have to store another copy of array. And in Hashes of this key I add commit Number so I can see which commits have that hash. This one is a bottleneck for my solution.

In Checkout I basically get int that I need by arguments. I think this method is already really fast. 
I need this class to be fast but I can't really provide test cases as they are hidden at my college testing system. My current solution is too slow with int Commit() . 
Here it is :

using System.Collections.Generic;
using System.Linq;
using System;

namespace GitTask
{
    public class Git
    {
        int[] FilesSet { get; set; }
        Dictionary&lt;int, int[]&gt; Commits { get; set; }
        SortedList&lt;int, List&lt;int&gt;&gt; Hashes { get; set; }

        public Git(int filesCount)
        {
            FilesSet = new int[filesCount];
            Commits = new Dictionary&lt;int, int[]&gt;();
            Hashes = new SortedList&lt;int, List&lt;int&gt;&gt;();
        }

        public void Update(int fileNumber, int value)
        {
            FilesSet[fileNumber] = value;
        }

        public int Commit()
        {
            var commitNumber = Commits.Count;
            var tempHash = GetMyHashCode(FilesSet);

            if (Hashes.TryGetValue(tempHash, out List&lt;int&gt; key))
            {
                Commits.Add(commitNumber, Commits[key[0]]);
                Hashes[tempHash].Add(commitNumber);
                return commitNumber;
            }
            Commits.Add(Commits.Count, FilesSet.Clone() as int[]);
            Hashes.Add(tempHash, new List&lt;int&gt;(new int[] { commitNumber }));
            return commitNumber;
        }

        public int Checkout(int commitNumber, int fileNumber)
        {
            if (commitNumber &gt;= Commits.Count)
                throw new ArgumentException();
            else
            {
                var commit = Commits[commitNumber];
                return commit[fileNumber];
            }
        }

        private int GetMyHashCode(int[] array)
        {
            int result = 0;
            for (int i = 0; i &lt; array.Length; ++i)
            {
                result = unchecked(result * 31 + array[i]);
            }
            return result;
        }
    }
}


I expect you to come up with tips on speeding it up or creating new kind of storing files. I also tried to measure performance of methods and it seems like calculating hash of big array is a problem and adding to SortedList&lt;int,List&lt;int&gt;&gt; is a problem as well.
Hash code collisions

There's a problem with Commit: matching hash codes do not mean that two objects are equal ('pigeonhole principle'). For example, both [1, 0] and [0, 31] have hash code 31. You can only be sure that objects are not equal when their hash codes don't match - the inverse is not true.

Also, are you sure that reusing arrays is worth the effort? If filesCount is small, then the savings are small as well, and if filesCount is large, then the likelyhood that a specific state has occurred before is probably very small.

Multiple smaller arrays

One alternative is to divide the state into multiple smaller arrays. This limits the impact of individual changes: you only need to copy a 'sub-array' (and create a new 'array-of-arrays'), instead of copying the full state. This also increases the likelyhood that a particular sub-array has occurred before, although I do not think that is something you need to be concerned about.

In Git terms, this is similar to using multiple files instead of a single large file. Committing changes to a single file only creates a new blob for the modified file and a new tree object that references the new blob (and older blobs for unmodified files).

Change-sets

Another alternative is to store a change-set for each value, using a binary search to find the value for a particular commit number. This can save a lot of space, but it'll make checkouts slower. Whether that trade-off is worth it depends on the intended use of this class.

Other notes


Instead of Clone() as int[], you can use Linq's ToArray() method.
I'd put the last 4 lines of Commit inside an else statement, to make it clearer that these are two mutually exclusive paths.
On the other hand, I would not use an else statement in Checkout - the if check is an 'early-out' guard clause, not a full-blown separate path.
In Checkout, an ArgumentOutOfRangeException might be better suited. You may also want to guard against negative values. And why not check for invalid file numbers as well?