2009-07-06 34 views
8

Mi tarea actual es optimizar un Monte Carlo Simulation que calcula las cifras de adecuación de capital por región para un conjunto de deudores.C# Monte Carlo Riesgo incremental Optimización del cálculo, números aleatorios, ejecución paralela

Se está ejecutando aproximadamente 10 veces más lento para donde tendrá que estar en producción y se necesita un número o ejecuciones diarias. Además, la granularidad de las cifras de resultados tendrá que mejorarse hasta el nivel de libro de escritorio, posiblemente, en algún momento, el código que me han dado es básicamente un prototipo que las unidades de negocio utilizan en una capacidad de semi producción.

La aplicación es actualmente el único subproceso así que necesitaré para que sea multi-hilo, puede mirar a System.Threading.ThreadPool o la Microsoft Parallel Extensions biblioteca, pero estoy obligado a .NET 2 en el servidor en este banco, así que quizás tenga que considerar el puerto de este tipo, http://www.codeproject.com/KB/cs/aforge_parallel.aspx.

Hago todo lo posible para que se actualicen a .NET 3.5 SP1, pero es un ejercicio importante en una organización de este tamaño y podría no ser posible en mis marcos de tiempo de contrato.

que he perfilado la aplicación usando el ensayo de dotTrace (http://www.jetbrains.com/profiler). ¿Qué otros buenos perfiladores existen? ¿Gratis?

Gran parte del tiempo de ejecución se gasta generando números aleatorios uniformes y luego traduciéndolo a un número aleatorio distribuido normalmente. Están utilizando una implementación de C# Mersenne twister. No estoy seguro de dónde lo obtuvieron o si es la mejor manera de hacerlo (o la mejor implementación) para generar los números aleatorios uniformes. Entonces esto es traducido a una versión normalmente distribuida para usar en el cálculo (aún no he profundizado en el código de traducción).

¿Cuál es la experiencia al utilizar lo siguiente?

Cualquier alternativa que conozca? Soy desarrollador de C# por lo que preferiría C#, pero un contenedor para C++ no debería ser un problema, ¿o sí?

Quizás incluso más rápido aprovechando las implementaciones de C++. Estoy pensando que algunas de estas bibliotecas tendrán el método más rápido para generar directamente números aleatorios distribuidos normalmente, sin el paso de traducción. También pueden tener algunas otras funciones que serán útiles en los cálculos posteriores.

También el ordenador está en un núcleo Opteron es quad 275, 8 GB de memoria pero Windows Server 2003 Empresa 32 bits. ¿Debo recomendarles que actualicen a 64 bit OS?Cualquier enlace a artículos que respalden esta decisión sería realmente apreciado.

De todos modos, cualquier consejo y ayuda que pueda tener es realmente apreciado.

+2

¿Por qué crees que tirar más hilos al problema mejorará las cosas? –

+0

Actualmente, el código es de un solo hilo ejecutándose en una caja de cuatro núcleos, Opteron 275 para ser precisos. El código está escrito para ejecutarse de forma secuencial, ya sea el compilador o el CLR o el conjunto de instrucciones de la CPU que puede adivinar cómo tomar este código e intentar ejecutar partes de él en paralelo para mejorar el rendimiento. O puedo escribir este código para ejecutarlo en un modelo paralelo enhebrado, sugiriendo efectivamente al CLR, compilador y CPU lo que se puede ejecutar al mismo tiempo y permitir que estas instrucciones de nivel inferior luego optimicen la ejecución. ¿Tus pensamientos? – m3ntat

+1

8 GB de memoria son 4 GB de desperdicio en 32 bits ... –

Respuesta

4

He encontrado que el Mersenne Twister es rápido. El problema puede estar en el algoritmo (Box-Muller) para transformar la distribución uniforme en distribución gaussiana. El algoritmo estándar se parece a:

y1 = sqrt(- 2 ln(x1)) cos(2 pi x2) 
y2 = sqrt(- 2 ln(x1)) sin(2 pi x2) 

Donde x1 y x2 son números aleatorios uniformes y Y1 e Y2 son las salidas de distribución de Gauss.

Las raíces cuadradas son lentos, pero el trig es peor, y es inestable cercano a 0. Taygeta's page sobre el tema da una más rápida (en pseudocódigo):

  float x1, x2, w, y1, y2; 

    do { 
      x1 = 2.0 * ranf() - 1.0; 
      x2 = 2.0 * ranf() - 1.0; 
      w = x1 * x1 + x2 * x2; 
    } while (w >= 1.0); 

    w = sqrt((-2.0 * ln(w))/w); 
    y1 = x1 * w; 
    y2 = x2 * w; 

Si no están usando algo así, puede acelerar un poco las cosas al evitar las funciones trigonométricas o incluso pregenerar los números aleatorios.

+0

Como nota, muchos procesadores modernos tienen instrucciones de ensamblaje para el cálculo simultáneo de sen y cos, y es mucho más económico que llamar ambos secuencialmente. No está disponible en ninguna biblioteca estándar, afaik, ya que es una función específica del procesador. –

+0

Gracias @R Ubben, ¿su propuesta es la misma que esta http://en.wikipedia.org/wiki/Box-Muller_transformation o es algo diferente? – m3ntat

+0

Sí, la forma polar que describen. Es un muestreo de rechazo, por lo que arrojas algunos números, pero aún así termina mucho más rápido. Aunque también trabajo en banca, estaba haciendo esto para el disfrute: iluminación global en un rayo trazador. Sí marcó la diferencia. Si la velocidad sigue siendo un problema, puede generar varios cientos de millones de ejecuciones fuera de línea entre las ejecuciones diarias, dependiendo de la cantidad de ejecuciones, y simplemente leerlas según sea necesario. Luego vuelva a la generación si la tienda está agotada. –

1

¿Ha considerado señalar a profiler at your code? He visto casos en los que hay soluciones simples que obtienen mejoras muy significativas. Como cambiar un par de propiedades a campos.

+0

He intentado con la versión de prueba de dottrace pero los resultados no fueron tan granulares, ¿tendré una oportunidad con RedGate si tienen una versión de prueba gratuita? – m3ntat

+0

Creo que sí, las hormigas me han salvado un montón de veces. –

0

estar obligado a utilizar .Net en el primer lugar de una simulación a gran escala va a costar un poco de rendimiento de hasta delante ... pero dicho ...

Si eres ejecutando una implementación pura de C# del Mersenne Twister, es probable que le cueste ajustar todo el rendimiento posible. Si revisa el Mersenne Twister reference implementation, verá que tienen una versión C que está muy optimizada para procesadores con capacidad SSE, esto es muy rápido. No creo que sea posible en C# (o al menos, no sé cómo) forzar el uso de las instrucciones de SSE con ese nivel de optimización. Sugeriría escribir un contenedor de C++/CLI (o un contenedor de P/Invoke) alrededor de las bibliotecas de Mersenne Twister, y ver cómo eso afecta su rendimiento. Sin embargo, tendrá que tener cuidado con los ataques administrados no administrados que afecten su rendimiento, ya que he visto otras publicaciones aquí en SO sobre ese tema (aunque parece que no puedo encontrarlas en este momento ...).

Puedo generar algo de llama para decir esto, pero si el rendimiento es una preocupación importante en su aplicación, C o C++ bien escrito casi siempre va a ser preferible a cualquier lenguaje administrado o interpretado.

+2

En realidad, estoy en desacuerdo bastante fuerte con su última declaración ... C# puede funcionar muy, muy bien. Requiere perfil, pero en mi experiencia, es mucho más fácil perfilar C# y mejorarlo hasta el punto en que puede superar a C y C++, especialmente si aprovecha las bibliotecas correctas, y entiende cómo escribir código ajustado y de alto rendimiento. Cª#. –

+1

También quiero estar en desacuerdo constructivo :) – peterchen

+1

@Reed - Permítanme aclararlo - No estoy hablando de la facilidad de los perfiles, ni las herramientas disponibles, ni la dificultad de optimizar. Estoy afirmando que para cualquier programa escrito en un lenguaje interpretado o administrado, se puede demostrar que un programa funcionalmente igual con un rendimiento igual o mejor se puede escribir en un lenguaje no administrado. –

0

Mi experiencia es que el rendimiento relativo de C# contra C++ depende en gran medida de lo que está haciendo. Una gran discusión de eso aquí:

C++ performance vs. Java/C#

Para bucles apretados hace matemáticas (digamos los cálculos físicos vector) C++ es un 2-3 veces más rápido que C# aunque la perforación puede estar dominada por las funciones subyacentes como sqrt ()

He adoptado un enfoque de lenguaje mixto, (re) implementando el código más lento en C++/OpenMP con un contenedor administrado de C++/CLI. Esto le permite solo "pagar por lo que usa".

Hay un resumen de cómo envolver nativo de C/C++ con C++/CLI aquí:

http://msdn.microsoft.com/en-us/library/ms235281.aspx

Una vez que el cuelgue de C++/CLI que es bastante fácil de conseguir las cosas funcionando.