SlideShare a Scribd company logo
1 of 73
Стековые/безстековые
      сопрограммы и
           Boost.Asio

          Eugene Kulak
           keu@cilkum.net

      C++ Saturday, Nov 2011
Сопрограммы
Сопрограммы


„ Подпрограммы – это частные случаи более общих программных
компонентов, называемых сопрограммами. В противоположность
нессиметричной связи между главной программой и подпрограммой, между
сопрограммами, которые вызывают одна другую, существует полная
симметрия. “
                                Д.Кнут, «Искусство программирования»
Сопрограммы




подпрограмма

               подпрограмма
Сопрограммы




подпрограмма

               подпрограмма
Сопрограммы




подпрограмма

               подпрограмма
Сопрограммы




сопрограмма

              подпрограмма
Сопрограммы




сопрограмма

              подпрограмма
Сопрограммы




сопрограмма

              подпрограмма
Сопрограммы

Стэк




       А




       В




       С
Сопрограммы




А




В




С
Сопрограммы




А




В




С
Сопрограммы




А




В




С
Сопрограммы




А




В




С
Сопрограммы

Классификация:

    ●   Асимметричные




                        В
        А
Сопрограммы

Классификация:

    ●   Асимметричные
    ●   Симметричные




                        В
        А
                   С
Сопрограммы

Классификация:

    ●   Асимметричные   ●   Без стековые (stackless)
    ●   Симметричные



        А
                                          В
Сопрограммы

Классификация:

    ●   Асимметричные   ●   Без стековые (stackless)
    ●   Симметричные    ●   Стековые (stackfull)



        А
                                           В
Сопрограммы



Зачем?
Сопрограммы



                 Зачем?

●   Генераторы
Сопрограммы



                      Зачем?

●   Генераторы
●   Задачи типа Производитель/Потребитель
Сопрограммы



                      Зачем?

●   Генераторы
●   Задачи типа Производитель/Потребитель
●   Кооперативная многопоточность (Псевдо-
многопоточность в одном потоке)
Параллельная обработка данных


  ●
      Синхронная
Параллельная обработка данных


  ●
      Синхронная
  ●
      Асинхронная
Параллельная обработка данных


  ●
      Синхронная    ●
                        Блокирующий I/O
  ●
      Асинхронная   ●
                        Не блокирующий I/O
Параллельная обработка данных


  ●
      Синхронная    ●
                        Блокирующий I/O
  ●
      Асинхронная   ●
                        Не блокирующий I/O
Параллельная обработка данных




p1 = connect();
read(p1, buff);
p2 = connect();
write(p2, buff);
Параллельная обработка данных



Выполнение остановилось здесь



      p1 = connect();
      read(p1, buff);
      p2 = connect();
      write(p2, buff);
Параллельная обработка данных




p1 = connect();
read(p1, buff);
p2 = connect();
write(p2, buff);



                   потоки
Параллельная обработка данных



                   ✔ Написание программы в синхронном
                     (интуитивном) стиле

p1 = connect();    ✔ Поддежка многоядерных систем
read(p1, buff);
p2 = connect();
write(p2, buff);
Параллельная обработка данных



                   ✔ Написание программы в синхронном
                     (интуитивном) стиле

p1 = connect();    ✔ Поддежка многоядерных систем
read(p1, buff);    ✗ Проблемы синхронизации
p2 = connect();    ✗ Число потоков сильно ограничено
write(p2, buff);
Параллельная обработка данных
 Reactor                   I/O



 while(true)
 {
     e = r.events.dequeue();
     r.dispatch(e);
 }




                               read_h(...)
                               {
                                 read(s,buff)
                               }
r.registerHandler(
                                                connect_h(...)
      events::Read,                             {
      read_h);
                                                }
Параллельная обработка данных
 Reactor                   I/O

                                         ✔ Однопоточность - нет проблем
 while(true)                               синхронизации
 {
                                         ✔ Поддержка новой задачи дешевле
     e = r.events.dequeue();               создания потока
     r.dispatch(e);
 }




                               read_h(...)
                               {
                                 read(s,buff)
                               }
r.registerHandler(
                                                connect_h(...)
      events::Read,                             {
      read_h);
                                                }
Параллельная обработка данных
 Reactor                   I/O

                                         ✔ Однопоточность - нет проблем
 while(true)                               синхронизации
 {
                                         ✔ Поддержка новой задачи дешевле
     e = r.events.dequeue();               создания потока
     r.dispatch(e);
                                         ✗ Большую задачу приходится разбивать на
 }                                         множество мелких.
                                         ✗ Одна задача может повесить всю систему

                               read_h(...)
                               {
                                 read(s,buff)
                               }
r.registerHandler(
                                                connect_h(...)
      events::Read,                             {
      read_h);
                                                }
Параллельная обработка данных
 Proactor                  I/O



 while(true)
 {
     e = r.events.dequeue();
     r.dispatch(e);
 }




                               read_h(...)
                               {

                               }
read(s, read_h, buff);                          connect_h(...)
                                                {

                                                }
Параллельная обработка данных
 Proactor                  I/O

                                        ✔ Однопоточность - нет проблем
 while(true)                                 синхронизации
 {
                                        ✔ Поддержка новой задачи дешевле
     e = r.events.dequeue();                 создания потока
     r.dispatch(e);
                                        ✔ Одновременное выполнение кода и
 }                                           выполнение операций I/O




                               read_h(...)
                               {

                               }
