Стоит ли увлекаться лямбдами?
От: Neco  
Дата: 12.10.10 19:23
Оценка:
В последнее время всё чаще строю многоэтажные конструкции типа:
            xml.Div(new { @class = "editor-area" }, () => {
                if (!HideBackButton()) {
                    xml.PTag(() => {
                        xml.ImageActionRef(Page.Content.ImgUrl(IMG_PREVIOUS_BUTTON), "To the List", ActionNameForBackButton(), "Back to the List", ParentListRoutes());
                    });
                }

                xml.FieldSet((fieldset) => {
                    fieldset.Legend(LegendText());

                    if (!string.IsNullOrEmpty(model.ErrorMessage)) {
                        xml.Div(new { @class = "error-message" }, () => {
                            xml.WriteString(model.ErrorMessage);
                        });
                    }
                    if (model.Exception != null) {
                        xml.Div(new { @class = "error-exception" }, () => {
                            xml.PrintException(model.Exception);
                        });
                    }

                    xml.ComplexFieldHidden(GetLambdaFroSerializedObject(m => m.SerializedOriginalModel), model.SerializedOriginalModel);

                    if (fields != null) {
                        foreach (var one_field in fields) {
                            one_field.Render(xml);
                        }
                    } else if (customFields != null) {
                        customFields(xml);
                    } else {
                        throw new InvalidOperationException("Do not have any field for render!");
                    }

                    if (! this.HideSubmitButton())
                    {
                        xml.SubmitButton("Submit");
                    }
                });
            });

помеченные жирным конструкции могут вкладываться друг в друга весьма глубоко, если не в одном методе, то в их последовательном вызове. Вовсю используются замыкания.
в данном случае, это как бы Presentation, но планирую использовать такой же подход на уровне контроллеров и вообще по жизни.
Так вопрос, чем это может грозить? Потерей производительности, например?
Слышал что-то, что бинарники бУхнут от таких конструкций (из-за количества анонимных классов), но меня это мало беспокоит. Или должно беспокоить, как считаете?
всю ночь не ем, весь день не сплю — устаю
Re: Стоит ли увлекаться лямбдами?
От: Lloyd Россия  
Дата: 12.10.10 19:27
Оценка: 1 (1) +5
Здравствуйте, Neco, Вы писали:

N>в данном случае, это как бы Presentation, но планирую использовать такой же подход на уровне контроллеров и вообще по жизни.

N>Так вопрос, чем это может грозить? Потерей производительности, например?
N>Слышал что-то, что бинарники бУхнут от таких конструкций (из-за количества анонимных классов), но меня это мало беспокоит. Или должно беспокоить, как считаете?

Я бы беспокоился больше по поводу читабельности. А она, по-моему, в приведенном примере хромает на обе ноги.
Re[2]: Стоит ли увлекаться лямбдами?
От: Neco  
Дата: 12.10.10 23:36
Оценка:
Здравствуйте, Lloyd, Вы писали:

L>Я бы беспокоился больше по поводу читабельности. А она, по-моему, в приведенном примере хромает на обе ноги.

Хех-хех, да это есть ))
Но пока идей, как это улучшить, нет. Может у Вас есть? Буду признателен.
всю ночь не ем, весь день не сплю — устаю
Re[3]: Стоит ли увлекаться лямбдами?
От: MozgC США http://nightcoder.livejournal.com
Дата: 12.10.10 23:45
Оценка:
Здравствуйте, Neco, Вы писали:

N>Но пока идей, как это улучшить, нет. Может у Вас есть? Буду признателен.


— Не использовать многоэтажные лямбда-конструкции?
Re[3]: Стоит ли увлекаться лямбдами?
От: Neco  
Дата: 12.10.10 23:53
Оценка:
Здравствуйте, Neco, Вы писали:

N>Здравствуйте, Lloyd, Вы писали:


L>>Я бы беспокоился больше по поводу читабельности. А она, по-моему, в приведенном примере хромает на обе ноги.

N>Хех-хех, да это есть ))
N>Но пока идей, как это улучшить, нет. Может у Вас есть? Буду признателен.
Кстати, раз уж я спрашиваю про идею, то пожалуй скажу для чего такая конструкция нужна (чтобы можно было исходя из требования что-то предлагать, а не из кода). По сути нужно иметь возможность повторно использовать элементы дизайна. В моём случае это Asp.Net MVC, хотя от оригинала уже мало что осталось. Проблема зародилась в том, что при объединении нескольких mvc-контролов в один более сложный (например, кнопка с текстом — т.е. по сути гиперлинк с картинкой и текстом внутри), предлагалось либо не париться и дублировать, либо писать своё расширение, код которого в общем-то, будет состоять из бубнов с TagBuilder'ом. И сделать такой код повторно используемым я не понял как. Постепенно пришёл к тому, что всякое расширение любого html элемента, который может в себе что-то содержать, должно принимать параметр-лямбду, типа subHtml. Если сообщать вместо лямбды string, то визуально нарушается ход событий (т.е. сначала готовим начинку, а потом её используем). Таким образом такая конструкция и случилась.
всю ночь не ем, весь день не сплю — устаю
Re[4]: Стоит ли увлекаться лямбдами?
От: Neco  
Дата: 12.10.10 23:58
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>- Не использовать многоэтажные лямбда-конструкции?

Это логично, но они неспроста появились — т.е. они решают вполне конкретную задачу.
В общем, я Lloyd'у написал подробное объяснение.
всю ночь не ем, весь день не сплю — устаю
Re[3]: Стоит ли увлекаться лямбдами?
От: Lloyd Россия  
Дата: 13.10.10 00:13
Оценка:
Здравствуйте, Neco, Вы писали:

L>>Я бы беспокоился больше по поводу читабельности. А она, по-моему, в приведенном примере хромает на обе ноги.

N>Хех-хех, да это есть ))
N>Но пока идей, как это улучшить, нет. Может у Вас есть? Буду признателен.

Если честно, я даже не понял что там написано.
Re: Стоит ли увлекаться лямбдами?
От: _FRED_ Черногория
Дата: 13.10.10 03:00
Оценка: 8 (2) +1
Здравствуйте, Neco, Вы писали:

N>В последнее время всё чаще строю многоэтажные конструкции типа:


ИМХО, ничего страшного нет.

