Сообщение Interpolated strings: есть идеи, как подправить производител от 19.04.2016 7:47
Изменено 19.04.2016 8:41 Sinix
в библиотеке CodeJam есть методы-ассерты с api вида
Code.AssertState(someCondition, "Message #123");
Code.AssertState(someCondition, "Message #{0}", 123);
ну, т.е. обычная перегрузка и вариант с строкой форматирования.
в 99.99% случаев ассерт не срабатывает, форматирование не требуется.
В чём проблема: рекомендации решарпера заменяют второй вариант на
Code.AssertState(someCondition, $"Message #{123}");
, компилятор превращает эту строчку в Code.AssertState(someCondition, string.Format("Message #{0}", 123));
, получаем ~120x падение в производительности из-за того, что теперь форматирование выполняется при каждом вызове метода. Как показывает практика, простое решение "не верьте советам решарпера" не работает
Будем искать сложное.
Матчасть:
Надо сказать, что мы тут не одиноки, на те же проблемы наткнулся, скажем, NLog.
Подписываюсь под
>We generally believe that libraries will mostly be written with different API names for methods which do different things. Therefore overload resolution differences between FormattableString and String don't matter, so string might as well win. Therefore we should stick with the simple principle that an interpolated string is a string. End of story.
Assumptions are the Base of evil.
Обсуждения этого дела в issues рослина:
https://github.com/dotnet/roslyn/issues/46
https://github.com/dotnet/roslyn/issues/10221
Предполагаемое решение (нам не подходит, т.к. к static-классу extension-метод не прикрутишь):
http://pvlerick.github.io/2016/01/poking-the-csharp-compiler-overload-resolution-for-string-and-formattablestring/
Бенчмарк:
Formattable: 253ms, ips: 39 487 312,53 | Mem: 2 288,09 kb, GC 0/1/2: 279/0/0 => 10000000
FormattableNoArg: 112ms, ips: 89 158 822,18 | Mem: 2 232,11 kb, GC 0/1/2: 101/0/0 => 10000000
Formattable (int): 40ms, ips: 245 853 073,29 | Mem: 904,04 kb, GC 0/1/2: 76/0/0 => 10000000
String: 2ms, ips: 3 343 251 646,55 | Mem: 8,00 kb, GC 0/1/2: 0/0/0 => 10000000
String.Format: 125ms, ips: 79 721 103,69 | Mem: 64,04 kb, GC 0/1/2: 178/0/0 => 10000000
Done.
код | |
FormattableString как параметр не прикрутишь, таргетинг на 4.5 стоит, там этого типа нет.
| |
Собственно вопрос:
Ну и как с этим жить? В смысле, есть у кого-то опыт дизайна API, позволяющего и удовлетворить сторонников interpolated strings, и не убить при этом перфоманс приложения?
в библиотеке CodeJam есть методы-ассерты с api вида
Code.AssertState(someCondition, "Message #123");
Code.AssertState(someCondition, "Message #{0}", 123);
ну, т.е. обычная перегрузка и вариант с строкой форматирования.
в 99.99% случаев ассерт не срабатывает, форматирование не требуется.
В чём проблема: рекомендации решарпера заменяют второй вариант на
Code.AssertState(someCondition, $"Message #{123}");
, компилятор превращает эту строчку в Code.AssertState(someCondition, string.Format("Message #{0}", 123));
, получаем ~120x падение в производительности из-за того, что теперь форматирование выполняется при каждом вызове метода. Как показывает практика, простое решение "не верьте советам решарпера" не работает
Будем искать сложное.
Матчасть:
Надо сказать, что мы тут не одиноки, на те же проблемы наткнулся, скажем, NLog.
Подписываюсь под
>We generally believe that libraries will mostly be written with different API names for methods which do different things. Therefore overload resolution differences between FormattableString and String don't matter, so string might as well win. Therefore we should stick with the simple principle that an interpolated string is a string. End of story.
Assumptions are the Base of evil.
Обсуждения этого дела в issues рослина:
https://github.com/dotnet/roslyn/issues/46
https://github.com/dotnet/roslyn/issues/10221
Предполагаемое решение (нам не подходит, т.к. к static-классу extension-метод не прикрутишь):
http://pvlerick.github.io/2016/01/poking-the-csharp-compiler-overload-resolution-for-string-and-formattablestring/
Бенчмарк:
Formattable: 260ms, ips: 38 442 435,53 | Mem: 2 288,09 kb, GC 0/1/2: 279/0/0 => 10000000
FormattableNoArg: 109ms, ips: 91 461 100,22 | Mem: 2 232,11 kb, GC 0/1/2: 101/0/0 => 10000000
FormattableInt: 41ms, ips: 242 532 426,59 | Mem: 904,04 kb, GC 0/1/2: 76/0/0 => 10000000
String: 3ms, ips: 3 317 850 033,18 | Mem: 8,00 kb, GC 0/1/2: 0/0/0 => 10000000
String.Format: 125ms, ips: 79 656 647,98 | Mem: 64,04 kb, GC 0/1/2: 178/0/0 => 10000000
FuncNoClosure: 28ms, ips: 344 932 255,31 | Mem: 8,00 kb, GC 0/1/2: 0/0/0 => 10000000
FuncClosure: 91ms, ips: 109 542 823,03 | Mem: 1 376,06 kb, GC 0/1/2: 203/0/0 => 10000000
FuncClosureFormat: 93ms, ips: 107 068 788,48 | Mem: 1 376,06 kb, GC 0/1/2: 203/0/0 => 10000000
Done.
код | |
FormattableString как параметр не прикрутишь, таргетинг на 4.5 стоит, там этого типа нет.
| |
Собственно вопрос:
Ну и как с этим жить? В смысле, есть у кого-то опыт дизайна API, позволяющего и удовлетворить сторонников interpolated strings, и не убить при этом перфоманс приложения?
UPD Добавил пример с Func для ленивого получения строки.