С# - Внутренние элементы для петли

быстрый, простой вопрос от меня о for-loops.

Ситуация В настоящее время я пишу какой-то высокопроизводительный код, когда мне вдруг стало интересно, как работает for-loop. Я знаю, что я наткнулся на это раньше, но не могу, чтобы жизнь меня снова находила эту информацию:/

Тем не менее, моя главная проблема была связана с ограничителем. Скажем, у нас есть:

for(int i = 0; i < something.awesome; i++)
{
// Do cool stuff
}

Вопрос Является ли что-то. Сохраненным как внутренняя переменная или цикл постоянно извлекает что-то. Как сделать логическую проверку? Почему я спрашиваю, конечно, потому что мне нужно перебирать множество индексированных материалов, и я действительно не хочу лишних служебных накладных функций для каждого прохода.

Однако, если что-то.развитие называется только один раз, то я возвращаюсь под свой счастливый рок!:)

Ответы

Ответ 1

Вы можете использовать простую программу для проверки поведения:

using System;

class Program
{
    static int GetUpperBound()
    {
        Console.WriteLine("GetUpperBound called.");
        return 5;
    }

    static void Main(string[] args)
    {
        for (int i = 0; i < GetUpperBound(); i++)
        {
            Console.WriteLine("Loop iteration {0}.", i);
        }
    }
}

Вывод следующий:

GetUpperBound called. 
Loop iteration 0. 
GetUpperBound called. 
Loop iteration 1. 
GetUpperBound called. 
Loop iteration 2. 
GetUpperBound called. 
Loop iteration 3. 
GetUpperBound called. 
Loop iteration 4. 
GetUpperBound called.

Подробности этого поведения описаны в Спецификации языка С# 4.0, раздел 8.3.3 (вы найдете спецификацию внутри C:\Program Files\Microsoft Visual Studio 10.0\VС#\Specifications\1033):

Оператор A для выполнения выполняется как следующим образом:

  • Если присутствует инициализатор, инициализаторы переменных или оператор выражения выполняются в порядке они написаны. Этот шаг только выполняется один раз.

  • Если присутствует условие, оно оценивается.

  • Если условие отсутствия отсутствует или если оценка дает true, управление передается во встроенный выражение. Когда и если контроль достигнет конечная точка встроенного (возможно, от выполнения оператор продолжения), выражения для итератора, если таковые имеются, являются оценивается последовательно, а затем выполняется другая итерация, начиная с оценки для условия на предыдущем шаге.

  • Если условие условия присутствует, и оценка дает false, управление переносится в конечную точку для утверждения.

Ответ 2

Если something.awesome является полем , он, вероятно, будет иметь доступ каждый раз вокруг цикла, поскольку что-то в теле цикла может его обновить. Если тело цикла достаточно простое и не вызывает каких-либо методов (кроме методов, которые встроены в компилятор), тогда компилятор может доказать, что безопасно помещать значение something.awesome в регистр. Писатели-компиляторы часто ходили на многое, чтобы делать это.

Однако в эти дни требуется очень много времени, чтобы получить доступ к значению из основной памяти, но как только значение было прочитано в первый раз, он тренируется CPU. Чтение значения в 2-й раз из кэша ЦП намного ближе по скорости, чтобы считывать его из регистра, а затем читать его из основной памяти. Ненормально, что кеш процессора в сотни раз быстрее, чем основная память.

Теперь, если something.awesome является свойством, то это фактически вызов метода. Компилятор будет вызывать метод каждый раз по циклу. Однако, если свойство/метод - это всего лишь несколько строк кода, он может быть встроен компилятором. Inlineing - это когда компилятор помещает копию кода метода непосредственно, а не вызывает метод, поэтому свойство, которое просто возвращает значение поля, будет вести себя так же, как пример поля выше.

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

В старые времена это было просто, потому что все операции доступа к памяти и действия процессора заняли примерно одно и то же время. В наши дни кеш процессора может легко изменить время для некоторых обращений к памяти и вызовов методов с помощью в 100 раз. Профилиторы, как правило, все же предполагают, что все доступ к памяти происходит в одно и то же время! Поэтому, если вы профиль, вам будет предложено внести изменения, которые могут не иметь никакого эффекта в реальном мире.

Изменение кода на:

int limit = something.awesome; 
for(int i = 0; i < limit; i++) 
{ 
// Do cool stuff 
}

Будет в некоторых случаях распространяться, но также делает его более сложным. Однако

int limit = myArray.length; 
for(int i = 0; i < limit; i++) 
{ 
   myArray[i[ = xyn;
}

медленнее, чем

for(int i = 0; i < myArray.length; i++) 
{ 
   myArray[i[ = xyn;
}

as.net проверяет привязку массивов при каждом доступе и имеет логику для удаления проверки, когда цикл достаточно прост.

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

Ответ 3

Он оценивается каждый раз. Попробуйте это в простом консольном приложении:

public class MyClass
{
    public int Value
    {
        get
        {                
            Console.WriteLine("Value called");
            return 3;
        }
    }
}

Используется следующим образом:

MyClass myClass = new MyClass();
for (int i = 0; i < myClass.Value; i++)
{                
}

В результате на экране будут напечатаны три строки.

Обновление

Чтобы избежать этого, вы можете это сделать:

int awesome = something.awesome;
for(int i = 0; i < awesome; i++)
{
// Do cool stuff
}

Ответ 4

something.awesome будет пересматриваться каждый раз, когда вы проходите цикл.

Было бы лучше сделать это:

int limit = something.awesome;
for(int i = 0; i < limit; i++)
{
// Do cool stuff
}

Ответ 5

Каждый раз, когда компилятор будет извлекать значение something.awesome и оценивать его

Ответ 6

Условие оценивается каждый раз, включая получение значения something.awesome. Если вы хотите этого избежать, установите временную переменную something.awesome и сравните ее с переменной temp.

Ответ 7

Он будет хранить некоторую переменную и очищать ее после использования.

используя (int limit = something.awesome)
{
for (int я = 0; я < limiti; я ++)
{
// Код.
}
}

Таким образом, он не будет проверять каждый раз.