ThrowHelper
От: rameel https://github.com/rsdn/CodeJam
Дата: 03.11.16 12:10
Оценка: 48 (1)
В конце лета в JIT внесли изменение, учитывающее семантику методов, которые не содержат return. Грубо говоря, теперь методы, не возвращающие нормально управление обратно в вызывающий их код, помечаются флагом DOES_NOT_RETURN.

Вот здесь можно почитать подробнее: Do not inline methods that never return

Соответственно, предлагаю вынести throw Exception из внутреннестей Code/EnumCode и других методов в отдельный класс, например, в ThrowHelper.

Что это дает? Сейчас методы из набора Code инлайнятся, что очень правильно, так как позволяет с экономить на вызовах и/или избежать самих проверок. В случае, когда джит удаляет проверки проблем нет. Они возникают, если джит не может их устранить. В этом случае, вместе с встраиванием ассерта инлайнится и код, генерирующий исключение. Ассемблерный листинг показывает, что генерация исключений спокойно может раздуть код в несколько раз, для маленьких методов так вообще в 10-15 раз.

В целом, для текущего джита все останется примерно так же как сейчас, от нового мы получим профит
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re: ThrowHelper
От: Sinix  
Дата: 03.11.16 12:34
Оценка:
Здравствуйте, rameel, Вы писали:

R>В целом, для текущего джита все останется примерно так же как сейчас, от нового мы получим профит


Наивный вопрос: а как оно нам поможет?

В смысле, у нас весь код выполнен в стиле
void Check(...)
{
  if (somethingBadHappened)
  {
    throw CreateException(...);
  }
}


всё, что поменяется с DOES_NOT_RETURN — это не будет инлайниться CreateException(), который по определению находится в cold path. Сам Check инлайнится по-прежнему.
Короч, нужен тест чтоб было видно что текущее положение дел не ухудшилось.
Re[2]: ThrowHelper
От: rameel https://github.com/rsdn/CodeJam
Дата: 03.11.16 13:47
Оценка:
Здравствуйте, Sinix, Вы писали:

  Скрытый текст
S>В смысле, у нас весь код выполнен в стиле
S>
S>void Check(...)
S>{
S>  if (somethingBadHappened)
S>  {
S>    throw CreateException(...);
S>  }
S>}
S>

S>всё, что поменяется с DOES_NOT_RETURN — это не будет инлайниться CreateException(), который по определению находится в cold path. Сам Check инлайнится по-прежнему.

Предлагается сделать так:
void Check(...)
{
  if (somethingBadHappened)
  {
    ThrowHelper.ThrowSomeException();
  }
}

class ThrowHelper
{
    public static void ThrowSomeException()
    {
        throw CodeExceptions.SomeException();
    }
}


В данном случае инлайнится Check и CodeExceptions. С DOES_NOT_RETURN не инлайнится ThrowHelper.ThrowSomeException(), который переедет в cold path.

Для вот такого кода
  Пример кода на C#
public static int Add(int value)
{
    Assert(value, nameof(value));
    return value + 10;
}

public static void Assert(int value, string paramName)
{
    if (value < 0)
        throw CreateException(paramName);
}

private static ArgumentOutOfRangeException CreateException(string paramName)
{
    return new ArgumentOutOfRangeException(paramName);
}

c DOES_NOT_RETURN и без него джит генерирует такой код
00007FF7BA0D04C0  push        rsi  
00007FF7BA0D04C1  sub         rsp,20h  

            Assert(value, nameof(value));
00007FF7BA0D04C5  test        ecx,ecx  
00007FF7BA0D04C7  jl          00007FF7BA0D04D2

            return value + 10;
00007FF7BA0D04C9  lea         eax,[rcx+0Ah]  
00007FF7BA0D04CC  add         rsp,20h  
00007FF7BA0D04D0  pop         rsi  
00007FF7BA0D04D1  ret  

            Assert(value, nameof(value));
00007FF7BA0D04D2  mov         rcx,7FF818831FC0h  
00007FF7BA0D04DC  call        00007FF8196C2510  
00007FF7BA0D04E1  mov         rsi,rax  
00007FF7BA0D04E4  mov         ecx,1  
00007FF7BA0D04E9  mov         rdx,7FF7B9FC40E0h  
00007FF7BA0D04F3  call        00007FF8198737E0  
00007FF7BA0D04F8  mov         rdx,rax  
00007FF7BA0D04FB  mov         rcx,rsi  
00007FF7BA0D04FE  call        00007FF818ED4890  
00007FF7BA0D0503  mov         rcx,rsi  
00007FF7BA0D0506  call        00007FF8198604E0  
00007FF7BA0D050B  int         3


Вот для такого
  Пример кода на C#
public static int Add(int value)
{
    Assert(value, nameof(value));
    return value + 10;
}

public static void Assert(int value, string paramName)
{
    if (value < 0)
    {
        // Вынесли код, бросающий исключение в отдельный метод
        ThrowException(paramName);
    }
}

private static void ThrowException(string paramName)
{
    throw CreateException(paramName);
}

private static ArgumentOutOfRangeException CreateException(string paramName)
{
    return new ArgumentOutOfRangeException(paramName);
}

c DOES_NOT_RETURN джит генерирует вот такое
00007FF7BA0D04C0  push        rsi  
00007FF7BA0D04C1  sub         rsp,20h  

            Assert(value, nameof(value));
00007FF7BA0D04C5  test        ecx,ecx  
00007FF7BA0D04C7  jl          00007FF7BA0C04CB

            return value + 10;
00007FF7BA0D04C9  lea         eax,[rcx+0Ah]  
00007FF7BA0D04CC  add         rsp,20h  
00007FF7BA0D04D0  pop         rsi  
00007FF7BA0D04D1  ret  

            Assert(value, nameof(value));
00007FF7BA0C04CB  mov         rcx,1D39B833578h  
00007FF7BA0C04D5  mov         rcx,qword ptr [rcx]  
00007FF7BA0C04D8  call        00007FF7BA0C0090

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

S>Короч, нужен тест чтоб было видно что текущее положение дел не ухудшилось.


Для текущего джита можно навесить на методы ThrowHelper.Throw... MethodImplOptions.AggressiveInlining, тогда асм. код будет один в один как сейчас.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Отредактировано 03.11.2016 13:52 rameel . Предыдущая версия .
Re[3]: ThrowHelper
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 03.11.16 19:34
Оценка:
Здравствуйте, rameel, Вы писали:

R>Для текущего джита можно навесить на методы ThrowHelper.Throw... MethodImplOptions.AggressiveInlining, тогда асм. код будет один в один как сейчас.


Ну, если это текущие фреймворки не затронет, то можешь и поменять.

Тут другой момент — Core мы пока что не поддерживаем вообще, а когда оно доберется до .Net FW — одному богу известно.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[3]: ThrowHelper
От: Sinix  
Дата: 03.11.16 20:02
Оценка:
Здравствуйте, rameel, Вы писали:

R>Предлагается сделать так:

Угу, дошло, про что речь. В минус — ~16 дополнительных методов только для CodeExceptions (не забываем, что надо ещё и копировать / синхронизировать xml-документацию), в плюс — сокращение внедряемого il за счёт переноса обработки return value и throw в non-inlineable-метод. Я что-то такое видел для исключений, которые вытаскивают сообщения из ресурсов, но сильно не уверен, что оно даст хоть что-то в плане производительности в нашем случае. Надо мерять.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.