2012-03-27 9 views
8

He compilado parte de boost - la función ibeta_inv - en un ensamblado .Net de 64 bits y funcionó muy bien hasta que comencé a llamarlo desde múltiples hilos. Luego, ocasionalmente devuelve resultados incorrectos.Boost math (función ibeta_inv) ¿no es seguro para subprocesos?

Accedí que el uso de este código (C++/CLI):

// Boost.h 

#pragma once 

#include <boost/math/special_functions/beta.hpp> 

using namespace boost::math; 

namespace Boost { 

    public ref class BoostMath 
    { 
    public: 
     double static InverseIncompleteBeta(double a, double b, double x) 
     { 
      return ibeta_inv(a,b,x); 
     } 
    }; 
} 

Alguien ha probado esto antes?

No he probado esto fuera de .Net, así que no sé si esta es la causa, pero realmente no veo por qué, ya que funciona muy bien.

Uso (C#):

private void calcBoost(List<Val> vals) 
{ 
    //gives WRONG results (sometimes): 
    vals.AsParallel().ForAll(v => v.BoostResult = BoostMath.InverseIncompleteBeta(v.A, v.B, v.X)); 
    //gives CORRECT results: 
    vals.ForEach(v => v.BoostResult = BoostMath.InverseIncompleteBeta(v.A, v.B, v.X)); 
} 

ACTUALIZACIÓN: Como se puede ver en mis comentarios a continuación - no estoy seguro en todos los más que se trata de un problema Boost. Tal vez es un error raro de PLinq a C++/CLI ??? Estoy descartado y volveré con más hechos más tarde.

+2

La documentación dice que todo boost.math debe ser seguro para subprocesos siempre que uses tipos de punto flotante integrados (que, como veo, lo haces). Tal vez deberías presentar un error? http://www.boost.org/doc/libs/release/libs/math/doc/sf_and_dist/html/math_toolkit/main_overview/threads.html – stanwise

+0

Si no aparece nada más, puedo probarlo en un C++ nativo aplicación y ver si el problema persiste. Si es así, un informe de error puede ser lo único que se debe hacer, ya que no puedo encontrar nada en el código fuente. Aunque es una pena ... funciona dos veces más rápido que nuestra implementación actual de la función beta incompleta inversa. –

+0

¡Interesante!Me vienen a la mente dos pensamientos: (1) la triplicación ha creado impulso en modo multiproceso (las versiones actuales aún distinguen), y (2) esta cita, de la documentación @stanwise vinculada: 'La razón de esta última limitación es la necesidad de inicializar constantes simbólicas usando constructos ... como en este caso hay una necesidad de ejecutar los constructores de T, lo que conduce a posibles condiciones de carrera. "Me pregunto abiertamente si su código expone esta condición de carrera de forma inesperada, y de todo corazón de manera estándar al informar esto como un error. – MrGomez

Respuesta

2

Sucede que he encapsulado parte del impulso en un proyecto de C++/CLI de 64 bits y lo uso de C# exactamente como lo hace.

Así que me tiró en la clase de C++ en mi propio envoltorio Boost y añade este código al proyecto de C#:

private class Val 
    { 
     public double A; 
     public double B; 
     public double X; 
     public double ParallellResult; 
    } 

    private static void findParallelError() 
    { 
     var r = new Random(); 

     while (true) 
     { 
      var vals = new List<Val>(); 
      for (var i = 0; i < 1000*1000; i++) 
      { 
       var val = new Val(); 
       val.A = r.NextDouble()*100; 
       val.B = val.A + r.NextDouble()*1000; 
       val.X = r.NextDouble(); 
       vals.Add(val); 
      } 

      // parallel calculation 
      vals.AsParallel().ForAll(v => v.ParallellResult = Boost.BoostMath.InverseIncompleteBeta(v.A, v.B, v.X)); 

      /sequential verification 
      var error = vals.Exists(v => v.ParallellResult != Boost.BoostMath.InverseIncompleteBeta(v.A, v.B, v.X)); 
      if (error) 
       return; 
     } 
    } 

Y simplemente ejecuta "para siempre". Los resultados paralelos son iguales a los resultados secuenciales todo el tiempo. No hay hilo inseguridad aquí ...

¿Puedo sugerirle que descargue una copia nueva de Boost e incluirla en un proyecto completamente nuevo y probarlo?

También noté que llamó su resultado "BoostResult" ... y mencionó en los comentarios algo sobre "nuestra implementación actual". Exactamente, ¿qué estás comparando con tus resultados? ¿Cuál es su definición de "correcto"?

+0

Bonkers. Tendré que escribir una disculpa por esta pregunta. Se basa en suposiciones completamente equivocadas. Un homenaje a la fuerza de la Ley Murphys uno podría decir. El error está en el wcode de producción que estaba tratando de acelerar. –

4

Es vital que la clase Val sea segura para los hilos.

Una manera simple de asegurar esto sería hacerlo inmutable, pero veo que también tiene que escribir BoostResult. Entonces eso tendrá que ser volatile, o tener alguna forma de bloqueo.

public sealed class Val 
{ 
    // Immutable fields are inheriently threadsafe 
    public readonly double A; 
    public readonly double B; 
    public readonly double X; 

    // volatile is an easy way to make a single field thread safe 
    // box and unbox to allow double as volatile 
    private volatile object boostResult = 0.0; 

    public Val(double A, double B, double X) 
    { 
     this.A = A; 
     this.B = B; 
     this.X = X; 
    } 

    public double BoostResult 
    { 
     get 
     { 
      return (double)boostResult; 
     } 
     set 
     { 
      boostResult = value; 
     } 
    } 
} 

versión de bloqueo: (ver this question para determinar cuál es el más adecuado para su aplicación)

public sealed class Val 
{ 
    public readonly double A; 
    public readonly double B; 
    public readonly double X; 

    private readonly object lockObject = new object(); 
    private double boostResult; 

    public Val(double A, double B, double X) 
    { 
     this.A = A; 
     this.B = B; 
     this.X = X; 
    } 

    public double BoostResult 
    { 
     get 
     { 
      lock (lockObject) 
      { 
       return boostResult; 
      } 
     } 
     set 
     { 
      lock (lockObject) 
      { 
       boostResult = value; 
      } 
     } 
    } 
} 

Y si usted piensa 6 millones de cerraduras serán lentos, sólo trata de esto:

using System; 

namespace ConsoleApplication17 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      { //without locks 
       var startTime = DateTime.Now; 
       int i2=0; 
       for (int i = 0; i < 6000000; i++) 
       { 
        i2++; 
       } 
       Console.WriteLine(i2); 
       Console.WriteLine(DateTime.Now.Subtract(startTime)); //0.01 seconds on my machine 
      } 
      { //with locks 
       var startTime = DateTime.Now; 
       var obj = new Object(); 
       int i2=0; 
       for (int i = 0; i < 6000000; i++) 
       { 
        lock (obj) 
        { 
         i2++; 
        } 
       } 
       Console.WriteLine(i2); 
       Console.WriteLine(DateTime.Now.Subtract(startTime)); //0.14 seconds on my machine, and this isn't even in parallel. 
      } 
      Console.ReadLine(); 
     } 
    } 
} 
+0

