Я там маленько порефакторил (разнес код по файлам) и сделал простенький но очень удобный движек для тестов.
Сейчас нужно все обложить тестами и тогда можно будет сдеать чтобы трансформация шла через промежуточную модель.
2dsorokin займешся?
... << RSDN@Home 1.2.0 alpha 4 rev. 1305>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
WH>Сейчас нужно все обложить тестами и тогда можно будет сдеать чтобы трансформация шла через промежуточную модель. WH>2dsorokin займешся?
Я планировал сначала async из f# приручить. Билдер для Немерле уже существует, но нужен еще модуль для запуска вычислений и некоторые асинхронные расширения к паре стандартных классов. По сути это будет оболочка над f#-повским async. Здесь же проверится в целом возможность интегрироваться с workflow из f#, которую я считаю важной.
Тесты мне понравились. Кстати, я там еще в самом начале внес ошибку в метод Combine. Первый аргумент на самом деле должен иметь тип M[FakeVoid] или M[Unit], где Unit из f#. Это лучше исправить. И, вообще, мне ужасно не нравится название FakeVoid, но ничего лучше пока не придумал. Название Unit занято за f#. Еще думаю, что можно избавиться от поля FakeVoid.Value. Можно просто null передавать, хотя это не совсем правильно использовать null вместо значения с точки зрения теории ф.п., но похоже, что f# внутри так и поступает со своим Unit.
Здравствуйте, dsorokin, Вы писали:
D> Кстати, я там еще в самом начале внес ошибку в метод Combine. Первый аргумент на самом деле должен иметь тип M[FakeVoid] или M[Unit], где Unit из f#. Это лучше исправить.
Здравствуйте, dsorokin, Вы писали:
D>Я планировал сначала async из f# приручить.
Мне честно говоря async из F# не понравился совсем.
Его делали явно теоретики которые реальную многопоточность в глаза не видели.
За привязку к одному пулу потоков нужно канделябром по рукам. Это эпикфейл который делает всю либо малополезной.
Да и просто криво проектирован.
Я сейчас выношу свой мозг CPS'ом после чего сделаю свой async.
D>Можно просто null передавать, хотя это не совсем правильно использовать null вместо значения с точки зрения теории ф.п., но похоже, что f# внутри так и поступает со своим Unit.
По хорошему генерики должны параметризоваться void'ом, а на физическом уровне вообще ничего не должно передаваться. Но CLR писали дуболомы и он так не умеет.
... << RSDN@Home 1.2.0 alpha 4 rev. 1305>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
WH>Я сейчас выношу свой мозг CPS'ом после чего сделаю свой async.
Что-то ты добрый сегодня Кстати, внутри async сидит три продолжения: (1) для основного потока вычислений, (2) для обработки ошибок и (3) для экстренной отмены вычислений.
А в привязке к одному системному пулу есть свой смысл. Новый поток создается на новую длительную асинхронную операцию, и как я понимаю, вызвавший поток отдается пулу (??). То есть, обычно заняты те потоки, которые выполняют что-то тяжеловесное. А ресурсы компьютера ограничены. Поэтому имеет смысл использовать ограниченное количество рабочих потоков. Если я прав в своем представлении, то такая схема выглядит логичной.
Вообще, async — довольно таки сложная штука. Там должно быть много подводных камней. В той же оптимизации хвостовой рекурсии не все так гладко. Внутри async используют, например, FakeUnit вместо unit из-за этого. Что-то там не так с виртуальной машиной дот-нета. На мой взгляд проще использовать уже готовое решение в виде async через небольшую оболочку, чем писать заново аналог.
Здравствуйте, dsorokin, Вы писали:
D>Что-то ты добрый сегодня
Я? Добрый? Не замечал.
D>Кстати, внутри async сидит три продолжения: (1) для основного потока вычислений, (2) для обработки ошибок и (3) для экстренной отмены вычислений.
Я в курсе.
D>А в привязке к одному системному пулу есть свой смысл. Новый поток создается на новую длительную асинхронную операцию, и как я понимаю, вызвавший поток отдается пулу (??). То есть, обычно заняты те потоки, которые выполняют что-то тяжеловесное. А ресурсы компьютера ограничены. Поэтому имеет смысл использовать ограниченное количество рабочих потоков. Если я прав в своем представлении, то такая схема выглядит логичной.
В теории.
Проблема в том что я провел два года на галерах в Яндексе и знаю что одного пула потоков хватает только в самых примитивных ситуациях.
Если же у нас логика чуть сложнее то одним пулом не отделаться.
Ибо появляются проблемы с откликом и дедлоки в том числе распределенные.
D>Вообще, async — довольно таки сложная штука. Там должно быть много подводных камней. В той же оптимизации хвостовой рекурсии не все так гладко. Внутри async используют, например, FakeUnit вместо unit из-за этого. Что-то там не так с виртуальной машиной дот-нета. На мой взгляд проще использовать уже готовое решение в виде async через небольшую оболочку, чем писать заново аналог.
Там единственная сложность это загнать вычисления в довольно хитрый CPS.
Тк я плохо понимаю CPS эта часть идет туго. Но как только разберусь что к чему все остальное сделаю за несколько часов.
... << RSDN@Home 1.2.0 alpha 4 rev. 1305>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
WH>Проблема в том что я провел два года на галерах в Яндексе и знаю что одного пула потоков хватает только в самых примитивных ситуациях. WH>Если же у нас логика чуть сложнее то одним пулом не отделаться. WH>Ибо появляются проблемы с откликом и дедлоки в том числе распределенные.
Честно говоря, не вижу причин для дедлоков. Впрочем, у меня нет богатого опыта в этой области.
WH>Там единственная сложность это загнать вычисления в довольно хитрый CPS.
Это как раз оказалось просто Но я с этим разбирался отдельно, и это заняло некоторое время.
Здравствуйте, dsorokin, Вы писали:
WH>>Там единственная сложность это загнать вычисления в довольно хитрый CPS. D>Это как раз оказалось просто Но я с этим разбирался отдельно, и это заняло некоторое время.
Короче я забил на CPS и сделал по рабоче-крестьянски.
Получилось совсем просто и подозреваю заметно быстрее чем в версии F#.
Набросок я закомитил.
В финальной версии отучу его на каждый шаг бегать по всему стеку. Это не сложно.
... << RSDN@Home 1.2.0 alpha 4 rev. 1305>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, dsorokin, Вы писали:
D>Я планировал сначала async из f# приручить.
Это плохая идея хотя бы потому, что заставляет таскать с собой лишнюю сботку. Потом еще не ясно не нарушит ли такое таскание лицензии f#. Ведь рантайм f#-а с дотнетом не поставляется.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, WolfHound, Вы писали:
WH>Короче я забил на CPS и сделал по рабоче-крестьянски. WH>Получилось совсем просто и подозреваю заметно быстрее чем в версии F#. WH>Набросок я закомитил. WH>В финальной версии отучу его на каждый шаг бегать по всему стеку. Это не сложно.
У меня есть сомнения в том, что это несложно. А не придешь ли к необходимости того же самого CPS, которого сейчас хочешь избежать?
Получается, что где-то глубоко в локальном месте на каждом шаге ты либо редуцируешь вычисление типа Async, либо возвращаешь значение. И тут нужно передать управление выше по стеку вызовов, но это именно то, что может делать CPS.
Здравствуйте, VladD2, Вы писали:
VD>Это плохая идея хотя бы потому, что заставляет таскать с собой лишнюю сботку. Потом еще не ясно не нарушит ли такое таскание лицензии f#. Ведь рантайм f#-а с дотнетом не поставляется.
С .NET v4, насколько знаю, поставляется (это не powerpack). Что касается более ранних версий, то можно требовать самостоятельной установки F# разработчиком и ручного компилирования такого переходника. Тогда, наверное, лицензия не будет нарушена. То есть, поставлять переходник в исходниках.
Здравствуйте, WolfHound, Вы писали:
WH>Набросок я закомитил.
Обработку while можно написать короче для ContBuilder:
public While[A, R](pred : void -> bool, body : Cont[A, R]) : Cont[A, R]
where A : new()
{
if (pred ())
Bind (body, () => While (pred, body))
else
Zero ()
}
Вот, с foreach дело обстоит сложнее. Для него нужен using, который в свою очередь выражается через try-finally. А для try-finally нужно уже второе продолжение для обработки ошибок.
Здравствуйте, WolfHound, Вы писали:
WH>Там единственная сложность это загнать вычисления в довольно хитрый CPS.
Между прочим, могу поделиться своей реализацией Cont, но на f#. Там используется два продолжения: одно для основного потока, другое — для обработки ошибок. Все это вместе позволяет реализовать все конструкции.
Здравствуйте, dsorokin, Вы писали:
D>У меня есть сомнения в том, что это несложно.
Не надо сомневаться.
Берем код:
[Record]
public class AsyncBind[A, B] : Async[B]
{
private mutable async : Async[A];
private cont : A -> Async[B];
public override Exec(job : AsyncJob, _res : ref B) : Async[B]
{
mutable r;
async = async.Exec(job, ref r);
if (async == null)
cont(r);
else
this;
}
}
И меняем так:
async = job.ExecWhileReady(async, ref r);
ExecWhileReady будет выполнять код пока не дойдет до конца или не встретит Async который ждет результата.
D>А не придешь ли к необходимости того же самого CPS, которого сейчас хочешь избежать?
Нет.
D>Получается, что где-то глубоко в локальном месте на каждом шаге ты либо редуцируешь вычисление типа Async, либо возвращаешь значение. И тут нужно передать управление выше по стеку вызовов, но это именно то, что может делать CPS.
Я это и без CPS могу.
См код. Сейчас такая передача управления происходит каждый шаг.
... << RSDN@Home 1.2.0 alpha 4 rev. 1305>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, dsorokin, Вы писали:
D>С .NET v4, насколько знаю, поставляется (это не powerpack).
Нет. Не поставляется.
D> Что касается более ранних версий, то можно требовать самостоятельной установки F# разработчиком и ручного компилирования такого переходника.
Что мягко говоря очень неудобно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, dsorokin, Вы писали:
D>А как в твоем случае будет реализована такая важная фича продолжений: умение прерываться в одном потоке и возобновляться в другом?
Ты вот этот код видел?
Если очень захочется каждую итерацию можно проводить в отдельном потоке
public static ExecAll(job : AsyncJob, mutable async : Async[A]) : A
{
mutable res;
while (async != null)
async = async.Exec(job, ref res);
res;
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1305>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
D>>А как в твоем случае будет реализована такая важная фича продолжений: умение прерываться в одном потоке и возобновляться в другом?
WH>Если очень захочется каждую итерацию можно проводить в отдельном потоке
Но ведь переключаться на другой поток нужно будет внутри Exec (и внутри job.ExecWhileReady), если я правильно понимаю задачу async.
Здравствуйте, dsorokin, Вы писали:
D>Но ведь переключаться на другой поток нужно будет внутри Exec (и внутри job.ExecWhileReady), если я правильно понимаю задачу async.
Взводим внутри job флажек и вываливаемся на самый верх.
После чего спокойно переключаемся.
Думаю этого хватит: