VB.NET 在 Parallel.for Synclock 内的嵌套循环中运行 sum 丢失信息
Posted
技术标签:
【中文标题】VB.NET 在 Parallel.for Synclock 内的嵌套循环中运行 sum 丢失信息【英文标题】:VB.NET running sum in nested loop inside Parallel.for Synclock loses information 【发布时间】:2012-11-28 13:59:25 【问题描述】:下面是我能够开发的用于计算循环内的运行总和的最佳表示,该循环嵌套在 VB.NET(Visual Studio 2010,.NET Framework 4)中的 Parallel.for 循环内。请注意,当在屏幕上显示“总和”的结果时,两个总和之间存在细微差别,因此在并行化变体中会丢失信息。那么信息是如何丢失的,发生了什么?任何人都可以提供一些关于在这种情况下保持流动总和的方法的“显微外科手术”吗? (注意 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】:您正在使用双打,而双打根本不准确。 在非并行循环中,所有错误都直接存储在 sum 中。在并行循环中,您有一个额外的 tmp,稍后将其添加到 sum。在非并行循环中使用相同的 tmp(在内循环运行后添加到 sum),最终结果将相等。
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
【讨论】:
感谢您的建议,他们做得很好。但是,关于 double 不准确的说法听起来像是您知道具有更高数字精度的 Visual Studio 变量类型?你会用什么比 double 更准确? @LEP:Decimal
更准确。当然它会占用更多内存并且计算速度要慢得多,因为 CPU 不直接支持Decimal
。
我有很多嵌套循环,其中包含大段代码以及内部许多矩阵的处理。您能否推荐一种将大量工作捆绑在 parallel.for 中的适当方法? (我认为会有很多基于 lambda 的问题。)以上是关于VB.NET 在 Parallel.for Synclock 内的嵌套循环中运行 sum 丢失信息的主要内容,如果未能解决你的问题,请参考以下文章
我可以在 Parallel.For 循环中使用相同的函数委托吗
C#的并发循环(for,foreach,parallel.for,parallel.foreach)对比