read(s, read_h, buff);                            connect_h(...)
                                                  {

                                                  }
Параллельная обработка данных
 Proactor                  I/O

                                        ✔ Однопоточность - нет проблем
 while(true)                                 синхронизации
 {
                                        ✔ Поддержка новой задачи дешевле
     e = r.events.dequeue();                 создания потока
     r.dispatch(e);
                                        ✔ Одновременное выполнение кода и
 }                                           выполнение операций I/O
                                        ✗ Необходимость управлять буферами

                               read_h(...)
                               {

                               }
read(s, read_h, buff);                            connect_h(...)
                                                  {

                                                  }
Параллельная обработка данных
Параллельная обработка данных
Boost.Asio

„Thinking Asynchronously in C++“
                                   Christopher Kohlhoff
Boost.Asio
Поддерживает:
●   Блокирующий I/O (read/write)
●   Не блокирующий I/O (async_read/async_write)
●   Асинхронную модель Proactor
●   Синхронизацию обработки сообщений (io_service::strand)
●   STL-like потоки (streambuf)
Boost.Asio


asio::io_service io_service;

// …

tcp::socket socket(io_service);

// ...

socket.async_connect(
       server_endpoint,
       your_completion_handler);

// …

io_service.run();
Boost.Asio


asio::io_service io_service;

// …

tcp::socket socket(io_service);

// ...

socket.async_connect(
       server_endpoint,
       your_completion_handler);

// …

io_service.run();
Boost.Asio


asio::io_service io_service;

// …
                                   I/O object
tcp::socket socket(io_service);

// ...

socket.async_connect(
       server_endpoint,
       your_completion_handler);

// …

io_service.run();
Boost.Asio


asio::io_service io_service;

// …

tcp::socket socket(io_service);

// ...
                                   Ассинхронная
                                     операция
socket.async_connect(
       server_endpoint,
       your_completion_handler);

// …

io_service.run();
Boost.Asio


asio::io_service io_service;

// …

tcp::socket socket(io_service);

// ...

socket.async_connect(
       server_endpoint,
       your_completion_handler);

// …

io_service.run();
Boost.Asio


socket.async_connect(
       server_endpoint,
       your_completion_handler);




                      io_service



                                        work
                                       handler

                Операционная система
Boost.Asio




io_service.run();



                     io_service



                                       work
                                      handler

               Операционная система
Boost.Asio




io_service.run();



                     io_service



                                        work
                            notifies
                                       handler

               Операционная система
Boost.Asio




io_service.run();



                     io_service



                                       work
                                      handler

               Операционная система
Boost.Asio




io_service.run();                            result
                                             handler


                     io_service       dequeues



                                                 work
                                             handler

               Операционная система
Boost.Asio


                                  your_completion_handler(ec);


io_service.run();                            result
                                             handler


                     io_service




               Операционная система
Boost.Asio

Еще не видите проблемы?
Boost.Asio

     Еще не видите проблемы? А так?

         your_completion_handler(ec);
                                    your_completion_handler(ec);
your_completion_handler(ec);              your_completion_handler(ec);
                             your_completion_handler(ec);
               your_completion_handler(ec);
                                 your_completion_handler(ec);
              your_completion_handler(ec);              your_completion_handler(ec
  your_completion_handler(ec);             your_completion_handler(ec);
                         your_completion_handler(ec);
 your_completion_handler(ec);          your_completion_handler(ec);
                your_completion_handler(ec);
                                                    your_completion_handler(ec);
    your_completion_handler(ec);      your_completion_handler(ec);
                   your_completion_handler(ec);
   your_completion_handler(ec);
                                             your_completion_handler(ec);
              your_completion_handler(ec);
                                               your_completion_handler(ec);
Boost.Asio

 Асинхронное программирование (в частности паттерны Reactor и
Proactor) требует чтобы задача, пусть и не сложная, была разбита на
несколько подзадач. Иногда такое деление неприемлимо,
трудозатратно и делает логику в целом трудной для понимания.
 Есть ли выход?
 Представим себе алгоритм описанный как синхронный, но
работающий асинхронно. Что-то способное приостанавливать свое
выполнение и продолжать его позже.
 Например – сопрограмма.
Синхронное асинхронно
Синхронное асинхронно

 Введем ключевое слово yield


coroutine server(...)
{
    // ...
    yield socket1.async_connect(
                  server_endpoint,
                  your_completion_handler);
    yield socket1->async_read_some(buffer(*buffer_),*this);
    yield socket2.async_connect(
                  server_endpoint,
                  your_completion_handler);
    yield socket2->async_write_some(buffer(*buffer_), *this);
    // ...
}
Синхронное асинхронно

Реализации сопрограмм для С++
strcpy(to, from, count)                   Безстековые сопрограммы
register char *to, *from;                       (протопотоки)
register count;
{
    register n = (count + 7) / 8;
    if (!count) return;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
    }
}
                             Устройство „Даффа“
Синхронное асинхронно

Реализации сопрограмм для С++
                                                Стековые сопрограммы


 ●   POSIX ucontext;
 ●   Windows fibers (легковесные потоки);
 ●   Платформенно зависимые игры со стеком через asm.
Синхронное асинхронно