N>            xml.Div(new { @class = "editor-area" }, () => {
N>                if (!HideBackButton()) {
N>                    xml.PTag(() => {
                          // сделать разве что "поуже", не так широко,
                          // перенеся длинные строки на несколько строк.
N>                        xml.ImageActionRef(Page.Content.ImgUrl(IMG_PREVIOUS_BUTTON), 
                              "To the List", ActionNameForBackButton(), 
                              "Back to the List", ParentListRoutes());
N>                    });
N>                }

                  // убрал бы скобки вокруг одного параметра лямбды
N>                //xml.FieldSet((fieldset) => {
                  xml.FieldSet(fieldset => {
N>                    fieldset.Legend(LegendText());

N>                    if (!string.IsNullOrEmpty(model.ErrorMessage)) {
N>                        xml.Div(new { @class = "error-message" }, 
                              // А тут убрал бы не обязательные скобки.
                              () => xml.WriteString(model.ErrorMessage););
N>                    }
N>                    if (model.Exception != null) {
N>                        xml.Div(new { @class = "error-exception" },
                              // И тут.
                              () => xml.PrintException(model.Exception););
N>                    }

N>                    xml.ComplexFieldHidden(
                          GetLambdaFroSerializedObject(m => m.SerializedOriginalModel),
                          model.SerializedOriginalModel);

N>                    if (fields != null) {
N>                        foreach (var one_field in fields) {
N>                            one_field.Render(xml);
N>                        }
N>                    } else if (customFields != null) {
N>                        customFields(xml);
N>                    } else {
N>                        throw new InvalidOperationException("Do not have any field for render!");
N>                    }

                      // Этот абзац сильвы выбивается из общего стиля:
                      // скобки не так расставлены, "this." …
N>                    if (!HideSubmitButton()) {
N>                        xml.SubmitButton("Submit");
N>                    }
N>                });
N>            });


Так же (но это уже не совсем к читабельности, хотя может и к ней) смущает "new { @class = "editor-area" }" — набор атрибутов не на столько большой, что бы сложно было бы описать один единственный раз его и пользщоваться далее уже типизированными аналогами.

N>помеченные жирным конструкции могут вкладываться друг в друга весьма глубоко, если не в одном методе, то в их последовательном вызове. Вовсю используются замыкания.

N>в данном случае, это как бы Presentation, но планирую использовать такой же подход на уровне контроллеров и вообще по жизни.
N>Так вопрос, чем это может грозить? Потерей производительности, например?

Ну это вряд ли.

N>Слышал что-то, что бинарники бУхнут от таких конструкций (из-за количества анонимных классов), но меня это мало беспокоит. Или должно беспокоить, как считаете?


Я бы не беспокоился.
Help will always be given at Hogwarts to those who ask for it.
Re: Стоит ли увлекаться лямбдами?
От: GlebZ Россия  
Дата: 13.10.10 06:36
Оценка: +4
Здравствуйте, Neco, Вы писали:

N>в данном случае, это как бы Presentation, но планирую использовать такой же подход на уровне контроллеров и вообще по жизни.

У лямбд есть существенный недостаток. Это неимение имени. Имя — это ясный абстрактный коментарий того, что данный код делает. Неимение имени существенно снижает читабельность а следовательно сопровождаемость кода. Поэтому для сложных конструкций, где с одного взгляда не поймешь что делегат делает, именованные функции рулят.


N>Так вопрос, чем это может грозить? Потерей производительности, например?

N>Слышал что-то, что бинарники бУхнут от таких конструкций (из-за количества анонимных классов), но меня это мало беспокоит. Или должно беспокоить, как считаете?
Не должно, ежели это не битовыжимательство.
Re: Стоит ли увлекаться лямбдами?
От: DuШes  
Дата: 13.10.10 07:21
Оценка:
Здравствуйте, Neco, Вы писали:
[...]

рекомендую посмотреть пост Andir в свое блоге Цикл как самый выразительный способ перебора элементов...это что касается читабельности, а в остальном полностью соглашусь с GlebZ
Re: Стоит ли увлекаться лямбдами?
От: master_of_shadows Беларусь  
Дата: 13.10.10 10:25
Оценка: +1
Здравствуйте, Neco, Вы писали:

N>Так вопрос, чем это может грозить? Потерей производительности, например?


Мне в лямбдах не нравится стектрейс при эксепшене. Неприятно его разгребать .
Re: Стоит ли увлекаться лямбдами?
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 13.10.10 12:14
Оценка: +1
Здравствуйте, Neco, Вы писали:

N>Слышал что-то, что бинарники бУхнут от таких конструкций (из-за количества анонимных классов), но меня это мало беспокоит. Или должно беспокоить, как считаете?


По поводу лямбд стоит беспокоиться в Performance-critical местах, поскольку они ОЧЕНЬ небесплатны по производительности
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[2]: Стоит ли увлекаться лямбдами?
От: _FRED_ Черногория
Дата: 13.10.10 12:41
Оценка:
Здравствуйте, xvost, Вы писали:

N>>Слышал что-то, что бинарники бУхнут от таких конструкций (из-за количества анонимных классов), но меня это мало беспокоит. Или должно беспокоить, как считаете?


X>По поводу лямбд стоит беспокоиться в Performance-critical местах, поскольку они ОЧЕНЬ небесплатны по производительности


По сравнению с вызовом именованного метода?
Help will always be given at Hogwarts to those who ask for it.
Re[3]: Стоит ли увлекаться лямбдами?
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 13.10.10 12:50
Оценка: 8 (1)
Здравствуйте, _FRED_, Вы писали:

X>>По поводу лямбд стоит беспокоиться в Performance-critical местах, поскольку они ОЧЕНЬ небесплатны по производительности

_FR>По сравнению с вызовом именованного метода?

Во-первых да.
Во-вторых там не только вызов, а еще
2а) Создание объекта-замыкания
2б) Создание делегата над методом класса замыкания

Все это во-первых весьма и весьма жрет ЦПУ (учитывая подводные и широко неизвестные факты в EE про создания лямбд над методом класса с дженериками)
А во-вторых объекты замыкания, если их много, весьма основательно мусорят, что приводит к mid-life crisis'у
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[4]: Стоит ли увлекаться лямбдами?
От: _FRED_ Черногория
Дата: 13.10.10 13:13
Оценка: 1 (1) +2
Здравствуйте, xvost, Вы писали:

X>>>По поводу лямбд стоит беспокоиться в Performance-critical местах, поскольку они ОЧЕНЬ небесплатны по производительности

_FR>>По сравнению с вызовом именованного метода?

X>Во-первых да.

X>Во-вторых там не только вызов, а еще
X> 2а) Создание объекта-замыкания

Я так понимаю, что вряд-ли "Создание объекта-замыкания" отличается от просто создания (и инициализации) некоторого пользовательского объекта с такими же полями?

Конечно, считая относительно медленным создание любого объекта, можно придти к тому, что лямбды тормозят. Надеюсь, такие подсчёты не станут обычными для большинства прогоаммистов

Или создание именно "объекта-замыкания" обычно медленнее, чем создание пользовательского объекта?

X> 2б) Создание делегата над методом класса замыкания


Делегат тоже создаётся заметно медленнее чем обычный пользовательский объект?

X>Все это во-первых весьма и весьма жрет ЦПУ (учитывая подводные и широко неизвестные факты в EE про создания лямбд над методом класса с дженериками)


Разве EE знает, с объектом лямбды она дело имеет просто с пользовательским объектом/методом и не знает, что когда-то обыло лямбдой? Мне казалось, что "лямбда" — лишь понятия компилятора, а EE уже дела нет, что это за объект. Это не так? Другими словами, чем отличается поведение ЕЕ "про создания лямбд над методом класса с дженериками" от "про создания делегатов над методом класса с дженериками"?

