Промежуточная сумма VB.NET во вложенном цикле внутри Parallel.for Synclock теряет информацию
Ниже представлено лучшее представление, которое я смог разработать для расчета промежуточной суммы внутри цикла, который вложен в цикл Parallel.for в VB.NET (Visual Studio 2010, .NET Framework 4). Обратите внимание, что при отображении результатов в виде "суммы" на экране есть небольшая разница между двумя суммами и, следовательно, потеря информации в распараллеленном варианте. Так как же теряется информация и что происходит? Может ли кто-нибудь предложить какую-нибудь "микрохирургическую" методологию для поддержания текущей суммы в этом контексте? (Примечание для новых пользователей Parallel.for: я обычно не использую нулевые методы, поэтому в операторе Parallel.for I1 повторяется до 101, поскольку код использует 101-1 в качестве верхней границы. Это потому, что MS разработала параллельный код, предполагающий счетчики с нуля):
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)
1 ответ
Вы работаете с двойными, а двойные просто не точны. В непараллельном цикле все ошибки хранятся непосредственно в сумме. В параллельном цикле у вас есть дополнительный tmp, который позже добавляется к сумме. Используйте ту же самую tmp в вашем непараллельном цикле (добавляя к сумме после выполнения внутреннего цикла), и в итоге результаты будут равны.
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
выход:
-1,00005000333357 0
-1,00005000333347 0
-1,00005000333347 26
Вывод: если вы работаете с double, то (a + b) + c НЕ (всегда) равно a + (b + c)
ОБНОВИТЬ
еще более простой пример:
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)
теперь вывод
9,99999999999998
10