Реализации сопрограмм для С++
                                                   Стековые сопрограммы


 ●   POSIX swapcontext;
 ●   Windows fibers (легковесные потоки);
 ●   Платформенно зависимые игры со стеком через asm.



     Существующие:

 ●   Boost.Coroutines       ●   Coro
 ●   Boost.Context          ●   LibCoroutine
                            ●   LibCoro
                            ●   State threads
Синхронное асинхронно

Boost.Coroutines:

 ●   Stackfull сопрограммы
 ●   Cимметричные/асимметричные сопрограммы
 ●   Основа библиотеки – класс coroutine
 ●   Сопрограммы разделены на тело (ф-цию) и состояние
 ●   Не развивается с 2009 года
Синхронное асинхронно
typedef generator<leaf> generator_type;

leaf tree_leaves
 (generator_type::self& self,
  const node_type& node)
{
  if(is_leaf(node)) {
    self.yield(boost::get<leaf_type>(tree));
  } else {
    tree_leaves(self, boost::get<node_type>.first);
    tree_leaves(self, boost::get<node_type>.second);
  }
  self.exit();
}

bool same_fringe(const element& tree1, const element& tree2) {
  generator_type tree_leaves_a(boost::bind(tree_leaves, _1, tree1));
  generator_type tree_leaves_b(boost::bind(tree_leaves, _1, tree2));
  while(tree_leaves_a && tree_leaves_b) {
    if(tree_leaves_a() != tree_leaves_b())
      return false;
  }
  return true && (!tree_leaves_b && !tree_leaves_a);
}
Синхронное асинхронно

void foo(coro::coroutine<void()>::self& self)
{
     typedef boost::asio::ip::tcp::socket socket_type;
     typedef boost::asio::error error_type;

    char token[1024];
    socket_type source;
    coro::future<error_type, std::size_t> read_result(self);
    //...
    asio::async_read(source,
                       boost::asio::buffer(token, 1024),
                       coro::make_callback(read_result));
    //...
    coro::wait(source);
    if(source->get<0>())
    {
         std::cout <<"Errorn!";
    }
    else
    {
         std::cout <<"Written "<<source->get<1>()<<" bytes";
    }
}
Синхронное асинхронно

Boost.Context:

 ●   Stackfull сопрограммы
 ●   Cимметричные/асимметричные сопрограммы
 ●   Основа библиотеки – класс context
 ●   Библиотека общего назначения, на ее основе разрабатывается функционал
     boost.Fibers, „продолжений“ и генераторов (enumeration)
 ●   С весны 2011 года находится на ревью по включению ее в состав Boost.
Синхронное асинхронно
for (;;){
    boost::asio::ip::tcp::socket socket( acceptor_.get_io_service() );
    acceptor_.async_accept( socket, boost::bind( & server::operator(), this->
                                     shared_from_this(), _1, 0) );
    suspend();

    while (!ec_)
    {
        boost::array< char, 1024 > buffer;

        socket.async_read_some(
            boost::asio::buffer( buffer),
            boost::bind( & server::operator(), this->shared_from_this(), _1, _2) );
        suspend();

        if ( ec_) break;

        boost::asio::async_write(
            socket,
            boost::asio::buffer( buffer, n_),
            boost::bind( & server::operator(), this->shared_from_this(), _1, _2) );
        suspend();
    }
}
...
void operator()( boost::system::error_code ec, size_t n){
    ec_ = ec;
    n_ = n;
    resume();
}
Синхронное асинхронно
for (;;){
    boost::asio::ip::tcp::socket socket( acceptor_.get_io_service() );
    acceptor_.async_accept( socket, boost::bind( & server::operator(), this->
                                     shared_from_this(), _1, 0) );
    suspend();

    while (!ec_)
    {
        boost::array< char, 1024 > buffer;

        socket.async_read_some(
            boost::asio::buffer( buffer),
            boost::bind( & server::operator(), this->shared_from_this(), _1, _2) );
        suspend();

        if ( ec_) break;

        boost::asio::async_write(
            socket,
            boost::asio::buffer( buffer, n_),
            boost::bind( & server::operator(), this->shared_from_this(), _1, _2) );
        suspend();
    }
}
...
void operator()( boost::system::error_code ec, size_t n){
    ec_ = ec;
    n_ = n;
    resume();
}
Синхронное асинхронно
for (;;){
    boost::asio::ip::tcp::socket socket( acceptor_.get_io_service() );
    acceptor_.async_accept( socket, boost::bind( & server::operator(), this->
                                     shared_from_this(), _1, 0) );
    suspend();

    while (!ec_)
    {
        boost::array< char, 1024 > buffer;

        socket.async_read_some(
            boost::asio::buffer( buffer),
            boost::bind( & server::operator(), this->shared_from_this(), _1, _2) );
        suspend();

        if ( ec_) break;

        boost::asio::async_write(
            socket,
            boost::asio::buffer( buffer, n_),
            boost::bind( & server::operator(), this->shared_from_this(), _1, _2) );
        suspend();
    }
}
...
void operator()( boost::system::error_code ec, size_t n){
    ec_ = ec;
    n_ = n;
    resume();
}
Синхронное асинхронно
for (;;){
    boost::asio::ip::tcp::socket socket( acceptor_.get_io_service() );
    acceptor_.async_accept( socket, boost::bind( & server::operator(), this->
                                     shared_from_this(), _1, 0) );
    suspend();

    while (!ec_)
    {
        boost::array< char, 1024 > buffer;

        socket.async_read_some(
            boost::asio::buffer( buffer),
            boost::bind( & server::operator(), this->shared_from_this(), _1, _2) );
        suspend();

        if ( ec_) break;

        boost::asio::async_write(
            socket,
            boost::asio::buffer( buffer, n_),
            boost::bind( & server::operator(), this->shared_from_this(), _1, _2) );
        suspend();
    }
}
...
void operator()( boost::system::error_code ec, size_t n){
    ec_ = ec;
    n_ = n;
    resume();
}
Синхронное асинхронно
reenter (this)
{
     do{
          socket_.reset(new tcp::socket(acceptor_->get_io_service()));
          yield acceptor_->async_accept(*socket_, *this);
          fork server(*this)();
     } while (is_parent());

    buffer_.reset(new boost::array<char, 8192>);
    request_.reset(new request);

    do
    {
         yield socket_->async_read_some(boost::asio::buffer(*buffer_), *this);
         boost::tie(valid_request_, boost::tuples::ignore)
                 = request_parser_.parse(*request_,
                    buffer_->data(), buffer_->data() + length);
    } while (boost::indeterminate(valid_request_));

    reply_.reset(new reply);
    if (valid_request_) {
         request_handler_(*request_, *reply_);
    }
    else {
         *reply_ = reply::stock_reply(reply::bad_request);
    }

    yield boost::asio::async_write(*socket_, reply_->to_buffers(), *this);
    socket_->shutdown(tcp::socket::shutdown_both, ec);
}
Сопрограммы:


