vb.net nested floating-accuracy parallel.foreach

VB.NET ejecuta suma en bucle anidado dentro de Parallel.for Synclock pierde información



nested floating-accuracy (1)

A continuación se muestra la mejor representación que he podido desarrollar para calcular una suma en ejecución dentro de un bucle anidado dentro de un bucle Parallel.for en VB.NET (Visual Studio 2010, .NET Framework 4). Tenga en cuenta que al mostrar los resultados en ''suma'' en la pantalla, hay una ligera diferencia entre las dos sumas y, por lo tanto, la pérdida de información en la variante paralelizada. Entonces, ¿cómo se pierde la información y qué está sucediendo? ¿Alguien puede ofrecer alguna "microcirugía" sobre la metodología para mantener una suma corriente en este contexto? (Nota para los nuevos usuarios de Parallel.for: normalmente no utilizo métodos basados ​​en cero, por lo que en la declaración Parallel.for, I1 sube a 101, ya que el código usa 101-1 como límite superior. Esto se debe a que MS desarrolló el código paralelo suponiendo contadores basados ​​en cero):

Dim sum As Double = 0 Dim lock As New Object Dim clock As New Stopwatch Dim i, j As Integer clock.Start() sum = 0 For i = 1 To 100 For j = 1 To 100 sum += Math.Log(0.9999) Next j Next i clock.Stop() MsgBox(sum & " " & clock.ElapsedMilliseconds) sum = 0 clock.Reset() clock.Start() Parallel.For(1, 101, Sub(i1) Dim temp As Double = 0 For j1 As Integer = 1 To 100 temp += Math.Log(0.9999) Next SyncLock lock sum += temp End SyncLock End Sub) clock.Stop() MsgBox(sum & " " & clock.ElapsedMilliseconds)


Estás trabajando con dobles y dobles simplemente no son precisos. En el bucle no paralelo, todos los errores se almacenan directamente en suma. En el bucle paralelo tiene un tmp adicional que luego se agrega a la suma. Use el mismo tmp en su bucle no paralelo (sumando a la suma después de que se haya ejecutado el bucle interno) y, finalmente, los resultados serán iguales en ese momento.

Dim sum As Double = 0 Dim lock As New Object Dim clock As New Stopwatch Dim i, j As Integer clock.Start() sum = 0 For i = 1 To 100 For j = 1 To 100 sum += Math.Log(0.9999) Next j Next i clock.Stop() Console.WriteLine(sum & " " & clock.ElapsedMilliseconds) sum = 0 clock.Reset() clock.Start() sum = 0 For i = 1 To 100 Dim tmp As Double = 0 For j = 1 To 100 tmp += Math.Log(0.9999) Next sum += tmp Next i clock.Stop() Console.WriteLine(sum & " " & clock.ElapsedMilliseconds) sum = 0 clock.Reset() clock.Start() Parallel.For(1, 101, Sub(i1) Dim temp As Double = 0 For j1 As Integer = 1 To 100 temp += Math.Log(0.9999) Next SyncLock lock sum += temp End SyncLock End Sub) clock.Stop() Console.WriteLine(sum & " " & clock.ElapsedMilliseconds) End Sub

salida:

-1,00005000333357 0 -1,00005000333347 0 -1,00005000333347 26

Conclusión: si trabajas con doble, entonces (a + b) + c NO es (siempre) igual a a + (b + c)

ACTUALIZAR

un ejemplo aún más simple:

Dim sum As Double For i = 1 To 100 sum += 0.1 Next Console.WriteLine(sum) sum = 0 For i = 1 To 2 Dim tmp As Double = 0 For j = 1 To 50 tmp += 0.1 Next sum += tmp Next Console.WriteLine(sum)

ahora la salida es

9,99999999999998 10