编辑:这不是关于使用GoTo语句是否可以的问题。
这是一个关于如何使用GoTo语句在没有的.NET / IL 中处理O(n ^ 3)算法的中心的问题。 Dijkstra哲学的坚持者和同伴,请在未能阅读问题之前注意。
考虑以下代码,其中对于大多数用例,For o = 0 to nz
循环的内容将在300万到1800万次之间执行。子例程在我的代码中取代它作为Parallel.For()调用的参数。 m
,ny
和nz
的域名均在10到300之间。
它是手动优化的,以避免堆栈推送和子程序调用,换句话说,速度。我的愿望是避免编译IL,其中包括最内层循环内的calli
或call
操作码。
为了在测试满足后中止最里面的三个循环,我使用GoTo语句来中止不需要的测试。
问题是,有没有办法在没有GoTo的情况下对此进行编码?是否有一种方法可以对.net JIT-Compiler编译为更快的代码进行编码,而目标代码中没有call
或calli
个操作码?
Sub SomeLambda(m As Integer, newarray As Short(,,))
For n = 0 To ny
For o = 0 To nz
If newarray(m, n, o) <> 1 AndAlso newarray(m, n, o) <> -1 Then
For m1 = m - 1 To m + 1
For n1 = n - 1 To n + 1
For o1 = o - 1 To o + 1
If SomeCondition = True Then 'the array is not out of bounds '
Dim testVal = newarray(m1, n1, o1)
If testVal = -1 Then
newarray(m, n, o) = -2
GoTo Exitloopslabel2
End If
End If
Next
Next
Next
Exitloopslabel2:
End If
Next
Next
End Sub
答案 0 :(得分:5)
是否有任何理由不将其推出单独的方法,然后使用MethodImplOptions.AggressiveInlining修饰该方法(“如果可能,应该内联该方法”)。
只要该方法满足某些要求(见下文),编译器就会在调用该方法时复制该方法。
这将允许您使用Return
并大大整理您的代码,同时跳过通常与方法调用相关联的堆栈推送,跳转等。
不幸的是,由于你施加的限制,没有太多选择。
根据要求,VB.Net中的一些示例用法:
Imports System.Runtime.CompilerServices
<MethodImpl(MethodImplOptions.AggressiveInlining)>
Public Function Blah() As String
...
End Function
和C#
using System.Runtime.CompilerServices;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string Blah() {
...
}
我应该提一下,这是对编译器的暗示,并且存在限制。以下内容不支持内联;
可能还有一个IL字节数限制(没有此标志的情况下有32个字节的限制,可以增加或完全删除)。我找不到足够的文件。
答案 1 :(得分:1)
虽然我建议你使用分离的方法来进行循环,但如果你真的热衷于使用嵌套循环,那么可以选择从你喜欢的循环中跳出来:
Dim list1 = Enumerable.Range(1, 10)
Dim list2 = Enumerable.Range(101, 10)
Dim list3 = Enumerable.Range(201, 10)
Console.WriteLine("Loop Start")
For i As Integer = 0 To list1.Count - 1
For j As Integer = 0 To list2.Count - 1
For k As Integer = 0 To list3.Count - 1
Console.WriteLine(k)
If list3(k) = 205 Then ' Assume this is the condition to exit
k = list3.Count ' -- exit the loop of list3
j = list2.Count ' -- exit the loop of list2
End If
Next
Next
Next
Console.WriteLine("Finished")
我编写了一个更简单的示例(除了使用复杂的示例),这将适用于嵌套循环(无论循环次数)。而且我认为开销很小。
答案 2 :(得分:1)
简单 - 将此作品For m1 = m - 1 To m + 1
重写为While
循环。然后做Exit While
。你可以像这样处理多个GoTo,因为还有一个Do
循环,它有自己的Exit Do
。 More about Exit Statement on MSDN
虽然,我的首选解决方案是重构它:
Sub SomeLambda(m As Integer, newarray As Short(,,))
For n = 0 To ny
For o = 0 To nz
If newarray(m, n, o) = 1 OrElse newarray(m, n, o) = -1 Then Continue For
DoSomething(m, n, o, newarray)
Next
Next
End Sub
Private Sub DoSomething(m, n, o, newarray)
For m1 = m - 1 To m + 1
For n1 = n - 1 To n + 1
For o1 = o - 1 To o + 1
If Not SomeCondition() = True Then Continue For 'the array is not out of bounds
Dim testVal = newarray(m1, n1, o1)
If testVal <> -1 Then Continue For
newarray(m, n, o) = -2
Return
Next
Next
Next
End Sub
确保它不会影响性能,并始终使用Option Strict On
。上面显然不会用它来编译 - 只是为了展示这个概念。
注意我删除了不必要的缩进,因此代码变得更加扁平,希望更具可读性。
编辑:作为性能和可维护性之间的折衷,您可以使用类级变量。所以是的,你仍然跳到DoSomething
然后回来,但没有堆栈推动。
答案 3 :(得分:0)
我的目的是避免编译IL,其中包含最内层循环中的
calli
或call
操作码。
您不应该太在意代码生成IL的内容,唯一能满足性能的是机器代码(通常是x86)。
所以,你应该做的是以可读的方式编写你的代码(使用另一个带有Return
的函数用于内部循环)然后测量性能以查看它是否是差异与否。
作为替代方案,您可以查看已编译的机器代码,以查看内部循环方法是否内联。