✔ Сокращение числа обработчиков, упрощение логики;
✔ Сохранение в некоторой мере синхронного стиля написания в
 асинхронной модели;
Сопрограммы:


✔ Сокращение числа обработчиков, упрощение логики;
✔ Сохранение в некоторой мере синхронного стиля написания в
 асинхронной модели;
✗ Отсутствие поддержки в языке;
✗ Сложность реализации;
✗ Подверженность ошибкам;
Подробности:

 https://github.com/olk/boost.context/tree/master/libs/context, Boost.Context

 http://www.crystalclearsoftware.com/soc/coroutine/index.html, Boost.Coroutines

 http://www.boost.org/doc/libs/1_48_0/doc/html/boost_asio.html Boost.Asio

 http://www.inf.puc-rio.br/~roberto/docs/MCC15-04.pdf Revisiting Coroutines

 http://www.kegel.com/c10k.html C10k Problem
Вопросы

More Related Content

Similar to CiklumCPPSat: Eugene Kulak "Stackless/stackfull coroutines and boost.asio"

Machine learning c использованием нейронных сетей, Дмитрий Лапин
Machine learning c использованием нейронных сетей, Дмитрий ЛапинMachine learning c использованием нейронных сетей, Дмитрий Лапин
Machine learning c использованием нейронных сетей, Дмитрий ЛапинIT61
 
WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...
WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...
WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...WebCamp
 
О сложностях программирования, или C# нас не спасет?
О сложностях программирования, или C# нас не спасет?О сложностях программирования, или C# нас не спасет?
О сложностях программирования, или C# нас не спасет?Tatyanazaxarova
 
