Здравствуйте, Sinix, Вы писали:
S>Ну вот с кейса и надо было начинать S>Можешь детали подкинуть?
Напишу ребятам, узнаю подробности.
Насколько я могу судить, смысл в том, что из такого кода:
public static class Sample<T>
{
public readonly Lazy<int> Ten = new Lazy<int>(() => 10);
}
генерируется вот это:
public static class Sample<T>
{
[Serializable]
[CompilerGenerated]
private sealed class <>c
{
public static readonly <>c <>9 = new <>c();
public static Func<int> <>9__0_0;
internal int <.cctor><>b__0_0()
{
return 10;
}
}
public readonly Lazy<int> Ten = new Lazy<int>(<>c.<>9__0_0 ?? (<>c.<>9__0_0 = new Func<int>(<>c.<>9.<.cctor>b__0_0)));
И вот, в этом случае, хоть после обращения к свойству инициализации Ten.Value класс Lazy обнуляет у себя ссылку на делегат, ссылка на делегат все равно продолжает висеть в кеширующем поле.
Кстати, глянул IL, похоже сейчас рослин более смышленный, чем ранее. Для классов Fn<T>/Operators<T> шарп генерирует создание делегатов без кеширования (Студия VS2017 15.9 Preview 3).
S>Подумаю вечером.
Я для класса Operators уже внес изменения, описал в первом сообщении. Для каждого свойства завел отдельный внутренний класс, и при обращении к конкретному свойству, обращение делегируется в этот внутренний класс, остальные свойства не затрагиваются.
S>Поле наружу не выставляется, так что без разницы.
Чтобы избежать лишних аллокации надо бы заменить в классе Fn<T> readonly поля на свойства, но сложность в обратной бинарной совместимости.
PS. Смысл в том, чтобы не инициализировать все поля за раз, так как ситуация, когда все свойства класса Operators<T> используются, исчезающе мала. Поэтому вместо того, чтобы инициализировать все и сразу, делаем лениво. Как то так:
Было:
private static readonly Lazy<Func<T, T>> _unaryMinus =
new Lazy<Func<T, T>>(() => UnaryOperator<T>(ExpressionType.Negate), _lazyMode);
public static Func<T, T> UnaryMinus => _unaryMinus.Value;
...
И так все 25 свойств
Стало:
public static Func<T, T> UnaryMinus => UnaryMinusHelper.LazyValue.Value;
private static class UnaryMinusHelper
{
public static readonly Lazy<Func<T, T>> LazyValue = new Lazy<Func<T, T>>(Factory, _lazyMode);
private static Func<T, T> Factory() => UnaryOperator<T>(ExpressionType.Negate);
}
В этом случае, инициализированы будут только используемые свойства.
Так вот, такой трюк не проходит с классом Fn<T>, так как используются поля вместо свойств. Если совместимостью в этом случае можно пожертвовать, я поменяю поля на свойства, если нет — для класса Fn<T> верну как было.
Здравствуйте, rameel, Вы писали:
R>Чтобы избежать лишних аллокации надо бы заменить в классе Fn<T> readonly поля на свойства, но сложность в обратной бинарной совместимости.
Кмк, не имеет смысла. Мы сравниваем создание 1 Lazy vs создание ~25 Lazy при старте приложения. Я не думаю, что вызов нескольких конструкторов сильно влияет на перфоманс.
С точки зрения GC наоборот выгодно создавать долгоживущие объекты как можно раньше, чтобы не возиться при очистке gen0. Но это опять-таки копейки.
Бинарная совместимость не ломается, т.к. public api не меняется. Если кто лезет рефлексией — его проблемы.
Здравствуйте, Sinix, Вы писали:
S>Кмк, не имеет смысла. Мы сравниваем создание 1 Lazy vs создание ~25 Lazy при старте приложения. Я не думаю, что вызов нескольких конструкторов сильно влияет на перфоманс.
Тут борьба больше не за тики, а за байты)
Да, только класс у нас generic, поэтому все это для каждого из используемых типов.
Нам коллеги репорт отписали, у них очень активно используются Fn<T>.SelfValue для разных типов и что-то из класса Operators, но лишнего плодится много, так вот в борьбе за байты, просили с оптимизировать этот момент, в точности, заменить на Method Group параметр lazy конструктора, так как Lazy за собой подчищает, но если пользоваться () => ..., то шарп кеширует в статической переменной до конца работы приложения, хотя по сути нужен всего раз. И того, у нас получается память выделилась на не используемые поля, на кеширование делегатов и так для каждого из задействованных типов.
ЗЫ. В нашем проекте разницы особо нет, так на уровне погрешности, но подумалось, если есть возможность немного урезать аппетит для случаев, когда используется для большого количества типов, то почему бы и нет, да и для GC работы в итоге меньше.
S>Бинарная совместимость не ломается, т.к. public api не меняется. Если кто лезет рефлексией — его проблемы.
Вроде же разный байткод используется для обращения к полю и свойству, нет?
Здравствуйте, rameel, Вы писали:
R>Тут борьба больше не за тики, а за байты)
А есть какой-то пруфтест, который подтверждает эффект?
R>Нам коллеги репорт отписали, у них очень активно используются Fn<T>.SelfValue для разных типов и что-то из класса Operators, но лишнего плодится много, так вот в борьбе за байты, просили с оптимизировать этот момент, в точности, заменить на Method Group параметр lazy конструктора, так как Lazy за собой подчищает, но если пользоваться () => ..., то шарп кеширует в статической переменной до конца работы приложения, хотя по сути нужен всего раз. И того, у нас получается память выделилась на не используемые поля, на кеширование делегатов и так для каждого из задействованных типов
Ну вот с кейса и надо было начинать
Можешь детали подкинуть?
Подумаю вечером.
R>Вроде же разный байткод используется для обращения к полю и свойству, нет?
Поле наружу не выставляется, так что без разницы.
Здравствуйте, rameel, Вы писали:
R>Напишу ребятам, узнаю подробности.
Да, давай. Я не против правок, главное чтоб от них измеримый эффект был.
R>И вот, в этом случае, хоть после обращения к свойству инициализации Ten.Value класс Lazy обнуляет у себя ссылку на делегат, ссылка на делегат все равно продолжает висеть в кеширующем поле.
Сейчас не так.
R>Кстати, глянул IL, похоже сейчас рослин более смышленный, чем ранее. Для классов Fn<T>/Operators<T> шарп генерирует создание делегатов без кеширования (Студия VS2017 15.9 Preview 3).
Давно так. С 7 емнип.
R>В классе Fn<T> у нас поля публичные.
Да, тут мы ССЗБ
Здравствуйте, rameel, Вы писали:
R>Чтобы избежать лишних аллокации надо бы заменить в классе Fn<T> readonly поля на свойства, но сложность в обратной бинарной совместимости.
ИМХО смысла в бинарной совместимости не особо много.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>