Информация об изменениях

Сообщение Re[21]: Mногопоточность: C++ vs Erlang vs другие от 10.06.2015 21:44

Изменено 10.06.2015 21:48 Evgeny.Panasyuk

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

EP>>Нет, я говорю о том что корутины могут быть копируемыми, перемещаемыми, сериализуемыми, быть обычными объектами без аллоцируемого состояния, и при этом иметь удобный синтаксис для await/yield и т.п.

V>Ну ОК.
V>Не мог бы ты показать конкретные примеры из твой твоей ссылки proposal от одного из авторов буста, чтобы я точно знал, что мы обсуждаем одно и то же. Я прочел только первую половину и практически со всем написанным там не согласен. Похоже, мы сейчас заочно обсуждаем разные вещи.

Я прежде всего говорю о методе управления состоянием корутины, а конкретно в каком виде оно хранится и что с ним можно делать.

В Proposal от Microsoft — n4134 — состояние аллоцируется аллокатором определённым в resumable_traits. Сколько нужно места под это состояние — заранее неизвестно, также это состояние нельзя копировать/перемещать, и поэтому в общем случае оно будет жить в куче, откуда следуют неизбежные тормоза.
Например нельзя вернуть из функции саму корутину со всем состоянием — это всегда будет некоторый хэндл на состояние размещённое в куче.

В Proposal от автора Boost.Asio — n4244 — корутина со всем её состоянием это фактически объект обычного класса, поля которого полностью определяют состояние корутины. Размер всего состояния это всего лишь sizeof этого класса, поэтому его легко создавать на стэке, да и вообще где угодно. Поля такого объекта можно перемещать/копировать/сериализовывать/и т.п.
Layout такого класса (с оптимизациями типа union для полей из соседних scope) разобран в главе "13 Implementation approach". Но если не вдаваться в детали оптимизаций и времени жизни, то для вот такой корутины (псевдокод, подобный синтаксис реализуем при обоих подходах к управлению состоянием):
future<int> sum()
{
    int x = await foo();
    int y = await bar();
    return x + y;
}
получается вот такой layout:
struct coroutine
{
    int internal_state; // i.e. current await point
    int x;
    int y;
    int result;
};


V>Что интересует? Связь синтаксиса и семантики. По синтаксису в текущих proposal stackless-корутина может представлять из себя просто resumable ф-ию, в которой отсутствуют уши низлежащего механизма.


Синтаксис может быть, и должен быть лучше чем то что представлено в 4244. Я же говорю про реализацию состояния, возможности которые ею предоставляются, и насколько она эффективна.

V>Покажи, как "копировать ф-ию"?


Вот конкретный пример:
auto foo = [](auto continuation) // .then
{
    continuation(1);
    continuation(2);
    continuation(3);
};

auto bar = [](auto continuation) // .then
{
    continuation('a');
    continuation('b');
    continuation('c');
};

struct coroutine : asio::coroutine
{
    int x;
    char y;

    void operator()()
    {
        reenter(*this)
        {
            await(x, foo); // x = await foo;
            await(y, bar); // y = await bar;
            cout << x << " " << y << endl;
        }
    };
};

int main()
{
    coroutine{}();
}
Вывод:
1 a
1 b
1 c
2 a
2 b
2 c
3 a
3 b
3 c
LIVE DEMO on Coliru
Это подобие List Monad, без копирования продолжения так не получится.
Re[21]: Mногопоточность: C++ vs Erlang vs другие
Здравствуйте, vdimas, Вы писали:

EP>>Нет, я говорю о том что корутины могут быть копируемыми, перемещаемыми, сериализуемыми, быть обычными объектами без аллоцируемого состояния, и при этом иметь удобный синтаксис для await/yield и т.п.

V>Ну ОК.
V>Не мог бы ты показать конкретные примеры из твой твоей ссылки proposal от одного из авторов буста, чтобы я точно знал, что мы обсуждаем одно и то же. Я прочел только первую половину и практически со всем написанным там не согласен. Похоже, мы сейчас заочно обсуждаем разные вещи.

Я прежде всего говорю о методе управления состоянием корутины, а конкретно в каком виде оно хранится и что с ним можно делать.

В Proposal от Microsoft — n4134 — состояние аллоцируется аллокатором определённым в resumable_traits. Сколько нужно места под это состояние — заранее неизвестно, также это состояние нельзя копировать/перемещать, и поэтому в общем случае оно будет жить в куче, откуда следуют неизбежные тормоза.
Например нельзя вернуть из функции саму корутину со всем состоянием — это всегда будет некоторый хэндл на состояние размещённое в куче.

В Proposal от автора Boost.Asio — n4244 — корутина со всем её состоянием это фактически объект обычного класса, поля которого полностью определяют состояние корутины. Размер всего состояния это всего лишь sizeof этого класса, поэтому его легко создавать на стэке, да и вообще где угодно. Поля такого объекта можно перемещать/копировать/сериализовывать/и т.п.
Layout такого класса (с оптимизациями типа union для полей из соседних scope) разобран в главе "13 Implementation approach". Но если не вдаваться в детали оптимизаций и времени жизни, то для вот такой корутины (псевдокод, подобный синтаксис реализуем при обоих подходах к управлению состоянием):
future<int> sum()
{
    int x = await foo();
    int y = await bar();
    return x + y;
}
получается вот такой layout:
struct coroutine
{
    int internal_state; // i.e. current await point
    int x;
    int y;
    int result;
};


V>Что интересует? Связь синтаксиса и семантики. По синтаксису в текущих proposal stackless-корутина может представлять из себя просто resumable ф-ию, в которой отсутствуют уши низлежащего механизма.


Синтаксис может быть, и должен быть лучше чем то что представлено в 4244. Я же говорю про реализацию состояния, возможности которые ею предоставляются, и насколько она эффективна.

V>Покажи, как "копировать ф-ию"?


Вот конкретный пример:
auto foo = [](auto continuation) // .then
{
    continuation(1);
    continuation(2);
    continuation(3);
};

auto bar = [](auto continuation) // .then
{
    continuation('a');
    continuation('b');
    continuation('c');
};

struct coroutine : asio::coroutine
{
    int x;
    char y;

    void operator()()
    {
        reenter(*this)
        {
            await(x, foo); // x = await foo;
            await(y, bar); // y = await bar;
            cout << x << " " << y << endl;
        }
    };
};

int main()
{
    coroutine{}();
}
Вывод:
1 a
1 b
1 c
2 a
2 b
2 c
3 a
3 b
3 c
LIVE DEMO on Coliru
Это подобие List Monad, без копирования продолжения так не получится.