Лекция 6: Многопоточное программирование: часть 2 (Speedup, Amdahl's law, POS...
Лекция 6: Многопоточное программирование: часть 2 (Speedup, Amdahl's law, POS...Лекция 6: Многопоточное программирование: часть 2 (Speedup, Amdahl's law, POS...
Лекция 6: Многопоточное программирование: часть 2 (Speedup, Amdahl's law, POS...Mikhail Kurnosov
 
Теоретический минимум для понимания Java Memory Model (для JPoint 2014)
Теоретический минимум для понимания Java Memory Model (для JPoint 2014)Теоретический минимум для понимания Java Memory Model (для JPoint 2014)
Теоретический минимум для понимания Java Memory Model (для JPoint 2014)Roman Elizarov
 
20090720 hpc exercise1
20090720 hpc exercise120090720 hpc exercise1
20090720 hpc exercise1Michael Karpov
 
SWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотек
SWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотекSWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотек
SWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотекPython Meetup
 
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Yandex
 

Similar to CiklumCPPSat: Eugene Kulak "Stackless/stackfull coroutines and boost.asio" (8)

Machine learning c использованием нейронных сетей, Дмитрий Лапин
Machine learning c использованием нейронных сетей, Дмитрий ЛапинMachine learning c использованием нейронных сетей, Дмитрий Лапин
Machine learning c использованием нейронных сетей, Дмитрий Лапин
 
WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...
WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...
WebCamp2016:Front-End.Максим Климишин.Теоретические и практические концепции ...
 
О сложностях программирования, или C# нас не спасет?
О сложностях программирования, или C# нас не спасет?О сложностях программирования, или C# нас не спасет?
О сложностях программирования, или C# нас не спасет?
 
Лекция 6: Многопоточное программирование: часть 2 (Speedup, Amdahl's law, POS...
Лекция 6: Многопоточное программирование: часть 2 (Speedup, Amdahl's law, POS...Лекция 6: Многопоточное программирование: часть 2 (Speedup, Amdahl's law, POS...
Лекция 6: Многопоточное программирование: часть 2 (Speedup, Amdahl's law, POS...
 
Теоретический минимум для понимания Java Memory Model (для JPoint 2014)
Теоретический минимум для понимания Java Memory Model (для JPoint 2014)Теоретический минимум для понимания Java Memory Model (для JPoint 2014)
Теоретический минимум для понимания Java Memory Model (для JPoint 2014)
 
20090720 hpc exercise1
20090720 hpc exercise120090720 hpc exercise1
20090720 hpc exercise1
 
SWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотек
SWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотекSWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотек
SWIG — cоздание мультиязыковых интерфейсов для C/C++ библиотек
 
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
 

More from Ciklum Ukraine

"How keep normal blood pressure using TDD" By Roman Loparev
"How keep normal blood pressure using TDD" By Roman Loparev"How keep normal blood pressure using TDD" By Roman Loparev
"How keep normal blood pressure using TDD" By Roman LoparevCiklum Ukraine
 
"Through the three circles of the it hell" by Roman Liashenko
"Through the three circles of the it hell" by Roman Liashenko"Through the three circles of the it hell" by Roman Liashenko
"Through the three circles of the it hell" by Roman LiashenkoCiklum Ukraine
 
Alex Pazhyn: Google_Material_Design
Alex Pazhyn: Google_Material_DesignAlex Pazhyn: Google_Material_Design
Alex Pazhyn: Google_Material_DesignCiklum Ukraine
 
Introduction to amazon web services for developers
Introduction to amazon web services for developersIntroduction to amazon web services for developers
Introduction to amazon web services for developersCiklum Ukraine
 
Your 1st Apple watch Application
Your 1st Apple watch ApplicationYour 1st Apple watch Application
Your 1st Apple watch ApplicationCiklum Ukraine
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven DevelopmentCiklum Ukraine
 
Back to the future: ux trends 2015
Back to the future: ux trends 2015Back to the future: ux trends 2015
Back to the future: ux trends 2015Ciklum Ukraine
 
Developing high load systems using C++
Developing high load systems using C++Developing high load systems using C++
Developing high load systems using C++Ciklum Ukraine
 
Collection view layout
Collection view layoutCollection view layout
Collection view layoutCiklum Ukraine
 
Introduction to auto layout
Introduction to auto layoutIntroduction to auto layout
Introduction to auto layoutCiklum Ukraine
 
Unit Testing: Special Cases
Unit Testing: Special CasesUnit Testing: Special Cases
Unit Testing: Special CasesCiklum Ukraine
 
Model-View-Controller: Tips&Tricks
Model-View-Controller: Tips&TricksModel-View-Controller: Tips&Tricks
Model-View-Controller: Tips&TricksCiklum Ukraine
 
Future of Outsourcing report published in The Times featuring Ciklum's CEO To...
Future of Outsourcing report published in The Times featuring Ciklum's CEO To...Future of Outsourcing report published in The Times featuring Ciklum's CEO To...
Future of Outsourcing report published in The Times featuring Ciklum's CEO To...Ciklum Ukraine
 
Михаил Попчук "Cкрытые резервы команд или 1+1=3"
Михаил Попчук "Cкрытые резервы команд или 1+1=3"Михаил Попчук "Cкрытые резервы команд или 1+1=3"
Михаил Попчук "Cкрытые резервы команд или 1+1=3"Ciklum Ukraine
 

More from Ciklum Ukraine (20)

"How keep normal blood pressure using TDD" By Roman Loparev
"How keep normal blood pressure using TDD" By Roman Loparev"How keep normal blood pressure using TDD" By Roman Loparev
"How keep normal blood pressure using TDD" By Roman Loparev
 
"Through the three circles of the it hell" by Roman Liashenko
"Through the three circles of the it hell" by Roman Liashenko"Through the three circles of the it hell" by Roman Liashenko
"Through the three circles of the it hell" by Roman Liashenko
 
Alex Pazhyn: Google_Material_Design
Alex Pazhyn: Google_Material_DesignAlex Pazhyn: Google_Material_Design
Alex Pazhyn: Google_Material_Design
 
Introduction to amazon web services for developers
Introduction to amazon web services for developersIntroduction to amazon web services for developers
Introduction to amazon web services for developers
 
Your 1st Apple watch Application
Your 1st Apple watch ApplicationYour 1st Apple watch Application
Your 1st Apple watch Application
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven Development
 
Back to the future: ux trends 2015
Back to the future: ux trends 2015Back to the future: ux trends 2015
Back to the future: ux trends 2015
 
Developing high load systems using C++
Developing high load systems using C++Developing high load systems using C++
Developing high load systems using C++
 
Collection view layout
Collection view layoutCollection view layout
Collection view layout
 
Introduction to auto layout
Introduction to auto layoutIntroduction to auto layout
Introduction to auto layout
 
Groovy on Android
Groovy on AndroidGroovy on Android
Groovy on Android
 
Unit Testing: Special Cases
Unit Testing: Special CasesUnit Testing: Special Cases
Unit Testing: Special Cases
 
Material design
Material designMaterial design
Material design
 
Kanban development
Kanban developmentKanban development
Kanban development
 
Mobile sketching
Mobile sketching Mobile sketching
Mobile sketching
 
More UX in our life
More UX in our lifeMore UX in our life
More UX in our life
 
Model-View-Controller: Tips&Tricks
Model-View-Controller: Tips&TricksModel-View-Controller: Tips&Tricks
Model-View-Controller: Tips&Tricks
 
Unit Tesing in iOS
Unit Tesing in iOSUnit Tesing in iOS
Unit Tesing in iOS
 
Future of Outsourcing report published in The Times featuring Ciklum's CEO To...
Future of Outsourcing report published in The Times featuring Ciklum's CEO To...Future of Outsourcing report published in The Times featuring Ciklum's CEO To...
Future of Outsourcing report published in The Times featuring Ciklum's CEO To...
 
Михаил Попчук "Cкрытые резервы команд или 1+1=3"
Михаил Попчук "Cкрытые резервы команд или 1+1=3"Михаил Попчук "Cкрытые резервы команд или 1+1=3"
Михаил Попчук "Cкрытые резервы команд или 1+1=3"
 

CiklumCPPSat: Eugene Kulak "Stackless/stackfull coroutines and boost.asio"

  • 1. Стековые/безстековые сопрограммы и Boost.Asio Eugene Kulak keu@cilkum.net C++ Saturday, Nov 2011
  • 3. Сопрограммы „ Подпрограммы – это частные случаи более общих программных компонентов, называемых сопрограммами. В противоположность нессиметричной связи между главной программой и подпрограммой, между сопрограммами, которые вызывают одна другую, существует полная симметрия. “ Д.Кнут, «Искусство программирования»
  • 15. Сопрограммы Классификация: ● Асимметричные В А
  • 16. Сопрограммы Классификация: ● Асимметричные ● Симметричные В А С
  • 17. Сопрограммы Классификация: ● Асимметричные ● Без стековые (stackless) ● Симметричные А В
  • 18. Сопрограммы Классификация: ● Асимметричные ● Без стековые (stackless) ● Симметричные ● Стековые (stackfull) А В
  • 20. Сопрограммы Зачем? ● Генераторы
  • 21. Сопрограммы Зачем? ● Генераторы ● Задачи типа Производитель/Потребитель
  • 22. Сопрограммы Зачем? ● Генераторы ● Задачи типа Производитель/Потребитель ● Кооперативная многопоточность (Псевдо- многопоточность в одном потоке)
  • 24. Параллельная обработка данных ● Синхронная ● Асинхронная
  • 25. Параллельная обработка данных ● Синхронная ● Блокирующий I/O ● Асинхронная ● Не блокирующий I/O
  • 26. Параллельная обработка данных ● Синхронная ● Блокирующий I/O ● Асинхронная ● Не блокирующий I/O
  • 27. Параллельная обработка данных p1 = connect(); read(p1, buff); p2 = connect(); write(p2, buff);
  • 28. Параллельная обработка данных Выполнение остановилось здесь p1 = connect(); read(p1, buff); p2 = connect(); write(p2, buff);
  • 29. Параллельная обработка данных p1 = connect(); read(p1, buff); p2 = connect(); write(p2, buff); потоки
  • 30. Параллельная обработка данных ✔ Написание программы в синхронном (интуитивном) стиле p1 = connect(); ✔ Поддежка многоядерных систем read(p1, buff); p2 = connect(); write(p2, buff);
  • 31. Параллельная обработка данных ✔ Написание программы в синхронном (интуитивном) стиле p1 = connect(); ✔ Поддежка многоядерных систем read(p1, buff); ✗ Проблемы синхронизации p2 = connect(); ✗ Число потоков сильно ограничено write(p2, buff);
  • 32. Параллельная обработка данных Reactor I/O while(true) { e = r.events.dequeue(); r.dispatch(e); } read_h(...) { read(s,buff) } r.registerHandler( connect_h(...) events::Read, { read_h); }
  • 33. Параллельная обработка данных Reactor I/O ✔ Однопоточность - нет проблем while(true) синхронизации { ✔ Поддержка новой задачи дешевле e = r.events.dequeue(); создания потока r.dispatch(e); } read_h(...) { read(s,buff) } r.registerHandler( connect_h(...) events::Read, { read_h); }
  • 34. Параллельная обработка данных Reactor I/O ✔ Однопоточность - нет проблем while(true) синхронизации { ✔ Поддержка новой задачи дешевле e = r.events.dequeue(); создания потока r.dispatch(e); ✗ Большую задачу приходится разбивать на } множество мелких. ✗ Одна задача может повесить всю систему read_h(...) { read(s,buff) } r.registerHandler( connect_h(...) events::Read, { read_h); }
  • 35. Параллельная обработка данных Proactor I/O while(true) { e = r.events.dequeue(); r.dispatch(e); } read_h(...) { } read(s, read_h, buff); connect_h(...) { }
  • 36. Параллельная обработка данных Proactor I/O ✔ Однопоточность - нет проблем while(true) синхронизации { ✔ Поддержка новой задачи дешевле e = r.events.dequeue(); создания потока r.dispatch(e); ✔ Одновременное выполнение кода и } выполнение операций I/O read_h(...) { } read(s, read_h, buff); connect_h(...) { }
  • 37. Параллельная обработка данных Proactor I/O ✔ Однопоточность - нет проблем while(true) синхронизации { ✔ Поддержка новой задачи дешевле e = r.events.dequeue(); создания потока r.dispatch(e); ✔ Одновременное выполнение кода и } выполнение операций I/O ✗ Необходимость управлять буферами read_h(...) { } read(s, read_h, buff); connect_h(...) { }
  • 40. Boost.Asio „Thinking Asynchronously in C++“ Christopher Kohlhoff
  • 41. Boost.Asio Поддерживает: ● Блокирующий I/O (read/write) ● Не блокирующий I/O (async_read/async_write) ● Асинхронную модель Proactor ● Синхронизацию обработки сообщений (io_service::strand) ● STL-like потоки (streambuf)
  • 42. Boost.Asio asio::io_service io_service; // … tcp::socket socket(io_service); // ... socket.async_connect( server_endpoint, your_completion_handler); // … io_service.run();
  • 43. Boost.Asio asio::io_service io_service; // … tcp::socket socket(io_service); // ... socket.async_connect( server_endpoint, your_completion_handler); // … io_service.run();
  • 44. Boost.Asio asio::io_service io_service; // … I/O object tcp::socket socket(io_service); // ... socket.async_connect( server_endpoint, your_completion_handler); // … io_service.run();
  • 45. Boost.Asio asio::io_service io_service; // … tcp::socket socket(io_service); // ... Ассинхронная операция socket.async_connect( server_endpoint, your_completion_handler); // … io_service.run();
  • 46. Boost.Asio asio::io_service io_service; // … tcp::socket socket(io_service); // ... socket.async_connect( server_endpoint, your_completion_handler); // … io_service.run();
  • 47. Boost.Asio socket.async_connect( server_endpoint, your_completion_handler); io_service work handler Операционная система
  • 48. Boost.Asio io_service.run(); io_service work handler Операционная система
  • 49. Boost.Asio io_service.run(); io_service work notifies handler Операционная система
  • 50. Boost.Asio io_service.run(); io_service work handler Операционная система
  • 51. Boost.Asio io_service.run(); result handler io_service dequeues work handler Операционная система
  • 52. Boost.Asio your_completion_handler(ec); io_service.run(); result handler io_service Операционная система
  • 54. Boost.Asio Еще не видите проблемы? А так? your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec); your_completion_handler(ec);
  • 55. Boost.Asio Асинхронное программирование (в частности паттерны Reactor и Proactor) требует чтобы задача, пусть и не сложная, была разбита на несколько подзадач. Иногда такое деление неприемлимо, трудозатратно и делает логику в целом трудной для понимания. Есть ли выход? Представим себе алгоритм описанный как синхронный, но работающий асинхронно. Что-то способное приостанавливать свое выполнение и продолжать его позже. Например – сопрограмма.
  • 57. Синхронное асинхронно Введем ключевое слово yield coroutine server(...) { // ... yield socket1.async_connect( server_endpoint, your_completion_handler); yield socket1->async_read_some(buffer(*buffer_),*this); yield socket2.async_connect( server_endpoint, your_completion_handler); yield socket2->async_write_some(buffer(*buffer_), *this); // ... }
  • 58. Синхронное асинхронно Реализации сопрограмм для С++ strcpy(to, from, count) Безстековые сопрограммы register char *to, *from; (протопотоки) register count; { register n = (count + 7) / 8; if (!count) return; switch (count % 8) { case 0: do { *to = *from++; case 7: *to = *from++; case 6: *to = *from++; case 5: *to = *from++; case 4: *to = *from++; case 3: *to = *from++; case 2: *to = *from++; case 1: *to = *from++; } while (--n > 0); } } Устройство „Даффа“
  • 59. Синхронное асинхронно Реализации сопрограмм для С++ Стековые сопрограммы ● POSIX ucontext; ● Windows fibers (легковесные потоки); ● Платформенно зависимые игры со стеком через asm.
  • 60. Синхронное асинхронно Реализации сопрограмм для С++ Стековые сопрограммы ● POSIX swapcontext; ● Windows fibers (легковесные потоки); ● Платформенно зависимые игры со стеком через asm. Существующие: ● Boost.Coroutines ● Coro ● Boost.Context ● LibCoroutine ● LibCoro ● State threads
  • 61. Синхронное асинхронно Boost.Coroutines: ● Stackfull сопрограммы ● Cимметричные/асимметричные сопрограммы ● Основа библиотеки – класс coroutine ● Сопрограммы разделены на тело (ф-цию) и состояние ● Не развивается с 2009 года
  • 62. Синхронное асинхронно typedef generator<leaf> generator_type; leaf tree_leaves (generator_type::self& self, const node_type& node) { if(is_leaf(node)) { self.yield(boost::get<leaf_type>(tree)); } else { tree_leaves(self, boost::get<node_type>.first); tree_leaves(self, boost::get<node_type>.second); } self.exit(); } bool same_fringe(const element& tree1, const element& tree2) { generator_type tree_leaves_a(boost::bind(tree_leaves, _1, tree1)); generator_type tree_leaves_b(boost::bind(tree_leaves, _1, tree2)); while(tree_leaves_a && tree_leaves_b) { if(tree_leaves_a() != tree_leaves_b()) return false; } return true && (!tree_leaves_b && !tree_leaves_a); }
  • 63. Синхронное асинхронно void foo(coro::coroutine<void()>::self& self) { typedef boost::asio::ip::tcp::socket socket_type; typedef boost::asio::error error_type; char token[1024]; socket_type source; coro::future<error_type, std::size_t> read_result(self); //... asio::async_read(source, boost::asio::buffer(token, 1024), coro::make_callback(read_result)); //... coro::wait(source); if(source->get<0>()) { std::cout <<"Errorn!"; } else { std::cout <<"Written "<<source->get<1>()<<" bytes"; } }
  • 64. Синхронное асинхронно Boost.Context: ● Stackfull сопрограммы ● Cимметричные/асимметричные сопрограммы ● Основа библиотеки – класс context ● Библиотека общего назначения, на ее основе разрабатывается функционал boost.Fibers, „продолжений“ и генераторов (enumeration) ● С весны 2011 года находится на ревью по включению ее в состав Boost.
  • 65. Синхронное асинхронно for (;;){ boost::asio::ip::tcp::socket socket( acceptor_.get_io_service() ); acceptor_.async_accept( socket, boost::bind( & server::operator(), this-> shared_from_this(), _1, 0) ); suspend(); while (!ec_) { boost::array< char, 1024 > buffer; socket.async_read_some( boost::asio::buffer( buffer), boost::bind( & server::operator(), this->shared_from_this(), _1, _2) ); suspend(); if ( ec_) break; boost::asio::async_write( socket, boost::asio::buffer( buffer, n_), boost::bind( & server::operator(), this->shared_from_this(), _1, _2) ); suspend(); } } ... void operator()( boost::system::error_code ec, size_t n){ ec_ = ec; n_ = n; resume(); }
  • 66. Синхронное асинхронно for (;;){ boost::asio::ip::tcp::socket socket( acceptor_.get_io_service() ); acceptor_.async_accept( socket, boost::bind( & server::operator(), this-> shared_from_this(), _1, 0) ); suspend(); while (!ec_) { boost::array< char, 1024 > buffer; socket.async_read_some( boost::asio::buffer( buffer), boost::bind( & server::operator(), this->shared_from_this(), _1, _2) ); suspend(); if ( ec_) break; boost::asio::async_write( socket, boost::asio::buffer( buffer, n_), boost::bind( & server::operator(), this->shared_from_this(), _1, _2) ); suspend(); } } ... void operator()( boost::system::error_code ec, size_t n){ ec_ = ec; n_ = n; resume(); }
  • 67. Синхронное асинхронно for (;;){ boost::asio::ip::tcp::socket socket( acceptor_.get_io_service() ); acceptor_.async_accept( socket, boost::bind( & server::operator(), this-> shared_from_this(), _1, 0) ); suspend(); while (!ec_) { boost::array< char, 1024 > buffer; socket.async_read_some( boost::asio::buffer( buffer), boost::bind( & server::operator(), this->shared_from_this(), _1, _2) ); suspend(); if ( ec_) break; boost::asio::async_write( socket, boost::asio::buffer( buffer, n_), boost::bind( & server::operator(), this->shared_from_this(), _1, _2) ); suspend(); } } ... void operator()( boost::system::error_code ec, size_t n){ ec_ = ec; n_ = n; resume(); }
  • 68. Синхронное асинхронно for (;;){ boost::asio::ip::tcp::socket socket( acceptor_.get_io_service() ); acceptor_.async_accept( socket, boost::bind( & server::operator(), this-> shared_from_this(), _1, 0) ); suspend(); while (!ec_) { boost::array< char, 1024 > buffer; socket.async_read_some( boost::asio::buffer( buffer), boost::bind( & server::operator(), this->shared_from_this(), _1, _2) ); suspend(); if ( ec_) break; boost::asio::async_write( socket, boost::asio::buffer( buffer, n_), boost::bind( & server::operator(), this->shared_from_this(), _1, _2) ); suspend(); } } ... void operator()( boost::system::error_code ec, size_t n){ ec_ = ec; n_ = n; resume(); }
  • 69. Синхронное асинхронно reenter (this) { do{ socket_.reset(new tcp::socket(acceptor_->get_io_service())); yield acceptor_->async_accept(*socket_, *this); fork server(*this)(); } while (is_parent()); buffer_.reset(new boost::array<char, 8192>); request_.reset(new request); do { yield socket_->async_read_some(boost::asio::buffer(*buffer_), *this); boost::tie(valid_request_, boost::tuples::ignore) = request_parser_.parse(*request_, buffer_->data(), buffer_->data() + length); } while (boost::indeterminate(valid_request_)); reply_.reset(new reply); if (valid_request_) { request_handler_(*request_, *reply_); } else { *reply_ = reply::stock_reply(reply::bad_request); } yield boost::asio::async_write(*socket_, reply_->to_buffers(), *this); socket_->shutdown(tcp::socket::shutdown_both, ec); }
  • 70. Сопрограммы: ✔ Сокращение числа обработчиков, упрощение логики; ✔ Сохранение в некоторой мере синхронного стиля написания в асинхронной модели;
  • 71. Сопрограммы: ✔ Сокращение числа обработчиков, упрощение логики; ✔ Сохранение в некоторой мере синхронного стиля написания в асинхронной модели; ✗ Отсутствие поддержки в языке; ✗ Сложность реализации; ✗ Подверженность ошибкам;
  • 72. Подробности: https://github.com/olk/boost.context/tree/master/libs/context, Boost.Context http://www.crystalclearsoftware.com/soc/coroutine/index.html, Boost.Coroutines http://www.boost.org/doc/libs/1_48_0/doc/html/boost_asio.html Boost.Asio http://www.inf.puc-rio.br/~roberto/docs/MCC15-04.pdf Revisiting Coroutines http://www.kegel.com/c10k.html C10k Problem