Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
IB> public static void Do<TClass>(TClass @object) where TClass : IBase
IB> {
IB> @object.Do();
IB> }
IB> public interface IBase
IB> {
IB> void Do();
IB> }
IB> public interface IDerived1 : IBase
IB> {
IB> new void Do();
IB> }
IB> public interface IDerived2 : IBase
IB> {
IB> new void Do();
IB> }
IB>}
IB>
У тебя 3 совсем разных метода Do, никак не связанных между собой. В ограничении генерика ты задаешь, что у переданного TClass должен быть метод IBase.Do. Вот его компилятор и использует.
Средствами компилятора, боюсь, сделать, то что ты хочешь, никак не получится. А через рефлекшен — можно, именно как было написано выше:
public static void Do<TClass>(TClass @object) where TClass : IBase
{
typeof(TClass).GetMethod("Do")?.Invoke(@object, null);
//@object.Do();
}
Тут происходит поиск метода именно у переданного класса (IDerived1, IDerived2), и уже этот метод вызывается у объекта.
Всем спасибо, c рефлешеном, понятно, что можно всё, но производительность страдает,
пока сделал так
using System;
namespace ConsoleApp17
{
class Program
{
static void Main(string[] args)
{
Class @object = new Class();
IBase @base = @object;
IDerived1 derived1 = @object;
IDerived2 derived2 = @object;
@base.Do();
derived1.Do();
derived2.Do();
Do(@base);
Do(derived1);
Do(derived2);
Console.ReadLine();
}
public static void Do<TClass>(TClass @object)
where TClass : IBase
{
if (typeof(TClass) == typeof(IDerived1))
((IDerived1) @object).Do();
else if (typeof(TClass) == typeof(IDerived2))
((IDerived2) @object).Do();
else
((IBase) @object).Do();
}
}
public class Class : IDerived1, IDerived2
{
#region Implementation of IBase
void IBase.Do()
{
Console.WriteLine("IBase");
}
void IDerived1.Do()
{
Console.WriteLine("IDerived1");
}
void IDerived2.Do()
{
Console.WriteLine("IDerived2");
}
#endregion
}
public interface IBase
{
void Do();
}
public interface IDerived1 : IBase
{
new void Do();
}
public interface IDerived2 : IBase
{
new void Do();
}
}
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Re[2]: Явная реализация интерфейса и generic метод
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Re[3]: Явная реализация интерфейса и generic метод
Здравствуйте, igor-booch, Вы писали:
IB>Всем спасибо, c рефлешеном, понятно, что можно всё, но производительность страдает,
Можно сделать так:
public static class Doer
{
private static Dictionary<Type, MethodInfo> _methodCache = new Dictionary<Type, MethodInfo>();
public static void Do<I>(I target) where I : IBase
{
MethodInfo m;
if (!_methodCache.TryGetValue(typeof(I), out m))
{
m = typeof(I).GetMethod("Do");
_methodCache[typeof(I)] = m;
}
m.Invoke(target, null);
}
}
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, igor-booch, Вы писали: S>Может, вам dynamic попробовать?
Тут случай, когда класс реализует более одного интерфейса, по которым нужна диспетчеризация. Динамик вынесет с заклом RuntimeBinderException.
Начал прикручивать через ad-hoc и обнаружил такое...
Здравствуйте, 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):
Как видно — в данном простом случае — JIT справился более чем отлично. Понятно, что это не совсем универсально (в случае если JIT откажется инлайнить — то будет уже совсем не всё так красиво ).
PS: Я имел ввиду, что с точки зрения производительности — на сегодня это один из самых простых и многообещающих способов. С другой стороны рефлексия + кеширование имеет стабильно предсказуемый результат в отличии от закидонов JIT.
MA>В частности с хинтом AggressiveInlining на Do и AggressiveOptimization на Main (не уверен что они нужны), метод Main примет форму (испытано на .net 5)
Offtop
У меня есть много методов, которые я хотел бы заинлайнить для повышения производительности.
Можете ли Вы посоветовать ставить для них атрибуты AggressiveInlining и AggressiveOptimization?
Или пользоваться этими атрибутами нужно осторожно, со знанием всех тонкостей?
Что кроме атрибутов AggressiveInlining и AggressiveOptimization нужно для инлайнинга методов?
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Здравствуйте, igor-booch, Вы писали:
IB>Offtop IB>У меня есть много методов, которые я хотел бы заинлайнить для повышения производительности. IB>Можете ли Вы посоветовать ставить для них атрибуты AggressiveInlining и AggressiveOptimization? IB>Или пользоваться этими атрибутами нужно осторожно, со знанием всех тонкостей? IB>Что кроме атрибутов AggressiveInlining и AggressiveOptimization нужно для инлайнинга методов?
Можно на ты. В целом это зависит от рантайма. Тут прежде всего важно — что это просто хинты — и рантайм в праве не следовать им, если не считает нужным.
Простейшие случаи можно проверить на sharplab.io. Случаи посложнее — если иниересует .net core — прийдется сбилдить рантайм (это просто). Инструкция Viewing jit dumps. Единственно, что там инструкции не очень точны. Проще всего собрать только отладочную версию и заменять только clrjit.dll. При этом важно, что бы собранная версия соответствовала рантайму который используется при публикации. С тэгами они ессно в репозитории все напутали, таким образом я не нашел тэга для 3.1 а взял соотв. тэг от проинсталлированного 5.0-preview6 (ну или какойтам инсталлируется). Если версии ре совпадают сильно — оно просто крэшнется и все. При чем именно их способ по инструкции у мкня вообще никак не заработал. Второе — уделить внимание переменным COMPlus_*. Так как оно работало раньше — у меня не работает, нашел новые опции — писать вывод в отдельный файл и все получилось. Сейчас не у компа, не знаю точнее. Позже могу уточнить.
Ну а после этого можно получать много информации. Там в частности есть и про инлайнинг опция — оно приблизительно сообщает почему инлайнит или почему нет. (Опять же tiered compilation — враг инлайнинга, — в том смысле что для таких целей проще отклють, чем ждать пока случится tier 1). Возможно есть и иные способы, я не знаю...
Что касается о расстановке атрибутов — но в 80% случаев они не нужны.
Однако, если мы говорим о нормальном фреймворке — 4.х — то там я перестал следить. 4.5 был печальным. А 4.8 — я честно говоря х.з. Сейчас в современных часто трюки с выносом throw в отдельные методы могут и не понадобится.
Иначе говоря: старайтесь не использовать эти атрибуты вообще. В реальном коде, они чаще всего никакого значения не имеют. Там где это важно — нужно проверять как JIT отработает с ним или без. Есть ситуации когда мы точно знаем что надо инлайнить — но, часто это обычные форварды вызовов где JIT и сам могуч. Поэтому по сути — рекомендаций, к сожалению не дам. Это экспериментальная часть.
К слову: у меня в коде применение BinaryPrimitives+Span дало 30% выигрыша. Но! Эта кухня в дебаге потеряла в скорости в 20х раз.
Здравствуйте, Mystic Artifact, Вы писали:
MA> К слову: у меня в коде применение BinaryPrimitives+Span дало 30% выигрыша. Но! Эта кухня в дебаге потеряла в скорости в 20х раз.
Т.е. так конкретно тормозит отладчик, или просто стало очень сложно отлаживать?
Здравствуйте, Михаил Романов, Вы писали:
MA>> К слову: у меня в коде применение BinaryPrimitives+Span дало 30% выигрыша. Но! Эта кухня в дебаге потеряла в скорости в 20х раз. МР>Т.е. так конкретно тормозит отладчик, или просто стало очень сложно отлаживать?
Нет, просто оригинальная конструкция была приблизительно такая (псевдо код):
По сути, эта конструкция, что в дебаге, что в релизе компилируется в одно и тоже.
Заменив на BinaryPrimitives + Span я считай заменил простой код на 2 вызова метода (конструктор Span + сама "конвертация"), и в отсутствии оптимизаций оно таким и остается и буксует.
Здравствуйте, Mystic Artifact, Вы писали:
MA> Заменив на BinaryPrimitives + Span я считай заменил простой код на 2 вызова метода (конструктор Span + сама "конвертация"), и в отсутствии оптимизаций оно таким и остается и буксует.
Эмм, а тут случайно не напрашивается
MemoryMarshal.Cast<byte, uint>(_data)
?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Напрашивается. Там напрашивается вообще сразу выделять int[], но есть пара int16 полей. Я думал об этом, да руки не дошли, и местами удобнее/привычнее работать с массивом байт... А все эти причудливые трансформации реально JIT сильно упрощает, так что выигрыша можно и не получить. Надо будет поковырять.