Как получить постоянную длину строки без системного вызова?

#c# #optimization #cil

Вопрос:

Пример кода:

 const string UtcTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffZ";
public byte[] UtcTimeAscii => Encoding.ASCII.GetBytes(DateTime.UtcNow.ToString(UtcTimeFormat));
 

Вот что я получил в качестве IL для Length свойства: (Используя Microscope VS add in)
введите описание изображения здесь

Теперь: почему существует системный вызов, а не просто обычный 24 ? Это постоянная строка. Есть ли способ сообщить компилятору, что это известно заранее?

Я знаю, я могу определить 24 как const int , но это неправильно, потому что не было бы прямой привязки между фактической строкой и ее длиной. Конечно, я могу позвонить Length() , но мне просто любопытно, может быть, есть специальный синтаксис для использования такого рода оптимизации?

Я также переключил конфигурацию с Debug на Release , но системный вызов остался.

Комментарии:

1. Я предполагаю здесь, но Length это метод (ну, это свойство, которое в C# реализовано как методы под капотом, как вы можете видеть в IL). Поэтому компилятор не может знать во время компиляции, что Length свойство не имеет побочных эффектов (например, возможно, оно форматирует жесткий диск). Поэтому он должен вызвать его (хотя дрожание может его оптимизировать).

2. Примечание, но если это для реальной оптимизации, это зашкаливает — если вы оптимизируете на этом уровне, я ожидаю какой-то формы встроенных систем

3. Помните, что вы смотрите на ИЛ . Промежуточный . Вы не видите, какой код на самом деле будет выполняться, я не уверен, какую оптимизацию здесь может выполнить JIT.

4. Но вот убедительная подсказка : «Фактически сгенерированный для этого код будет представлять собой одну инструкцию и будет встроен».

5. В каком сценарии доступ к Length собственности является для вас узким местом? Я часто кэширую длину в локальной переменной, если она используется в a for , но я даже не уверен, что JIT-компилятор не выполнит ту же оптимизацию. (Правка: неважно, Дэмиен только что прокомментировал ответ в тот же момент)

Ответ №1:

Эта оптимизация, если бы она была выполнена, была бы выполнена не компилятором C#, а JIT-компилятором во время (ну, непосредственно перед) выполнения. IL, как правило, не указывает на то, какие оптимизации происходят, и никогда не может доказать отсутствие данной оптимизации.

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

 C.M()
    L0000: mov eax, 0x18
    L0005: ret
 

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

Комментарии:

1. когда я публикую проект (.NET 5.0) с ReadyToRun RTR опцией () — получает ли он эти оптимизации? Если эта опция доступна только для Windows, как она работает в целевой сборке Linux, это только JIT?

2. @Гарри Я не знаю наверняка, будет ли эта оптимизация включена в машинный код, который поставляется в сборке R2R, я ожидаю, что это произойдет, но даже если этого не произойдет, горячий метод все равно будет заблокирован, так что есть второй шанс сделать это правильно. И я думал, что R2R также доступен для целей Linux, но это не то, что я когда-либо пытался сделать на самом деле.