Информация об изменениях

Сообщение Re[2]: Сделал так от 31.07.2020 19:26

Изменено 31.07.2020 19:33 Mystic Artifact

Re[2]: Сделал так
Здравствуйте, igor-booch, Вы писали:

IB>Всем спасибо, c рефлешеном, понятно, что можно всё, но производительность страдает,

IB>пока сделал так

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

Ну, например:

public T GetValue<T>() {
  if (typeof(T) == typeof(int)) {
    return (T)(object)GetIntValue();
  }
  else if (typeof(T) == typeof(double)) {
    return (T)(object)GetDoubleValue();
  }
  else if (typeof(T) == typeof(string)) {
    return (T)(object)GetStringValue();
  }
  else throw new InvalidOperationException();
}


Такой метод будет раскрываться в GetIntValue/GetDoubleValue/GetStringValue как будто никакого generic метода и нет вовсе, и т.к. T заранее известен — цепочка боксинга/анбоксинга так же подавляется, даже в Debug билдах.

--

С методом "public static void Do<TClass>(TClass @object) where TClass : IBase" немного иначе — в виду того, что на сегодня есть только фич-реквесты для раздельной компиляции специализаций. Тем не менее если метод Do<TClass> заставить инлайнится — то результат будет так же хорошим.

В частности с хинтом AggressiveInlining на Do и AggressiveOptimization на Main (не уверен что они нужны), метод Main примет форму (испытано на .net 5):

IN0015: 000000 sub      rsp, 40

G_M59663_IG02:        ; offs=000004H, size=0072H, bbWeight=1    PerfScore 20.75, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byref

IN0001: 000004 mov      rcx, 0x257900030B8
IN0002: 00000E mov      rcx, gword ptr [rcx]
IN0003: 000011 call     Console:WriteLine(String)
IN0004: 000016 mov      rcx, 0x257900030C0
IN0005: 000020 mov      rcx, gword ptr [rcx]
IN0006: 000023 call     Console:WriteLine(String)
IN0007: 000028 mov      rcx, 0x257900030C8
IN0008: 000032 mov      rcx, gword ptr [rcx]
IN0009: 000035 call     Console:WriteLine(String)
IN000a: 00003A mov      rcx, 0x257900030B8
IN000b: 000044 mov      rcx, gword ptr [rcx]
IN000c: 000047 call     Console:WriteLine(String)
IN000d: 00004C mov      rcx, 0x257900030C0
IN000e: 000056 mov      rcx, gword ptr [rcx]
IN000f: 000059 call     Console:WriteLine(String)
IN0010: 00005E mov      rcx, 0x257900030C8
IN0011: 000068 mov      rcx, gword ptr [rcx]
IN0012: 00006B call     Console:WriteLine(String)
IN0013: 000070 call     Console:ReadLine():String
IN0014: 000075 nop      

G_M59663_IG03:        ; offs=000076H, size=0005H, bbWeight=1    PerfScore 1.25, epilog, nogc, extend

IN0016: 000076 add      rsp, 40
IN0017: 00007A ret


Как видно — в данном простом случае — JIT справился более чем отлично. Понятно, что это не совсем универсально (в случае если JIT откажется инлайнить — то будет уже совсем не всё так красиво ).
Re[2]: Сделал так
Здравствуйте, igor-booch, Вы писали:

IB>Всем спасибо, c рефлешеном, понятно, что можно всё, но производительность страдает,

IB>пока сделал так

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

Ну, например:

public T GetValue<T>() {
  if (typeof(T) == typeof(int)) {
    return (T)(object)GetIntValue();
  }
  else if (typeof(T) == typeof(double)) {
    return (T)(object)GetDoubleValue();
  }
  else if (typeof(T) == typeof(string)) {
    return (T)(object)GetStringValue();
  }
  else throw new InvalidOperationException();
}


Такой метод будет раскрываться в GetIntValue/GetDoubleValue/GetStringValue как будто никакого generic метода и нет вовсе, и т.к. T заранее известен — цепочка боксинга/анбоксинга так же подавляется, даже в Debug билдах.

--

С методом "public static void Do<TClass>(TClass @object) where TClass : IBase" немного иначе — в виду того, что на сегодня есть только фич-реквесты для раздельной компиляции специализаций. Тем не менее если метод Do<TClass> заставить инлайнится — то результат будет так же хорошим.

В частности с хинтом AggressiveInlining на Do и AggressiveOptimization на Main (не уверен что они нужны), метод Main примет форму (испытано на .net 5):

IN0015: 000000 sub      rsp, 40

G_M59663_IG02:        ; offs=000004H, size=0072H, bbWeight=1    PerfScore 20.75, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byref

IN0001: 000004 mov      rcx, 0x257900030B8
IN0002: 00000E mov      rcx, gword ptr [rcx]
IN0003: 000011 call     Console:WriteLine(String)
IN0004: 000016 mov      rcx, 0x257900030C0
IN0005: 000020 mov      rcx, gword ptr [rcx]
IN0006: 000023 call     Console:WriteLine(String)
IN0007: 000028 mov      rcx, 0x257900030C8
IN0008: 000032 mov      rcx, gword ptr [rcx]
IN0009: 000035 call     Console:WriteLine(String)
IN000a: 00003A mov      rcx, 0x257900030B8
IN000b: 000044 mov      rcx, gword ptr [rcx]
IN000c: 000047 call     Console:WriteLine(String)
IN000d: 00004C mov      rcx, 0x257900030C0
IN000e: 000056 mov      rcx, gword ptr [rcx]
IN000f: 000059 call     Console:WriteLine(String)
IN0010: 00005E mov      rcx, 0x257900030C8
IN0011: 000068 mov      rcx, gword ptr [rcx]
IN0012: 00006B call     Console:WriteLine(String)
IN0013: 000070 call     Console:ReadLine():String
IN0014: 000075 nop      

G_M59663_IG03:        ; offs=000076H, size=0005H, bbWeight=1    PerfScore 1.25, epilog, nogc, extend

IN0016: 000076 add      rsp, 40
IN0017: 00007A ret


Как видно — в данном простом случае — JIT справился более чем отлично. Понятно, что это не совсем универсально (в случае если JIT откажется инлайнить — то будет уже совсем не всё так красиво ).

PS: Я имел ввиду, что с точки зрения производительности — на сегодня это один из самых простых и многообещающих способов. С другой стороны рефлексия + кеширование имеет стабильно предсказуемый результат в отличии от закидонов JIT.