X>А во-вторых объекты замыкания, если их много, весьма основательно мусорят, что приводит к mid-life crisis'у


А что можно назвать "mid-life crisis"-ом применительно к данному вопросу?
Help will always be given at Hogwarts to those who ask for it.
Re[5]: Стоит ли увлекаться лямбдами?
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 13.10.10 13:17
Оценка: 49 (2)
Здравствуйте, _FRED_, Вы писали:

_FR>Я так понимаю, что вряд-ли "Создание объекта-замыкания" отличается от просто создания (и инициализации) некоторого пользовательского объекта с такими же полями?


правильно.

X>> 2б) Создание делегата над методом класса замыкания

_FR>Делегат тоже создаётся заметно медленнее чем обычный пользовательский объект?

Да. Создание делегата — во много раз более дорогое удовольствие чем создание простого объекта

_FR>Разве EE знает, с объектом лямбды она дело имеет просто с пользовательским объектом/методом и не знает, что когда-то обыло лямбдой? Мне казалось, что "лямбда" — лишь понятия компилятора, а EE уже дела нет, что это за объект. Это не так? Другими словами, чем отличается поведение ЕЕ "про создания лямбд над методом класса с дженериками" от "про создания делегатов над методом класса с дженериками"?


Еще раз. Никаких лямбд в EE нет. Там есть только делегаты. А их создание — очень и очень дорогое удовольствие. К тому же (при определенных условиях) блокирующее, что немедленно сказывается на многопоточных приложениях

X>>А во-вторых объекты замыкания, если их много, весьма основательно мусорят, что приводит к mid-life crisis'у

_FR>А что можно назвать "mid-life crisis"-ом применительно к данному вопросу?

Пользовательская логика. Ее объекты передут в Gen1 тогда как могли бы умереть в Gen0
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[6]: Стоит ли увлекаться лямбдами?
От: _FRED_ Черногория
Дата: 13.10.10 13:22
Оценка:
Здравствуйте, xvost, Вы писали:

X>>> 2б) Создание делегата над методом класса замыкания

_FR>>Делегат тоже создаётся заметно медленнее чем обычный пользовательский объект?

X>Да. Создание делегата — во много раз более дорогое удовольствие чем создание простого объекта


ОК, это объясняет назойливое кэширование, когда это возможно, делегатов.

_FR>>Разве EE знает, с объектом лямбды она дело имеет просто с пользовательским объектом/методом и не знает, что когда-то обыло лямбдой? Мне казалось, что "лямбда" — лишь понятия компилятора, а EE уже дела нет, что это за объект. Это не так? Другими словами, чем отличается поведение ЕЕ "про создания лямбд над методом класса с дженериками" от "про создания делегатов над методом класса с дженериками"?


X>Еще раз. Никаких лямбд в EE нет. Там есть только делегаты. А их создание — очень и очень дорогое удовольствие.


ОК, я всего лишь пытался понять ваши же слова:

…(учитывая подводные и широко неизвестные факты в EE про создания лямбд над методом класса с дженериками)



X>К тому же (при определенных условиях) блокирующее, что немедленно сказывается на многопоточных приложениях


Спасибо, будет что посмотреть.

X>>>А во-вторых объекты замыкания, если их много, весьма основательно мусорят, что приводит к mid-life crisis'у

_FR>>А что можно назвать "mid-life crisis"-ом применительно к данному вопросу?
X>Пользовательская логика. Ее объекты передут в Gen1 тогда как могли бы умереть в Gen0

О как интересно: и термин здоровский, и не бросается так сразу в глаза.
Help will always be given at Hogwarts to those who ask for it.
Re[6]: Стоит ли увлекаться лямбдами?
От: _FRED_ Черногория
Дата: 13.10.10 13:28
Оценка: 6 (1)
Здравствуйте, xvost, Вы писали:

X>>> 2б) Создание делегата над методом класса замыкания

_FR>>Делегат тоже создаётся заметно медленнее чем обычный пользовательский объект?

Кстати, получается, если в примере топикстартера, например, мы заменим все лямбды на обычные, именованные методы или именованные методы во вспомагательных классах, то (поскольку и создание вспомагательных классов и создание делегатов останется) прироста производительности всё-таки не получится?

То есть, да: использование API, основанного на вызове методов через делегаты (linq/Rx и многое уже что появилось) уже чревато (из-за дороговизны создания делегата) но использование этого API посредстром анонимных методов или лямбд с точки зрения производительности всё-тки не хуже использования этого же API посредством именованных методов?

Надеюсь, не слишком завернул
Help will always be given at Hogwarts to those who ask for it.
Re[7]: Стоит ли увлекаться лямбдами?
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 13.10.10 13:52
Оценка: 26 (1) +4
Здравствуйте, _FRED_, Вы писали:

_FR>но использование этого API посредстром анонимных методов или лямбд с точки зрения производительности всё-тки не хуже использования этого же API посредством именованных методов?


В общем, да. Разницу в микроскоп будет не заметить.

И все приведенные мною выше слова относятся действительно к performance-critical коду, который вызывается десятки тысяч раз в секунду. Там использование лямбд, linq и прочей современной радости _строго_ противопоказано.

Во всех остальных случаях, как правило, удобство чтения, восприятия и модификации кода перевешивает перфоманс-деградацию
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[7]: Стоит ли увлекаться лямбдами?
От: Jolly Roger  
Дата: 13.10.10 14:02
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>То есть, да: использование API, основанного на вызове методов через делегаты (linq/Rx и многое уже что появилось) уже чревато (из-за дороговизны создания делегата) но использование этого API посредстром анонимных методов или лямбд с точки зрения производительности всё-тки не хуже использования этого же API посредством именованных методов?


В NET альтернатива делегату, по-моему, только интерфейс, то есть выбор не особо велик, а созданный делегат, в том числе и на базе анонима, можно "кэшировать". Кстати, кто-нибудь интересовался, при использовании лямбд кэширование выполняется?
"Нормальные герои всегда идут в обход!"
Re[6]: Стоит ли увлекаться лямбдами?
От: Пельмешко Россия blog
Дата: 13.10.10 14:16
Оценка:
Здравствуйте, xvost, Вы писали:

X>К тому же (при определенных условиях) блокирующее, что немедленно сказывается на многопоточных приложениях


Евгений, а не поясните, блокирующее что конкретно?
При создание делегата какие-нибудь стабы методов генерируются и в глобальных синхронизованных таблицах хранятся или что-то подобное?

p.s. А вот функциональные значения в F# лишены проблем производительности создания экземпляров, так как являются просто экземплярами обычных классов, да и вызываются чуть быстрее (самый обычный виртуальный вызов, так же как в Nemerle, если мне не изменяет память). Вот только они не бывают никаких других сигнатур кроме как 'a -> 'b, поэтому придётся либо время тратить и нагружать GC, складывая аргументы в тупль, либо использовать каррированную форму, но тогда все вызовы становятся медленнее на один downcast (надо бы сравнить с вызовом делегата)
Re[8]: Стоит ли увлекаться лямбдами?
От: Пельмешко Россия blog
Дата: 13.10.10 14:22
Оценка: 12 (1) +1
Здравствуйте, Jolly Roger, Вы писали:

