Как предотвратить использование JIT-компилятором этого метода - PullRequest
4 голосов
/ 24 февраля 2011

У меня есть метод расширения, который выглядит следующим образом:

//[MethodImpl(MethodImplOptions.NoOptimization)]
public static IEnumerable<char> TakeWhile(this BinaryReader reader, Func<int, bool> condition)
{
    while (condition(reader.PeekChar()))
    {
        char c = reader.ReadChar();
        yield return c;
    }
}

Я использую этот метод при анализе файла с BinaryReader, чтобы пропустить блок пробельных символов, среди прочего. Я обнаружил, что JIT-компилятор оптимизирует его, когда я называю это так:

// Skip white space
this.reader.TakeWhile(IsWhiteSpace);//.FirstOrDefault();

Я попытался добавить атрибут [MethodImpl (...)], чтобы дать указание компилятору JIT не оптимизировать метод, но он не работает. Теперь, очевидно, я мог бы написать еще одну реализацию этого, которая манипулирует позицией основного буфера потока, но из любопытства я хотел бы знать, почему это так.

Единственный способ предотвратить оптимизацию, который я нашел, - это использовать результаты IEnumerable (например, с помощью вызова .FirstOrDefault (), как указано выше) или скопировать код в вызывающий метод. Я пытался предотвратить оптимизацию вызывающих методов, используя MethodImplAttribute, но это не работает. Как ни странно, оптимизация полностью отключена в сборке Debug, поэтому она не должна происходить ни в одной ситуации. Кто-нибудь знает другой способ предотвращения оптимизации?

1 Ответ

8 голосов
/ 24 февраля 2011

Нет, JIT не оптимизирует . Однако ни один из ваших кодов не будет выполнен - ​​потому что вы игнорируете возвращаемое значение. До первого вызова MoveNext() не выполняется ни один код в блоке итератора. Это не имеет ничего общего с JIT, и все, что связано с работой блоков итераторов.

Возможно, вы захотите прочитать мои статьи о блоках итераторов ( основы , подробности реализации ) и сообщение в блоге Эрика Липперта "Психическая отладка" ( part 1 ; часть 2 ).

Обратите внимание, что при вызове FirstOrDefault() будет прочитан только первый символ. Звучит так, будто вы действительно хотите использовать весь поток до тех пор, пока не произойдет сбой условия, что означает использование чего-то вроде Count(), которое будет повторяться по всей возвращаемой последовательности.

В качестве альтернативы - и предпочтительно IMO - написать «активный» метод с пустым типом возврата, чтобы сделать это. Если вас не интересует возвращаемое значение метода, это сигнал о том, что это, вероятно, не идеальный метод для вызова. (Это не всегда случай, но обычно это так.)

...