tag:blogger.com,1999:blog-8596733192274108952.post7795236140670986199..comments2024-03-12T06:00:18.305+02:00Comments on Programming stuff: Кэширующий декоратор на деревьях выраженийSergey Teplyakovhttp://www.blogger.com/profile/14300835272589262297noreply@blogger.comBlogger21125tag:blogger.com,1999:blog-8596733192274108952.post-24492657650268735922013-01-21T15:48:32.083+02:002013-01-21T15:48:32.083+02:00@80InchNail: Возвращать таску с результатом - это ...@80InchNail: Возвращать таску с результатом - это уже не такое уж и новое. Это так называемый Task-based Async Pattern, который появился с выходом .NET 4.0, а в .NET 4.5 стал по-сути, стандартом.<br /><br />НИЧЕГО СЕБЕ, по полю для каждого метода и нет никаких проблем!! Тем более, что нужно будет 2 поля, для кэша и ключа, и еще гору кода сделать, чтобы решение было потокобезопасным.<br /><br />Так что все предложенные варианты - это огромное количество повторяющегося кода (и подумай, какие только имена кэшей будет для перегруженных методов ;)).Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-25123432160993946202013-01-21T07:07:30.967+02:002013-01-21T07:07:30.967+02:00Даже проще, можно полностью оставить твой код, или...Даже проще, можно полностью оставить твой код, или взять любой готовый кэш, а с каждым методом создавать поле с его уникальным ключем:<br /><br />private readonly object methodAKey = new object(); <br />ну или строку или Guid<br /><br />Параметры метода (которые будут частью ключа) сложно потерять при рефакторинге. 80InchNailhttps://www.blogger.com/profile/10414893001441132636noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-5557471704903074912013-01-21T07:03:49.506+02:002013-01-21T07:03:49.506+02:00Прикольная идея возвращать task результатом? Это ч...Прикольная идея возвращать task результатом? Это че-то не новое? Никогда раньше не видел. <br /><br />Насчет проблемы с ключем для dictionary на основе имени и т д. Не думаю, что это вообще проблема. <br /><br />Завели по полю для каждого метода и проблемы нет. <br /><br />class Service {<br />private Task methodACache;<br />public Task< object> MethodA()<br />{<br /> if (methodACache == null) { ... }<br /> return methodACache;<br />}<br /><br />private Dictionary< string, Task< object>> methodBCache = new...;<br />public Task< object> MethodB(string name)<br />{<br /> if (methodBCache.ContainsKey(name)) ...<br /> // ну в общем понятно...<br />}<br /><br />И для поля и для dictionary, код легко выносится в хэлперы.80InchNailhttps://www.blogger.com/profile/10414893001441132636noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-52414749604241667472012-10-13T08:56:51.826+03:002012-10-13T08:56:51.826+03:00"Офтоп": Тут смотри какое дело: сейчас т..."Офтоп": Тут смотри какое дело: сейчас ты добился того, что <b>все</b> "клиенты" ждут, пока кто-то один получит данные, а потом очистит кеш, но зато вызов происходит строго один раз. В условиях, когда "клиентов" много это, зачастую, не самая лучшая стратегия. Логически-практически требовать строго одного вызова необходимости нет, мы же всего лишь экономить пытаемся. <br /><br />ConcurrentDictionary вынуждает распараллелить [иногда] запросы (вызывая valueFactory тогда, когда это, вроде бы, и не нужно) и это выгодно - многие автомобилисты предпочитают плохо ехать (сделав крюк), чем хорошо стоять в пробке ;о) Но это, конечно, уже не имеет никакого отношения к теме статьи. Всего лишь стратегия против тактики :о)Anonymoushttps://www.blogger.com/profile/11725153141467656623noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-69882948205024290082012-10-11T19:39:17.264+03:002012-10-11T19:39:17.264+03:00@Никита: таки да, похоже Chess уже не развивается....@Никита: таки да, похоже Chess уже не развивается.<br /><br />Интересно, а что есть подобное хорошее?Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-47992357630567520092012-10-11T07:24:20.439+03:002012-10-11T07:24:20.439+03:00Sergey Teplyakov, я так понимаю проект Chess закры...Sergey Teplyakov, я так понимаю проект Chess закрыт и не развивается протестировать TPL или PLinq с его помощью не получится...Anonymoushttps://www.blogger.com/profile/13509501520633020957noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-54788711350423584922012-10-02T20:23:35.410+03:002012-10-02T20:23:35.410+03:00@Viacheslav: пришлось вернуть старый код и использ...@Viacheslav: пришлось вернуть старый код и использовать Dictionary + lock. См. апдейт.<br /><br />@jack: Да, это и правда сурово. Я померил производительность и ужаснулся. Я поменял код на ExpressionVisitor, для подавляющего большинства случаев быстрее стало раз в 80. См. апдейт.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-12956158069685298952012-09-27T09:26:56.659+03:002012-09-27T09:26:56.659+03:00>>Основная же работа в методе ProcessMethodC...>>Основная же работа в методе ProcessMethodCallExpression заключается в обработке аргументов объекта MethodCallExpression; поскольку аргументом метода может быть не просто константное выражение вида () => Foo(42,”Some string”), но и выражения вида () => Foo(GetId(), ProcessSomething()),<br /><br />Ну если ProcessMethodCallExpression вызывается только из такого кеширующего провайдера, то никаких GetId()/ProcessSomething() быть не может. Ведь по логике методы провайдера просто должны передать свои аргументы в ProcessMethodCallExpression. никаких выражений. Да и вообще лично меня коробит (1 + (количество аргументов)) компиляций на каждый вызов метода ICustomProviderjack128https://www.blogger.com/profile/06241586079811572654noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-32099157304953773542012-09-25T22:32:04.125+03:002012-09-25T22:32:04.125+03:00Важно отметить, что тест имеет дуступ к кэшу синхр...Важно отметить, что тест имеет дуступ к кэшу синхронный. Нельзя расчитывать, что реализация не станет оптимистичной (повзолять запуск одного таска паралельно) или вообще кеш не станет локальным для треда.<br /><br />Но всё же, референсы - это детали реализации и если кэш изменят таким образом что бы создавались таски каждый раз (например для избежания накопления синхронных континюэшенов), то это завалится. Мне кажется, правильнее проверять саму суть: "низлежащий объект будет дёргаться один раз".Anonymoushttps://www.blogger.com/profile/03159331377410025186noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-31870227386538685472012-09-24T23:14:09.397+03:002012-09-24T23:14:09.397+03:00:DDD Удалил лишний syncRoot.
Вернуть task.Continu...:DDD Удалил лишний syncRoot.<br /><br />Вернуть task.ContinueWith(...) не выйдет, поскольку ContinueWith возвращает не исходную задачу, а уже продолжение, тип которой Task, а не Task, так что последующее приведение типов упадет с ошибкой.<br /><br />Да и вообще, этот декоратор должен возвращать оригинальную задачу, а не задачу с нашим собственным продолжением.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-57089034282246942122012-09-24T23:01:41.921+03:002012-09-24T23:01:41.921+03:00Кстати, а не правильно ли делать return task.Conti...Кстати, а не правильно ли делать return task.ContinueWith(…)?Anonymoushttps://www.blogger.com/profile/11725153141467656623noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-45575478999436262802012-09-24T22:45:53.830+03:002012-09-24T22:45:53.830+03:00И правда легче стало! А теперь, что бы понять, поч...И правда легче стало! А теперь, что бы понять, почему меня никто почти не любит, напомню, что _cacheSyncRoot тоже можно удалить из определения класса :о)Anonymoushttps://www.blogger.com/profile/11725153141467656623noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-45917916659922604432012-09-24T21:54:41.556+03:002012-09-24T21:54:41.556+03:00Последний пример изменил с локов на ConcurrentDict...Последний пример изменил с локов на ConcurrentDictionary. В данном случае без особого усложнения, но зато наверняка с некоторым повышением производительности.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-86119846625224881992012-09-24T21:37:06.511+03:002012-09-24T21:37:06.511+03:00Ну если GetCustomerName… секундами, то да, "у...Ну если GetCustomerName… секундами, то да, "улучшать" не надо :о))<br /><br />Да и по сравнению даже с просто удалённым вызовом в таком простом случае конечно что-то ещё изобретать излишне, хотя CuncurrentDictionary<,> уже будет проще для чтения (для тех кто хоть примерно себе представляет что это такое) - никаких вроде бы специальных синхронизаций вообще нет - "чище" что ли получается.<br /><br />До лок-фри мне ещё как до городу Парижу пешком зимой из Иркутска, но уж да, больно хочется освоить. А освоить без применения не получается :о)Anonymoushttps://www.blogger.com/profile/11725153141467656623noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-82510650366929214402012-09-24T21:24:36.284+03:002012-09-24T21:24:36.284+03:00Это и правда интересная задача, хотя в данном конк...Это и правда интересная задача, хотя в данном конкретном случае это будет исключительно ради интереса, поскольку кэшируются длительные операции, время выполнения которой исчисляется секундами.<br /><br />Осваиваешь low lock/lock free техники? И чем, кстати, потом тестируешь? <a href="http://research.microsoft.com/en-us/projects/chess/" rel="nofollow">Chess</a>?Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-25907360543099489482012-09-24T21:20:29.012+03:002012-09-24T21:20:29.012+03:00А теперь можно приступить к одному из самых интере...А теперь можно приступить к одному из самых интересных (для меня в последнее время) - попробовать переписать вообще без локов :о)) А то, чесслово, как-то неприятно смотреть на код с локами, когда вокруг столько прекрасных средств обойтись без :о))Anonymoushttps://www.blogger.com/profile/11725153141467656623noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-65148758070261261262012-09-24T21:14:08.585+03:002012-09-24T21:14:08.585+03:00Да, конечно, я забыл lock. У меня первая версия во...Да, конечно, я забыл lock. У меня первая версия вообще была осознанно потонебезопасная, вообще без блокировок, потом блокировки в основном теле добавил, а в "продолжении" - забыл.<br /><br />Спасибо, поправил.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-30433961045492433352012-09-24T21:06:49.717+03:002012-09-24T21:06:49.717+03:00Извиняюсь за спам, сразу не отыскал в примерах что...Извиняюсь за спам, сразу не отыскал в примерах что это Dictionary<>. Тогда, как мне кажется, Remove из неё в ContinueWith будет происходить не из-под лока и поэтому не безопасно. Вот если это свой словарь, в который передаётся тот же sync root, то будет кажется нормально.Anonymoushttps://www.blogger.com/profile/11725153141467656623noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-49391095508301252322012-09-24T21:04:14.226+03:002012-09-24T21:04:14.226+03:00@Vyacheslav: чего-то твои комменты в спам попали;)...@Vyacheslav: чего-то твои комменты в спам попали;)<br /><br />Обновил примеры, вернул туда объявление этих полей.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-54984646876986528652012-09-24T20:58:21.903+03:002012-09-24T20:58:21.903+03:00…или _cache из примера ниже.…или _cache из примера ниже.Anonymoushttps://www.blogger.com/profile/11725153141467656623noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-15527400238884305002012-09-24T20:56:55.545+03:002012-09-24T20:56:55.545+03:00А что есть "_outstandingTasks"?А что есть "_outstandingTasks"?Anonymoushttps://www.blogger.com/profile/11725153141467656623noreply@blogger.com