JR>Здравствуйте, _FRED_, Вы писали:


_FR>>То есть, да: использование API, основанного на вызове методов через делегаты (linq/Rx и многое уже что появилось) уже чревато (из-за дороговизны создания делегата) но использование этого API посредстром анонимных методов или лямбд с точки зрения производительности всё-тки не хуже использования этого же API посредством именованных методов?


JR>В NET альтернатива делегату, по-моему, только интерфейс, то есть выбор не особо велик, а созданный делегат, в том числе и на базе анонима, можно "кэшировать". Кстати, кто-нибудь интересовался, при использовании лямбд кэширование выполняется?


Да, выполняется конечно, но только если лямбда имеет CLI-представление в виде статического метода, то есть если ни замыкается на локальные переменные, ни на this.
Много подобных кэшируется:
   .Select(person => person.Name)

Интерфейсный вызов тоже не айс, в дотнете он дважды косвенный (где-то здесь обсуждалось давно), самая быстрая альтернатива — обычный виртуальный вызов экземплярного метода.
Re[8]: Стоит ли увлекаться лямбдами?
От: _FRED_ Черногория
Дата: 13.10.10 14:27
Оценка: 12 (1)
Здравствуйте, Jolly Roger, Вы писали:

JR>В NET альтернатива делегату, по-моему, только интерфейс, то есть выбор не особо велик, а созданный делегат, в том числе и на базе анонима, можно "кэшировать".


Иногда безсмысленно: если анонимная функция использует замыкания, то кешированиеособо не спасёт.

JR>Кстати, кто-нибудь интересовался, при использовании лямбд кэширование выполняется?


Тогда, когда анонимную функцию можно представить в виде статического метода, делегат на неё кэшируется. Других оптимизаций по кешированию анонимных функций я не видел.
Help will always be given at Hogwarts to those who ask for it.
Re[7]: Стоит ли увлекаться лямбдами?
От: Пельмешко Россия blog
Дата: 13.10.10 14:29
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Кстати, получается, если в примере топикстартера, например, мы заменим все лямбды на обычные, именованные методы или именованные методы во вспомагательных классах, то (поскольку и создание вспомагательных классов и создание делегатов останется) прироста производительности всё-таки не получится?


Если среди лямбд есть статические (у топикстартера таких нет), то получите проигрыш.
Если, конечно, сами не реализуете кэширование создание экземпляров делегатов.
Re[8]: Стоит ли увлекаться лямбдами?
От: Пельмешко Россия blog
Дата: 13.10.10 14:36
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Если, конечно, сами не реализуете кэширование создание экземпляров делегатов.


Ой ой, вру, создание делегатов из обычных статических методов тоже кэшируются конечно же
Re[7]: Стоит ли увлекаться лямбдами?
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 13.10.10 16:27
Оценка: 67 (5)
Здравствуйте, Пельмешко, Вы писали:

П>Евгений, а не поясните, блокирующее что конкретно?

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

Я не знаю что блокируется конкретно. Однако в стектрейсах четко видно как управление уходит в EE и там видны блокировки.
Причем еще очень зависит на что создается делегат.

Дешевле всего (и это без блокировки) на обычный метод класса.
Дорого — на метод интерфейса, на метод структуры, на статический метод, и на метод класса с дженериками. Причем разница доходит до 50 раз.

Подобное поведение подтверждается как экспериментально, так и источниками из CLR Team
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[6]: Стоит ли увлекаться лямбдами?
От: Sinix  
Дата: 14.10.10 09:13
Оценка: 11 (2)
Здравствуйте, xvost, Вы писали:


_FR>>Я так понимаю, что вряд-ли "Создание объекта-замыкания" отличается от просто создания (и инициализации) некоторого пользовательского объекта с такими же полями?


X>правильно.


Не удержался и проверил. 200 млн вызовов, релиз, запуск по Ctrl-F5.
Единственный нюанс — анонимные методы действительно кэшируются (легко проверить в рефлекторе). В остальном — всё ожидаемо.
         Direct:       82,0 ms
           Data:    1 301,0 ms
    Cached Data:       81,0 ms
          IData:    1 692,0 ms
   Cached IData:      704,0 ms
         Lambda:      865,0 ms
  Cached lambda:      651,0 ms
       Delegate:    2 906,0 ms
Cached delegate:      627,0 ms


На практике, конечно же, эти копейки моментально сожрёт остальной код, стоит только усложнить реализацию Foo().
И да, для числомолотилок делегаты неэффективны

  код
  class Program
  {
    interface IData
    {
      int Foo();
    }

    class Data: IData
    {
      public int val;

      public int Foo()
      {
        return val;
      }
    }

    const int Count = 200 * 1000 * 1000;

    static int Foo()
    {
      return 0;
    }

    static void Main(string[] args)
    {
      Measure("Direct", () =>
      {
        for (int i = 0; i < Count; i++)
        {
          Foo();
        }
      });
      Measure("Data", () =>
      {
        for (int i = 0; i < Count; i++)
        {
          new Data()
          {
            val = i
          }.Foo();
        }
      });
      Measure("Cached Data", () =>
      {
        Data data = new Data()
        {
          val = 0
        };
        for (int i = 0; i < Count; i++)
        {
          data.Foo();
        }
      });
      Measure("IData", () =>
      {
        for (int i = 0; i < Count; i++)
        {
          ((IData)new Data()
          {
            val = i
          }).Foo();
        }
      });
      Measure("Cached IData", () =>
      {
        IData data = new Data()
        {
          val = 0
        };
        for (int i = 0; i < Count; i++)
        {
          data.Foo();
        }
      });
      Measure("Lambda", () =>
      {
        for (int i = 0; i < Count; i++)
        {
          Func<int> a = () => i;
          a();
        }
      });
      Measure("Cached lambda", () =>
      {
        int c = 5;
        Func<int> a = () => c;
        for (int i = 0; i < Count; i++)
        {
          a();
        }
      });
      Measure("Delegate", () =>
      {
        for (int i = 0; i < Count; i++)
        {
          Func<int> a = Foo;
          a();
        }
      });
      Measure("Cached delegate", () =>
      {
        Func<int> a = Foo;
        for (int i = 0; i < Count; i++)
        {
          a();
        }
      });
      Console.ReadKey();
    }

    static void Measure(string name, Action callback)
    {
      Stopwatch sw = Stopwatch.StartNew();
      callback();
      sw.Stop();
      Console.WriteLine("{0,15}: {1,10:#,##0.0###} ms", name, sw.ElapsedMilliseconds);
    }
  }

Re[7]: Стоит ли увлекаться лямбдами?
От: Sinix  
Дата: 14.10.10 09:46
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Евгений, а не поясните, блокирующее что конкретно?

