2013-12-19 2 views
6

EDIT: Это не вопрос о том, можно ли использовать инструкцию GoTo.Есть ли способ написать это без инструкции GoTo?

Это вопрос о том, как обращаться с центром в O алгоритма (п^3) в .NET/IL без используя заявление GoTo. Сторонники и попутчики философии Дейкстры, пожалуйста, обратите внимание, прежде чем не прочитать этот вопрос.

Рассмотрим следующий код, в котором для большинства случаев использование цикла For o = 0 to nz будет выполняться от 3 до 18 миллионов раз. Подпрограмма занимает свое место в моем коде как аргумент для вызова Parallel.For(). Область m, ny и nz все между 10 и 300.

Это ручной оптимизирован, чтобы избежать стека выталкивает и вызовы подпрограмм, другими словами, для скорости. Мое желание состоит в том, чтобы избежать компиляции IL, которая включает в себя код операции calli или call внутри самого внутреннего цикла.

Чтобы прервать самую внутреннюю три петли после завершения теста, я использую инструкцию GoTo для отмены ненужных тестов.

Вопрос в том, есть ли способ кодировать это без GoTo? Есть ли способ закодировать код, который компилятор .net JIT-компилятор будет компилировать для более быстрого кода без 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

Большинство людей сказали бы, что вы должны реорганизовать вложенные циклы, чтобы избежать goto, но если у вас есть основания полагать, что вызовы подпрограмм будут слишком дорогими (т. Е. Вы его измерили), то это действительно единственное решение. – siride

+0

Не дубликат: другой вопрос предлагает решение, которое формирует образец кода моего вопроса. Было бы неплохо, если бы кто-то опубликовал ответ, который я могу возместить и одобрить, а не просто комментарий. –

+0

@RobPerkins: Интересно, это лучший вопрос для программистов.stackexchange.com? Мне не нравится балканизация сайтов SE, но это то, что есть. – siride

ответ

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

Следует отметить, что это намек на компилятор, и есть пределы. Следующие не поддерживают inlining;

  • Виртуальные методы
  • Рекурсивные методы
  • Методы, которые принимают большое значение типа в качестве параметра
  • методы на классах MarshalByRef
  • Методы со сложными flowgraphs
  • методы, отвечающие другие, более экзотические критерии

Также может быть IL байта (это 32 байта без этого флага).

+0

Basic, единственная причина, по которой я могу думать, это то, что я не знал о MethodImplOptions.AggressiveInlining. Я попробую это! –

+0

Готовы ли вы отредактировать свой ответ, включив в него заголовок функции C# или VB.Net с соответствующим оформлением? –

+0

@ RobPerkins Немного поздно для примеров, но я надеюсь, что это поможет. – Basic

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") 

я написал более простой пример (кроме использования вашего сложного), это будет работать с вложенными циклами (независимо от количества циклов). и я считаю, что накладные расходы были бы минимальными.

+0

Это выглядит даже уродливее, чем «GoTo», я думаю. – svick

+0

именно поэтому я бы сказал, что использование разделенного метода лучше. – Rex

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. Вышеизложенное, очевидно, не скомпилируется с ним - просто для того, чтобы показать концепцию.

Уведомление Я удалил ненужный отступ, поэтому код стал более плоским и, надеюсь, более читаемым.

EDIT: В качестве компромисса между производительностью и ремонтопригодностью можно использовать переменные уровня класса. Итак, да, вы по-прежнему прыгаете на DoSomething и обратно, но ни один стек не толкает.

0

Мое желание, чтобы избежать компиляцию IL, который включает в себя calli или call опкод внутри внутреннего цикла.

Вы не должны заботиться о том, какой IL генерируется вашим кодом, единственное, что влияет на производительность, - это машинный код (обычно x86).

Итак, что вы должны сделать, это написать свой код в читаемом способом (с помощью другой функции с Return для внутреннего цикла), а затем мера производительность, чтобы увидеть сделали ли разница или нет.

В качестве альтернативы вы можете посмотреть скомпилированный машинный код, чтобы увидеть, был ли встроен метод внутренней петли или нет.

+0

Конечно, в качестве 20-летнего ветерана разработки программного обеспечения в целом, я не разбираюсь в этой методологии, так как это то, что я использовал, в комплекте с профилировщиком RedGate, чтобы получить функцию, которую у меня есть. –

+0

@ RobPerkins Итак, вы говорите, что вы измеряли обе версии, а другая с другим методом была на самом деле медленнее? Потому что я не заметил, что вы упоминаете об этом где угодно, и я думаю, что это важная информация. – svick

+0

Я весь день профилировал этот код. –

Смежные вопросы