No, esto no es correcto. Con la versión uno: sigo teniendo el mismo problema (y un doble no puede ser volátil, pero eso es irrelevante ya que no leo de él hasta que todos los cálculos se hayan completado). La segunda versión está descartada ya que estoy haciendo esto llamar seis millones de veces y si voy a bloquear cada vez, nuestra implementación actual será mucho más rápida. PERO: ma el rey Val a struct WORKS !!! –

+0

@danbystrom Oh sí, lo siento. Tener una versión volátil actualizada. A menos que esté satisfecho con su versión de estructura, me interesaría si funciona la nueva versión volátil. Puede que no lo lea, pero si está escrito por el hilo principal, incluso durante la inicialización de un 'nuevo Val', podría cachearse con ese hilo, creo. – weston

+1

No creo que hayas acertado con este concepto volátil. Le dice al compilador que el compilador no puede producir código que almacena en caché la variable en un registro, ya que puede cambiar en cualquier momento. No tiene un significado más profundo "thread safe". No que yo sepa, al menos ... ¡No dude en corregirme! Y, no, no estoy contento con mi solución estructural ... ¡hasta que sepa lo que están pasando los tontos! Pero me ha animado a creer que puedo hacer que esto funcione ... ¡de alguna manera! ¡No estoy seguro de que hubiera intentado lo estructural si no fuera por tu comentario! –

Cuestiones relacionadas