П>При создание делегата какие-нибудь стабы методов генерируются и в глобальных синхронизованных таблицах хранятся или что-то подобное?
Кмк, это проблемы EE, а не делегатов.

П>p.s. А вот функциональные значения в F# лишены проблем производительности создания экземпляров, так как являются просто экземплярами обычных классов, да и вызываются чуть быстрее (самый обычный виртуальный вызов, так же как в Nemerle, если мне не изменяет память).

Не совсем так. В il действительно callvirt, но JITтится оно аналогично call. Иначе они по производительности совпадали бы с обычными делегатами/интерфейсами.
См http://www.rsdn.ru/forum/dotnet/3996529.1.aspx
Автор: _FRED_
Дата: 13.10.10
Re[7]: Стоит ли увлекаться лямбдами?
От: Пельмешко Россия blog
Дата: 14.10.10 10:15
Оценка: 6 (1)
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, xvost, Вы писали:



_FR>>>Я так понимаю, что вряд-ли "Создание объекта-замыкания" отличается от просто создания (и инициализации) некоторого пользовательского объекта с такими же полями?


X>>правильно.


S>Не удержался и проверил. 200 млн вызовов, релиз, запуск по Ctrl-F5.

S>Единственный нюанс — анонимные методы действительно кэшируются (легко проверить в рефлекторе). В остальном — всё ожидаемо.

Я нюанс у Вас заметил, вот этот код:
S>      Measure("Data", () =>
S>      {
S>        for (int i = 0; i < Count; i++)
S>        {
S>          new Data()
S>          {
S>            val = i
S>          }.Foo();
S>        }
S>      });

И вот этот:
S>      Measure("Lambda", () =>
S>      {
S>        for (int i = 0; i < Count; i++)
S>        {
S>          Func<int> a = () => i;
S>          a();
S>        }
S>      });

Должны быть логически равнозначны (раз хотим проверить скорость создания делегата), однако здесь компилятор C# успешно применяет кэширование экземпляра делегата, даже не смотря на замыкание! Замыкание происходит на переменную итерации, которая находится вне блока цикла, а значит все делегаты могут разделить между собой класс-замыкание, да и сам экземпляр делегата... Сделайте вот так:
for (int i = 0; i < Count; i++)
{
    int j = i;
    Func<int> a = () => j;
    a();
}
...и удивитесь как изменится результат

Да и кэширование у Вас энергичное, не равнозначное тому, что делает C#...
Re[8]: Стоит ли увлекаться лямбдами?
От: Sinix  
Дата: 14.10.10 10:19
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Должны быть логически равнозначны (раз хотим проверить скорость создания делегата), однако здесь компилятор C# успешно применяет кэширование экземпляра делегата, даже не смотря на замыкание!

Ну да, собственно это и хотел показать, где-то выше по дискуссии видел обратное утверждение
Re[8]: Стоит ли увлекаться лямбдами?
От: hardcase Пират http://nemerle.org
Дата: 14.10.10 10:24
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Не совсем так. В il действительно callvirt, но JITтится оно аналогично call. Иначе они по производительности совпадали бы с обычными делегатами/интерфейсами.

S>См http://www.rsdn.ru/forum/dotnet/3996529.1.aspx
Автор: _FRED_
Дата: 13.10.10


Инструкция callvirt нужна чтобы генерировать NullReferenceException при this равном null.
Если меня не подводит память, то C++/CLI использует call для вызова невиртуальных методов и потому их можно позвать для null.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[8]: Стоит ли увлекаться лямбдами?
От: Sinix  
Дата: 14.10.10 10:27
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Да и кэширование у Вас энергичное, не равнозначное тому, что делает C#...


Это вы про Cached lambda vs Cached delegate? Там же копейки.

Если про Cached data — то там как раз jit оптимизирует callvirt, как я вам уже писал
Re[8]: Стоит ли увлекаться лямбдами?
От: Пельмешко Россия blog
Дата: 14.10.10 10:33
Оценка:
Здравствуйте, Sinix, Вы писали:

П>>p.s. А вот функциональные значения в F# лишены проблем производительности создания экземпляров, так как являются просто экземплярами обычных классов, да и вызываются чуть быстрее (самый обычный виртуальный вызов, так же как в Nemerle, если мне не изменяет память).

S>Не совсем так. В il действительно callvirt, но JITтится оно аналогично call. Иначе они по производительности совпадали бы с обычными делегатами/интерфейсами.

Что значит "JITтится оно аналогично call"?
Почему для F# что-то "JITтится" иначе?

