2012-05-07 11 views
19

Estoy tratando de utilizar el enfoque del "evaluador de la mano RayW" para obtener una puntuación de combinación de tarjetas (5 mejores tarjetas de un total de 7). Sin embargo estoy teniendo algunos problemas de rendimiento con este método. Según las fuentes: ¡con este enfoque, debe ser posible evaluar más de 300 mil manos por segundo! Mi resultado es 10 molinos en 1.5 segundos, que es mucho más lento.Evaluación más rápida de la mano de póquer

La idea detrás de "mano evaluador RayW" es el siguiente:

El evaluador dos más dos consta de una tabla de consulta que contiene gran unos treinta y dos millones de entradas (32.487.834 para ser exactos). Para obtener y buscar una mano de poker de 7 cartas determinada, trace una ruta a través de esta tabla , realizando una búsqueda por tarjeta. Cuando se llega a la última carta, el valor así obtenido es el valor oficial de la equivalencia de la mano

aquí es cómo se ve un código como:

namespace eval 
{ 
public struct TPTEvaluator 
{ 
    public static int[] _lut; 

    public static unsafe void Init() // to load a table 
    { 
     _lut = new int[32487834]; 
     FileInfo lutFileInfo = new FileInfo("HandRanks.dat"); 
     if (!lutFileInfo.Exists) 
     {throw new Exception("Handranks.dat not found");} 

     FileStream lutFile = new FileStream("HandRanks.dat", FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096); 

     byte[] tempBuffer = new byte[32487834 * 4]; 
     lutFile.Read(tempBuffer, 0, 32487834 * 4); 

     fixed (int* pLut = _lut) 
     { Marshal.Copy(tempBuffer, 0, (IntPtr)pLut, 32487834 * 4);} 
     tempBuffer = null; 
    } 

    public unsafe static int LookupHand(int[] cards) // to get a hand strength 
    { 
     fixed (int* pLut = _lut) 
     { 
      int p = pLut[53 + cards[0]]; 
      p = pLut[p + cards[1]]; 
      p = pLut[p + cards[2]]; 
      p = pLut[p + cards[3]]; 
      p = pLut[p + cards[4]]; 
      p = pLut[p + cards[5]]; 
      return pLut[p + cards[6]]; 
     } 
    } 
} 

}

y así es como me probar este enfoque:

private void button4_Click(object sender, EventArgs e) 
    { 
     int[] str = new int[] { 52, 34, 25, 18, 1, 37, 22 }; 

     int r1 = 0; 

     DateTime now = DateTime.Now; 
     for (int i = 0; i < 10000000; i++) // 10 mil iterations 1.5 - 2 sec 
     { r1 = TPTEvaluator.LookupHand(str);} // here 
     TimeSpan s1 = DateTime.Now - now; 
     textBox14.Text = "" + s1.TotalMilliseconds; 
    } 

Creo que este método se implementó originalmente en C++, pero n sin embargo, el puerto C# debería funcionar más rápido. ¿Hay alguna forma de cómo puedo acercarme a al menos 100 millones de manos en un segundo?

lo que he intentado hasta ahora:

  • intentado usar estático, y los métodos no estáticos - no hay diferencia.
  • intentado usar búsqueda de diccionario en lugar de gama

    public void ArrToDict(int[] arr, Dictionary<int, int> dic) 
    { 
        for (int i = 0; i < arr.Length; i++) 
        { 
         dic.Add(i, arr[i]); 
        } 
    } 
    
    public unsafe static int LookupHandDict(int[] cards) 
    { 
        int p = dict[53 + cards[0]]; 
        p = dict[p + cards[1]]; 
        p = dict[p + cards[2]]; 
        p = dict[p + cards[3]]; 
        p = dict[p + cards[4]]; 
        p = dict[p + cards[5]]; 
        return dict[p + cards[6]]; 
    } 
    

