有没有办法在没有GoTo语句的情况下编写它?

时间:2013-12-19 00:45:21

标签: .net vb.net performance goto il

编辑:这不是关于使用GoTo语句是否可以的问题。

这是一个关于如何使用GoTo语句在没有的.NET / IL 中处理O(n ^ 3)算法的中心的问题。 Dijkstra哲学的坚持者和同伴,请在未能阅读问题之前注意。

考虑以下代码,其中对于大多数用例,For o = 0 to nz循环的内容将在300万到1800万次之间执行。子例程在我的代码中取代它作为Parallel.For()调用的参数。 mnynz的域名均在10到300之间。

它是手动优化的,以避免堆栈推送和子程序调用,换句话说,速度。我的愿望是避免编译IL,其中包括最内层循环内的callicall操作码。

为了在测试满足后中止最里面的三个循环,我使用GoTo语句来中止不需要的测试。

问题是,有没有办法在没有GoTo的情况下对此进行编码?是否有一种方法可以对.net JIT-Compiler编译为更快的代码进行编码,而目标代码中没有callcalli个操作码?

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

4 个答案:

答案 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() {
    ...
}

我应该提一下,这是对编译器的暗示,并且存在限制。以下内容不支持内联;

  • 虚拟方法
  • 递归方法
  • 将大值类型作为参数的方法
  • MarshalByRef类的方法
  • 复杂流程图的方法
  • 满足其他更奇特标准的方法

可能还有一个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 DoMore 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,其中包含最内层循环中的callicall操作码。

您不应该太在意代码生成IL的内容,唯一能满足性能的是机器代码(通常是x86)。

所以,你应该做的是以可读的方式编写你的代码(使用另一个带有Return的函数用于内部循环)然后测量性能以查看它是否是差异与否。

作为替代方案,您可以查看已编译的机器代码,以查看内部循环方法是否内联。