Для меня всегда было так (в порядке производительности):

  1. Вызов virtual-метода = косвенный вызов, самый быстрый из косвенных в CLR;
  2. Вызов метода через интерфейс = дважды косвенный вызов, чуть медленнее;
  3. Вызов делегата = косвенный вызов + некоторый оверхед [ + ещё оверхед из-за подстановки Target'а ]

В F# используются обычные вызовы виртуальных методов, через callvirt.
Re[9]: Стоит ли увлекаться лямбдами?
От: Sinix  
Дата: 14.10.10 10:34
Оценка:
Здравствуйте, hardcase, Вы писали:

H>Инструкция callvirt нужна чтобы генерировать NullReferenceException при this равном null.

Не только. Ещё и чтоб не ломать код при внезапном

    class Data2: Data
    {
      public override int Foo() // Data.Foo - virtual
      {
        return 42;
      }
    }


H>Если меня не подводит память, то C++/CLI использует call для вызова невиртуальных методов и потому их можно позвать для null.

Да.

Чуть-чуть ссылок (вы-то уже наверняка читали, но кто-нить может заинтересоваться):
http://bartdesmet.net/blogs/bart/archive/2007/02/27/c-quiz-call-versus-callvirt.aspx
http://blogs.msdn.com/b/ericlippert/archive/2007/08/17/subtleties-of-c-il-codegen.aspx
http://stackoverflow.com/questions/845657/why-is-the-c-compiler-emitting-a-callvirt-instruction-for-a-gettype-method-cal
Re[9]: Стоит ли увлекаться лямбдами?
От: Sinix  
Дата: 14.10.10 10:49
Оценка: 71 (4)
Здравствуйте, Пельмешко, Вы писали:

П>Что значит "JITтится оно аналогично call"?

П>Почему для F# что-то "JITтится" иначе?
П>В F# используются обычные вызовы виртуальных методов, через callvirt.
Это фича рантайма, а не языка.
Посмотрите ссылки тут
Автор: Sinix
Дата: 14.10.10


П>Для меня всегда было так (в порядке производительности):

  1. Вызов virtual-метода = косвенный вызов, самый быстрый из косвенных в CLR;
  2. Вызов метода через интерфейс = дважды косвенный вызов, чуть медленнее;
  3. Вызов делегата = косвенный вызов + некоторый оверхед [ + ещё оверхед из-за подстановки Target'а ]

Ну, на самом деле оно не так, но имело некоторое отношение к реальности — для 1го фреймворка
http://msdn.microsoft.com/en-us/library/ms973852.aspx (см табличку)
Ещё пруфы
http://msdn.microsoft.com/en-us/library/ff647790.aspx
http://www.sturmnet.org/blog/2005/09/01/

Сейчас — примерно так:
http://tips.x-tensive.com/2008/10/method-call-performance.html
http://stackoverflow.com/questions/216008/c-virtual-function-invocation-is-even-faster-than-a-delegate-invocation
Ещё (из кэша гугля, оригинал сдох)
http://stackoverflow.com/questions/2082735/performance-of-calling-delegates-vs-methods
Re: Стоит ли увлекаться лямбдами?
От: Воронков Василий Россия  
Дата: 15.10.10 12:56
Оценка: +1
Здравствуйте, Neco, Вы писали:

Проблем в целом с таким подходом нет. С т.з. производительности все зависит от того, что пишите. Каких-то кардинальных проблем с производительностью нет.
Если судить о приведенном коде, то — буду неоригинален. Стоит прежде всего продумать форматирование. Убирать лишние скобочки, которые затрудняют чтение. И я бы на экономил на переносах строк — в идеале хочется, чтобы тело каждой лямбды было четко оформлено. Я обычно форматирую как-то так:

Fun(x =>
    {
        Fun2(y =>
            {

            });
    });


Да, ну и хорошо при этом на забывать о том, что лямбда "повторенная дважды" как-то уже теряет свою ценность Какие-то вещи можно оформлять и в виде старых добрых неанонимных функций. Заодно и клиентский код будет выглядеть чище.
Re[10]: Стоит ли увлекаться лямбдами?
От: Воронков Василий Россия  
Дата: 15.10.10 13:05
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Сейчас — примерно так:

S>http://tips.x-tensive.com/2008/10/method-call-performance.html
S>http://stackoverflow.com/questions/216008/c-virtual-function-invocation-is-even-faster-than-a-delegate-invocation
S>Ещё (из кэша гугля, оригинал сдох)
S>http://stackoverflow.com/questions/2082735/performance-of-calling-delegates-vs-methods

Гм, вызов через интерфейс стал медленее вызова через делегат? Интересно.
Re[11]: Стоит ли увлекаться лямбдами?
От: Sinix  
Дата: 15.10.10 13:18
Оценка: +1
Здравствуйте, Воронков Василий, Вы писали:

ВВ>Гм, вызов через интерфейс стал медленее вызова через делегат? Интересно.

Скорее, вызов делегата стал быстрее вызова ч/з интерфейс
Re[2]: Стоит ли увлекаться лямбдами?
От: Аноним  
Дата: 16.10.10 09:46
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>Здравствуйте, Neco, Вы писали:


N>>в данном случае, это как бы Presentation, но планирую использовать такой же подход на уровне контроллеров и вообще по жизни.

GZ>У лямбд есть существенный недостаток. Это неимение имени. Имя — это ясный абстрактный коментарий того, что данный код делает. Неимение имени существенно снижает читабельность а следовательно сопровождаемость кода. Поэтому для сложных конструкций, где с одного взгляда не поймешь что делегат делает,
именованные функции рулят.



имеете ввиду замена на стандартный foreach?
Re[4]: Стоит ли увлекаться лямбдами?
От: Аноним  
Дата: 16.10.10 09:54
Оценка:
Здравствуйте, xvost, Вы писали:

X>Все это во-первых весьма и весьма жрет ЦПУ (учитывая подводные и широко неизвестные факты в EE про создания лямбд над методом класса с дженериками)

X>А во-вторых объекты замыкания, если их много, весьма основательно мусорят, что приводит к mid-life crisis'у

а что такое EE?
Re[5]: Стоит ли увлекаться лямбдами?
От: Пельмешко Россия blog
Дата: 16.10.10 10:45
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, xvost, Вы писали:


X>>Все это во-первых весьма и весьма жрет ЦПУ (учитывая подводные и широко неизвестные факты в EE про создания лямбд над методом класса с дженериками)

X>>А во-вторых объекты замыкания, если их много, весьма основательно мусорят, что приводит к mid-life crisis'у

А>а что такое EE?


Execution Engine
Re[3]: Стоит ли увлекаться лямбдами?
От: GlebZ Россия  
Дата: 17.10.10 18:31
Оценка:
Здравствуйте, Аноним, Вы писали:

А>имеете ввиду замена на стандартный foreach?

Они не имеют никакого отношения друг-другу.
Re[4]: Стоит ли увлекаться лямбдами?
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.10.10 17:37
Оценка:
Здравствуйте, Neco, Вы писали:

N>Кстати, раз уж я спрашиваю про идею, то пожалуй скажу для чего такая конструкция нужна (чтобы можно было исходя из требования что-то предлагать, а не из кода). По сути нужно иметь возможность повторно использовать элементы дизайна. В моём случае это Asp.Net MVC, хотя от оригинала уже мало что осталось. Проблема зародилась в том, что при объединении нескольких mvc-контролов в один более сложный (например, кнопка с текстом — т.е. по сути гиперлинк с картинкой и текстом внутри), предлагалось либо не париться и дублировать, либо писать своё расширение, код которого в общем-то, будет состоять из бубнов с TagBuilder'ом. И сделать такой код повторно используемым я не понял как. Постепенно пришёл к тому, что всякое расширение любого html элемента, который может в себе что-то содержать, должно принимать параметр-лямбду, типа subHtml. Если сообщать вместо лямбды string, то визуально нарушается ход событий (т.е. сначала готовим начинку, а потом её используем). Таким образом такая конструкция и случилась.


То что ты описал — это попытка добиться ленивого (отложенного) выполнения методом "закат солнца вручную".

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

А вообще, у вас явно получается некий ДСЛ. Только реализуете вы его доступными средствами языка. От того и столько "шума".

ЗЫ

Кстати, что касается DSL-ей в контексте работы с ASP.NET, то очень интересно глянуть на Nemerle on rails. Там, средствами Nemerle, реализовано срезу несколько DSL-ей, начиная от DSL-реструктуризации БД, и кончая передачей данных из контроллеров в представления. Кроме того там используется альтернативный движок рендеренга который позволяет избавиться от проблемы с которыми вы боритесь.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Стоит ли увлекаться лямбдами?
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.10.10 18:11
Оценка:
Здравствуйте, xvost, Вы писали:

X>Да. Создание делегата — во много раз более дорогое удовольствие чем создание простого объекта


Ты уж людей так сильно не пугай. Овершэд от вызова лямбд не такой уж большой чтобы об этом вообще задумываться в 99% случаев. К тому же если это множественный вызов (работа со списками, например), то создание лямбды происходит один раз, а не при каждом вызове.

Так что в очень критических местах лямбдам конечно не место. Но по жизни таких мест не много. Некоторые даже комбинаторные парсеры на базе шарповских лямбды умудряются делать. И ничего — работает (хотя я бы так делать никогда не стал).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[8]: Стоит ли увлекаться лямбдами?
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.10.10 18:16
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>В NET альтернатива делегату, по-моему, только интерфейс, то есть выбор не особо велик, а созданный делегат, в том числе и на базе анонима, можно "кэшировать".


Обычные классы с виртуальными методами. Они шустрее интерфейсов.
Именно так реализованы функциональные типы в Nemerle и (наверно) в F#.

Раньше это давало очень большой выигрыш (в 3-4 раза), но в 3.5 фрэймворке вызов лямбды был оптимизирован и на сегодня (плюс процессоры Core 2 и выше) разницу заметить сложно.

JR>Кстати, кто-нибудь интересовался, при использовании лямбд кэширование выполняется?


В C# выполняется.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Стоит ли увлекаться лямбдами?
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.10.10 18:18
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Интерфейсный вызов тоже не айс, в дотнете он дважды косвенный (где-то здесь обсуждалось давно), самая быстрая альтернатива — обычный виртуальный вызов экземплярного метода.


Эта информация устарела (к дотнет 3.5 х86).

Последние тесты показывают, что разницу в скорости виртуального вызова и лямбды заметить практически невозможно. Так что что-то там в МС все же подшаманили.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Стоит ли увлекаться лямбдами?
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.10.10 18:20
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>p.s. А вот функциональные значения в F# лишены проблем производительности создания экземпляров, так как являются просто экземплярами обычных классов, да и вызываются чуть быстрее (самый обычный виртуальный вызов, так же как в Nemerle, если мне не изменяет память).


Эта информация устарела. Я рядом об этом писал уже. Разницы в скорости почти нет.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[10]: Стоит ли увлекаться лямбдами?
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.10.10 19:23
Оценка: 30 (2)
Здравствуйте, Sinix, Вы писали:

П>>Для меня всегда было так (в порядке производительности):

S>

    S>
  1. Вызов virtual-метода = косвенный вызов, самый быстрый из косвенных в CLR;
    S>
  2. Вызов метода через интерфейс = дважды косвенный вызов, чуть медленнее;
    S>
  3. Вызов делегата = косвенный вызов + некоторый оверхед [ + ещё оверхед из-за подстановки Target'а ]
    S>

S>Ну, на самом деле оно не так, но имело некоторое отношение к реальности — для 1го фреймворка


S>Сейчас — примерно так:

S>http://stackoverflow.com/questions/216008/c-virtual-function-invocation-is-even-faster-than-a-delegate-invocation

Воспроизвел тест из вышеприведенной ссылке на C# и Nemerle (оба теста под управлением 3.5-го фрэймворка).
Результаты...
Nemerle-тест:
Virtual call took: 661
Delegate call took: 782
Functional type call took: 642

Virtual call took: 642
Delegate call took: 787
Functional type call took: 640

C#-тест:
651
852
638
852

Первый вызов виртуальный, второй делегата.

Я уж не знаю какого черта у немерла скорость вызова делегата оказалась чуть шустрее шарповской (думаю просто расклад (с) Поркчик Ржевский).

Но главное что можно вывести из этих тестов, что на современном железе (а у меня Core 2 2.8 Ghz) и относительно современном фрэймворке (я проводил измерения на 3.5-ом) разница между скоростью вызова делегата и виртуальным взовом не столь существенна (как была ранее).

И чем более существенны вычисления идущие рядом, тем меньше влияние она оказывает на скорость самого приложения.

Кроме того я произвел смелые научные эксперементы...
Так я сделал метод с предикатом обобщенным:
public DoSomething[T](predicator : Predicate[T], word : T) : int

Но это никак не сказалось на скорости работы вычислениях.
Возможно на скорости создания замыкания это и сказывается, но никак не на скорости вызова.

Более интересным оказался другой эксперимент... Я сделал обобщенным объект Foo и использовал его параметр типов в методе методах использующих делегат и функциональный тип. Результат получился просто феерическим:
Virtual call took: 1065
Delegate call took: 781
Functional type call took: 640

Virtual call took: 1065
Delegate call took: 781
Functional type call took: 640

Замедлился виртуальный вызов, но вызовы выполняемые через делегат и функциональный объект выполнялись за то же время, что и раньше! И это при том, что параметр типов не используется явно внутри виртуального вызова, а в других методах используется.

Немерловый тест:
using System;
using System.Console;
using System.Diagnostics;

class Foo
{
  public virtual IsTokenChar(word : string) : bool 
  {
    String.IsNullOrEmpty(word);
  }

  // this is a template method
  public DoSomething(word : string) : int
  {
    unchecked
    {
      mutable trueCount = 0;
      
      for (mutable i = 0; i < Repeat; ++i)
        when (IsTokenChar(word))
          ++trueCount;
      
      trueCount;
    }
  }

  public DoSomething(predicator : Predicate[string], word : string) : int
  {
    unchecked
    {
      mutable trueCount = 0;
      
      for (mutable i = 0; i < Repeat; ++i)
        when (predicator(word))
          ++trueCount;
          
      trueCount;
    }
  }

  public DoSomethingHof(predicator : string -> bool, word : string) : int
  {
    unchecked
    {
      mutable trueCount = 0;
      
      for (mutable i = 0; i < Repeat; ++i)
        when (predicator(word))
          ++trueCount;
          
      trueCount;
    }
  }

  private Repeat = 200000000;
}

module Program
{
  Main() : void
  {
    def f = Foo();

    repeat (2)
    {
      def sw = Stopwatch.StartNew();
      _ = f.DoSomething(null);
      sw.Stop();
      WriteLine($"Virtual call took: $(sw.ElapsedMilliseconds)");


      def sw = Stopwatch.StartNew();
      _ = f.DoSomething(str => String.IsNullOrEmpty(str), null);
      sw.Stop();
      WriteLine($"Delegate call took: $(sw.ElapsedMilliseconds)");

      def sw = Stopwatch.StartNew();
      _ = f.DoSomethingHof(str => String.IsNullOrEmpty(str), null);
      sw.Stop();
      WriteLine($"Functional type call took: $(sw.ElapsedMilliseconds)");

      WriteLine();
    }
  }
}
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[11]: Стоит ли увлекаться лямбдами?
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.10.10 19:36
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Более интересным оказался другой эксперимент... Я сделал обобщенным объект Foo и использовал его параметр типов в методе методах использующих делегат и функциональный тип. Результат получился просто феерическим:

VD>
VD>Virtual call took: 1065
VD>Delegate call took: 781
VD>Functional type call took: 640

VD>Virtual call took: 1065
VD>Delegate call took: 781
VD>Functional type call took: 640
VD>

VD>Замедлился виртуальный вызов, но вызовы выполняемые через делегат и функциональный объект выполнялись за то же время, что и раньше! И это при том, что параметр типов не используется явно внутри виртуального вызова, а в других методах используется.

Проверил данный тест на 4-ом фрэймворке. В нем замедления от использования виртуальных функций в обобщенном классе не наблюдается. А результаты такие:
640
925

Первое — виртуальный вызов (без разницы в обобщенном классе или нет), а второй — это делегат.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[11]: Стоит ли увлекаться лямбдами?
От: Sinix  
Дата: 22.10.10 00:28
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Воспроизвел тест из вышеприведенной ссылке на C# и Nemerle (оба теста под управлением 3.5-го фрэймворка).

Да, примерно тот же расклад вышел у меня
Автор: Sinix
Дата: 14.10.10
.

Насколько понял, ув. Пельмешко интересовала именно производительность в вакууме — иначе зачем заводить речь о косвенных вызовах?

VD>Но главное что можно вывести из этих тестов...

Согласен и поддерживаю

VD>Более интересным оказался другой эксперимент... Я сделал обобщенным объект Foo и использовал его параметр типов в методе методах использующих делегат и функциональный тип. Результат получился просто феерическим:

Забавно. А у нас — от так (.NET 4):
                   Direct:       84,0 ms
                Direct<T>:       84,0 ms
------
                     Data:    1 304,0 ms
              Cached Data:       80,0 ms
          Data-GenericFoo:    1 273,0 ms
   Cached Data-GenericFoo:       79,0 ms
           GenericData<T>:    1 290,0 ms
    Cached GenericData<T>:       78,0 ms
------
                    IData:    1 864,0 ms
             Cached IData:      717,0 ms
         IData-GenericFoo:    3 855,0 ms
  Cached IData-GenericFoo:    2 780,0 ms
                 IData<T>:    1 415,0 ms
          Cached IData<T>:      637,0 ms
------
                   Lambda:      884,0 ms
            Cached lambda:      635,0 ms
                 Delegate:    2 812,0 ms
          Cached delegate:      722,0 ms
              Delegate<T>:    2 808,0 ms
       Cached delegate<T>:    1 115,0 ms
Done...


  код
using System;
using System.Diagnostics;

class Program
{
  interface IData
  {
    int Foo();
    T GenericFoo<T>() where T: class;
  }
  interface IData<T>
  {
    T Foo();
  }

  class Data: IData
  {
    public int val;

    public int Foo()
    {
      return val;
    }

    public T GenericFoo<T>() where T: class
    {
      return null;
    }
  }

  class GenericData<T>: IData<T>
  {
    public T val;

    public T Foo()
    {
      return val;
    }
  }

  const int Count = 200 * 1000 * 1000;

  static int Foo()
  {
    return 0;
  }
  static T Foo<T>() where T: class
  {
    return null;
  }

  static void Main(string[] args)
  {
    Measure("Direct", () =>
    {
      for (int i = 0; i < Count; i++)
      {
        Foo();
      }
    });
    Measure("Direct<T>", () =>
    {
      for (int i = 0; i < Count; i++)
      {
        Foo<object>();
      }
    });

    Console.WriteLine("------");
    Measure("Data", () =>
    {
      for (int i = 0; i < Count; i++)
      {
        new Data()
        {
          val = i
        }.Foo();
      }
    });
    Measure("Cached Data", () =>
    {
      Data data = new Data()
      {
        val = 0
      };
      for (int i = 0; i < Count; i++)
      {
        data.Foo();
      }
    });
    Measure("Data-GenericFoo", () =>
    {
      for (int i = 0; i < Count; i++)
      {
        new Data()
        {
          val = i
        }.GenericFoo<object>();
      }
    });
    Measure("Cached Data-GenericFoo", () =>
    {
      Data data = new Data()
      {
        val = 0
      };
      for (int i = 0; i < Count; i++)
      {
        data.GenericFoo<object>();
      }
    });
    Measure("GenericData<T>", () =>
    {
      for (int i = 0; i < Count; i++)
      {
        new GenericData<int>()
        {
          val = i
        }.Foo();
      }
    });
    Measure("Cached GenericData<T>", () =>
    {
      GenericData<int> data = new GenericData<int>()
      {
        val = 0
      };
      for (int i = 0; i < Count; i++)
      {
        data.Foo();
      }
    });

    Console.WriteLine("------");
    Measure("IData", () =>
    {
      for (int i = 0; i < Count; i++)
      {
        ((IData)new Data()
        {
          val = i
        }).Foo();
      }
    });
    Measure("Cached IData", () =>
    {
      IData data = new Data()
      {
        val = 0
      };
      for (int i = 0; i < Count; i++)
      {
        data.Foo();
      }
    });
    Measure("IData-GenericFoo", () =>
    {
      for (int i = 0; i < Count; i++)
      {
        ((IData)new Data()
        {
          val = i
        }).GenericFoo<object>();
      }
    });
    Measure("Cached IData-GenericFoo", () =>
    {
      IData data = new Data()
      {
        val = 0
      };
      for (int i = 0; i < Count; i++)
      {
        data.GenericFoo<object>();
      }
    });
    Measure("IData<T>", () =>
    {
      for (int i = 0; i < Count; i++)
      {
        ((IData<int>)new GenericData<int>()
        {
          val = i
        }).Foo();
      }
    });
    Measure("Cached IData<T>", () =>
    {
      IData<int> data = new GenericData<int>()
      {
        val = 0
      };
      for (int i = 0; i < Count; i++)
      {
        data.Foo();
      }
    });

    Console.WriteLine("------");
    Measure("Lambda", () =>
    {
      for (int i = 0; i < Count; i++)
      {
        Func<int> a = () => i;
        a();
      }
    });
    Measure("Cached lambda", () =>
    {
      int c = 5;
      Func<int> a = () => c;
      for (int i = 0; i < Count; i++)
      {
        a();
      }
    });
    Measure("Delegate", () =>
    {
      for (int i = 0; i < Count; i++)
      {
        Func<int> a = Foo;
        a();
      }
    });
    Measure("Cached delegate", () =>
    {
      Func<int> a = Foo;
      for (int i = 0; i < Count; i++)
      {
        a();
      }
    });
    Measure("Delegate<T>", () =>
    {
      for (int i = 0; i < Count; i++)
      {
        Func<object> a = Foo<object>;
        a();
      }
    });
    Measure("Cached delegate<T>", () =>
    {
      Func<object> a = Foo<object>;
      for (int i = 0; i < Count; i++)
      {
        a();
      }
    });
    Console.Write("Done...");
    Console.ReadKey();
  }

  static void Measure(string name, Action callback)
  {
    Stopwatch sw = Stopwatch.StartNew();
    callback();
    sw.Stop();
    Console.WriteLine("{0,25}: {1,10:#,##0.0###} ms", name, sw.ElapsedMilliseconds);
  }
}
Re[11]: Стоит ли увлекаться лямбдами?
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.10.10 14:09
Оценка:
Здравствуйте, Воронков Василий, Вы писали:

ВВ>Гм, вызов через интерфейс стал медленее вызова через делегат? Интересно.


На самом деле — это гон. На современных процах и фрэймворках они выполняются примерно за одно время. Разница ничтожна, да и зависит от фазы луны.

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

Если это так, то на лицо поддержка Интелом управляемых языков . И это еще цветочки. Говорят, что Интел решил свой ФЯ залудить. Да не простой, а для числодробления.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.