tiempo transcurrido durante 10 molinos de manos es casi 6 veces más lento ..

  • De acuerdo con una persona - puso en el rendimiento en 200 fábricas eliminando el código "inseguro". Traté de hacer lo mismo, pero los resultados son casi los mismos.

    public static int LookupHand(int[] cards) 
    { 
         int p = _lut[53 + cards[0]]; 
         p = _lut[p + cards[1]]; 
         p = _lut[p + cards[2]]; 
         p = _lut[p + cards[3]]; 
         p = _lut[p + cards[4]]; 
         p = _lut[p + cards[5]]; 
         return _lut[p + cards[6]]; 
    } 
    

Esta es la cita:

Después de quitar las partes de código "inseguros" y algunos pequeños ajustes en la versión # c es ahora también alrededor de 310 mio.

¿Hay alguna otra forma de aumentar el rendimiento de este sistema de clasificación manual?

+10

¿Ha intentado ejecutar en modo de lanzamiento? Podría funcionar más rápido, porque el código está optimizado. –

+0

el archivo .dat que contiene todas estas entradas es de 130 Mb de tamaño. Sin embargo, las PC P4 con 1 o 2 gigas de ram no tuvieron ningún problema. Tengo 4 conciertos, por lo que la RAM no debería ser un gran problema. – Alex

+0

@MichalB. ¡Son 200 milisegundos ahora! gracias un montón. No eliminaré este problema ... tal vez haya más mejoras que puedan hacerse ... ¡¡Gracias de nuevo !! – Alex

Respuesta

4

Primero: la evaluación comparativa siempre es complicada. Las cosas que funcionan de una manera en su máquina no siempre funcionan de la misma manera en otras máquinas y hay muchas cosas que están sucediendo 'ocultas' que pueden invalidar los datos (como el almacenamiento en caché realizado por el SO o incluso el hardware).

Habiendo dicho eso, eché un vistazo a su método Init() y me dejó rascándome la cabeza. Me resultó difícil seguirlo. Mi regla de oro para usar 'inseguro' es no usarlo, a menos que sea absolutamente necesario. Este método Init(), supongo, se llama una vez, ¿verdad? Decidí establecer criterios de referencia:

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); 

Dónde Init1() es el código original y init2() es mi código reescrito (También he volteado la orden varias veces en aras de la equidad). Aquí es lo que consigo (en mi máquina) ...

Actualizado método init(): 110

original método init(): 159

Aquí está el código que he usado. No se requiere una palabra clave insegura.

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(); 
    } 
} 

En mi opinión, este código es más fácil de leer y parece funcionar más rápido.

Sé que probablemente esté más preocupado por el rendimiento de LookupHand(), pero no pude hacer ninguna mejora significativa. Intenté algunos enfoques diferentes pero nada que me ayudara.

Pude ejecutar su código 100.000,000 veces en 500 milisegundos. Me estoy ejecutando en una computadora portátil de 64 bits bastante carnosa, que parece ser la velocidad que esperabas. Como han dicho otros, ejecutar en modo liberación (habilitando la optimización) puede tener un gran impacto en el rendimiento.

+0

Init2() carga ceros para mí .. – Alex

+0

@Alex - Estoy realmente avergonzado ... No publiqué el código que tenía previsto. Por favor, intente el código actualizado ahora! Los valores de referencia siguen siendo lo que veo en mi máquina. –

4

Si desea velocidad genérica, le sugiero usar el evaluador en Brecware: http://www.brecware.com/Software/software.html. El evaluador de Steve Brecher es más rápido que el evaluador RayW para las evaluaciones que ocurren en orden aleatorio, y es mucho más compacto.

Como se señala en los comentarios, el evaluador RayW depende de la localidad de referencia para su velocidad. Si no está recorriendo las evaluaciones en el mismo orden que las tablas de búsqueda, va a ser lento. Si ese es su problema, existen tres enfoques:

  1. Haga que su orden de evaluación coincida más estrechamente con las tablas.
  2. Haga tablas que coincidan con su orden de evaluación
  3. Haga un evaluador optimizado para su caso de uso.
+0

desafortunadamente no hay un puerto C# – Alex

Cuestiones relacionadas