Faster poker hand evaluation

If you want generic speed, I would suggest using the evaluator at Brecware: https://web.archive.org/web/20160502170946/http://brecware.com/Software/software.html. Steve Brecher's evaluator is faster than the RayW evaluator for evaluations which occur in random order, and is much more compact.

As noted in the comments, the RayW evaluator depends on locality of reference for it's speed. If you're not traversing the evaluations in the exact same order as the lookup tables, it's going to be slow. If that is your problem there are three approaches:

  1. Make your evaluation order more closely match the tables.
  2. Make tables that match your evaluation order
  3. Make an evaluator optimized to your use case.

First - benchmarking is always tricky. Things that perform one way on your machine don't always perform the same way on other machines and there is a lot going on 'under-the-covers' that can invalidate data (like caching done by the OS or even hardware).

Having said that - I took a look at just your Init() method and it left me scratching my head. I found it difficult to follow. My rule of thumb for using 'unsafe' is to not use it, unless I absolutely have to. This Init() method, I'm assuming, gets called once, right? I decided to benchmark it:

static void BenchmarkIt(string input, Action myFunc)
{
    myWatch.Restart();
    myFunc();
    myWatch.Stop();

    Console.WriteLine(input, myWatch.ElapsedMilliseconds);
}

BenchmarkIt("Updated Init() Method:  {0}", Init2);
BenchmarkIt("Original Init() Method: {0}", Init1);  

Where Init1() is your original code and Init2() is my rewritten code (I've also flipped the order several times in the sake of fairness). Here's what I get (on my machine)...

Updated Init() Method: 110

Original Init() Method: 159

Here's the code I used. No unsafe keyword required.

public static void Init2()
{
    if (!File.Exists(fileName)) { throw new Exception("Handranks.dat not found"); }            

    BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open));            

    try
    {
        _lut = new int[maxSize];
        var tempBuffer = reader.ReadBytes(maxSize * 4); 
        Buffer.BlockCopy(tempBuffer, 0, _lut, 0, maxSize * 4);
    }
    finally
    {
        reader.Close();
    }
}

In my opinion, this code is easier to read and it seems to run faster.

I know you are probably more concerned about LookupHand()'s performance, but I wasn't able to make any significant improvements. I tried a few different approaches but nothing that helped.

I was able to run your code 100,000,000 times in 500 milliseconds. I'm running on a fairly beefy 64-bit laptop - which seems to be the speed you were expecting. Like others have said - running in release mode (enabling optimization) can have a big impact on performance.