performance: boost::asio vs sync sockets
От: Аноним  
Дата: 13.01.14 18:11
Оценка: -1
Есть простой сервер на winsock, который был реализован в спешке на коленке, где на каждое соединение создается отдельный поток, все вызовы, естественно, блокирующие. Сейчас пытаюсь как-то все это дело оптимизировать, решил попоробовать использовать boost.asio, но возникли проблемы на одном из тестовых сценариев.

Итак, реализовал сервер на основе одного из бустовских примеров. Создаю пул потоков, в каждом из потоков вызываю io_service->run(), перед этим создаю acceptor, вызываю async_accept(). Здесь проблем нет, все соединения приходят, все ОК. После создания соединения схема простая:
делаю один вызов async_read(), на который вызывается handle_read(). Читаю из handle_read() данные с помощью socket->read_some(), если неполные данные вызываю опять async_read() один раз, если полные — вызываю async_send() и отправляю ответ клиенту, в handle_write() — обработчике, снова вызываю async_read(). И так процесс продолжается N-раз, потом соединение закрывается. То есть схема взаимодейтсвия абсолютно стандартная:
client-(connect)->server-(connection established)->client-(request)->server-(response)->client->...

В принципе, все работает, но не устраивает производительность в некоторых сценариях.
Итак, сценарий такой: запускаю одновременно 10 клиентов на другой машине, каждый клиент шлет 50000 сообщений размером в 2Кбайт, сервер отвечает на каждое сообщение таким же по размеру сообщением. Сеть 1Гигабит. Так вот, старый сервер отрабатывает где-то за 25 секунд, а бустовский в районе 40. А если запустить все это на одном компе, то там вообще старая реализация рвет бустовскую как тузик грелку. При этом, если увеличивать размер сообщения, то ситуация выравнивается. Профайлер особо ничего не дал. Точнее, что дал, то уже пофиксил. Сейчас 50% — буствоский код, 50% — мой обработчик.
Псмотрел в performance monitor, там вот такая хрень:


Для старой реализации все ОК:


Собственно вопрос в том, почему может быть такой высокий уровень CPU interrupts и как вообще boost.asio "дружит" с большим кол-вом небольших сообщений, реально ли добиться более высокой производительности?
Re: performance: boost::asio vs sync sockets
От: smeeld  
Дата: 13.01.14 19:34
Оценка:
Здравствуйте, Аноним, Вы писали:

У меня тоже свой http сервис на C/epoll тоже рвёт http сервис на boost::asio, и тоже в районе 3:2.
К тому же для функционирования boost::asio при 10K/ps пришлось добавлять размер стека, иначе прога сегфолтилась.
Не пойму только зачем изначально создавать пул потоков, почему не создавать их по мере появления соединений.
И непонятно зачем для каждого потока свой io_service поток, у меня увеличения их количества более одного
производительность не повышала, повышалась только загрузка процессора.
Re[2]: performance: boost::asio vs sync sockets
От: Аноним  
Дата: 14.01.14 00:29
Оценка:
Здравствуйте, smeeld, Вы писали:

S>Здравствуйте, Аноним, Вы писали:


S>У меня тоже свой http сервис на C/epoll тоже рвёт http сервис на boost::asio, и тоже в районе 3:2.

S>К тому же для функционирования boost::asio при 10K/ps пришлось добавлять размер стека, иначе прога сегфолтилась.
S>Не пойму только зачем изначально создавать пул потоков, почему не создавать их по мере появления соединений.
Да можно, просто сейчас это не особо принципиально.
S>И непонятно зачем для каждого потока свой io_service поток, у меня увеличения их количества более одного
S>производительность не повышала, повышалась только загрузка процессора.
Не, у меня один io_service на весь пул, но толку тоже не много. Кроме того, это синтетический тест, в реале будет гораздо
больше клиентов, но интенсивность каждого ниже, поэтому результаты будут другими, но все равно хотелось бы сравнимых результатов со старой реализацией, тем более, что там парсинг гораздо менее эффективен. А главное, не понятно почему так много хардварных прерываний в сравнении со старой реализацией — 54тыс в секунду против 3тыс в секунду.
Re: performance: boost::asio vs sync sockets
От: kaa.python Ниоткуда РСДН профессионально мёртв и завален ватой.
Дата: 14.01.14 05:07
Оценка: +1
По моим ощущениям, использовать ASIO для чего-то серьезного не вариант. Он крайне "overarchitectured", как и 50% BOOST, впрочем, и для высоких нагрузок не очень подходит. Лучше посмотреть на в сторону libuv, если хочется чего-то легкого или ACE, если хочется простой но мощный "всемогутор".
Re[3]: performance: boost::asio vs sync sockets
От: SkyDance Земля  
Дата: 14.01.14 05:35
Оценка:
А>А главное, не понятно почему так много хардварных прерываний в сравнении со старой реализацией — 54тыс в секунду против 3тыс в секунду.

Надо смотреть ваш код. Что за прерывания и откуда они берутся. Вероятно, где-то ошибка в вызовах, например, чтение/запись из/в сокета, который еще не готов.
Re[4]: performance: boost::asio vs sync sockets
От: Аноним  
Дата: 14.01.14 09:04
Оценка:
Здравствуйте, SkyDance, Вы писали:

А>>А главное, не понятно почему так много хардварных прерываний в сравнении со старой реализацией — 54тыс в секунду против 3тыс в секунду.


SD>Надо смотреть ваш код. Что за прерывания и откуда они берутся. Вероятно, где-то ошибка в вызовах, например, чтение/запись из/в сокета, который еще не готов.

На уровне моего кода и на уровне буста никаких ошибок не видно, все проходит прозрачно, отрабатывает "как должно быть", все данные отсылаются/принимаются, как правило, за один вызов, никаких исключений нет (не путать с прерываниями). Код записи такой:
(буфер хранится на уровне инстанса класса, state — POD тип, пуляется по значению в handle_read)
void Connection::async_read(const ReadingState &state)
{
    try
    {
       ...
        char *buf = &this->recv_buf[state.received_data_size];
        size_t buf_size = this->recv_buf.size() - state.received_data_size;

        this->socket->async_receive(
            boost::asio::buffer(buf, buf_size),
            boost::bind(
                &Connection::handle_read, 
                this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred,
                state));
    }
    catch (const std::exception &e)
    {
        // close connection and log err
    }
    catch (...)
    {
       // close connection and log err
    }
}

void Connection::handle_read(const boost::system::error_code &err, size_t bytes_transferred, ReadingState state)
{
    ...

    if (err)
    {
        // close connection, log err, and signal to server
        return;
    }

    ...

    state.received_data_size += bytes_transferred;

    if (state.received_data_size < state.msg_size)
    {
        this->async_read(state);
        return;
    }

    try
    {
        // process message and call message handler
        ...

        this->async_write(response);
    }
    catch ()
    {
     ...
    }


Статистику по прерываниям я вижу только по логам perfmon'a (см картинки в первом сообщении) и, как я понимаю, генерируются они сетевой картой и их высоких уровень под нагрузкой — это тоже нормально. Я просто не понимаю, почему такая большая разница между двумя реализациями на одном и том же входном трафике.
Re[2]: performance: boost::asio vs sync sockets
От: Аноним  
Дата: 14.01.14 09:32
Оценка:
Здравствуйте, kaa.python, Вы писали:

KP>По моим ощущениям, использовать ASIO для чего-то серьезного не вариант. Он крайне "overarchitectured", как и 50% BOOST, впрочем, и для высоких нагрузок не очень подходит. Лучше посмотреть на в сторону libuv, если хочется чего-то легкого или ACE, если хочется простой но мощный "всемогутор".

ОК, libav посмотрю, но склоняюсь к мысли написать сетевую часть на голом API, там я хоть смогу контролировать весь код. Заодно и попробую новые плюшки от Microsoft: What's New for Windows Sockets.
Re: performance: boost::asio vs sync sockets
От: Evgeny.Panasyuk Россия  
Дата: 14.01.14 11:22
Оценка:
Здравствуйте, Аноним, Вы писали:

А>делаю один вызов async_read(), на который вызывается handle_read(). Читаю из handle_read() данные с помощью socket->read_some(), если неполные данные вызываю опять async_read() один раз, если полные — вызываю async_send() и отправляю ответ клиенту, в handle_write() — обработчике, снова вызываю async_read().


Это опечатка? read_some — блокирующий.
Re[2]: performance: boost::asio vs sync sockets
От: Evgeny.Panasyuk Россия  
Дата: 14.01.14 11:25
Оценка: +1
Здравствуйте, kaa.python, Вы писали:

KP>По моим ощущениям, использовать ASIO для чего-то серьезного не вариант. Он крайне "overarchitectured", как и 50% BOOST,


Где же Asio "overarchitectured"? Достаточно простой и неинтрузивный интерфейс.

KP>впрочем, и для высоких нагрузок не очень подходит.


Почему? Есть какие-то принципиальные моменты?
Re: performance: boost::asio vs sync sockets
От: Evgeny.Panasyuk Россия  
Дата: 14.01.14 11:30
Оценка:
Здравствуйте, Аноним, Вы писали:

А>client-(connect)->server-(connection established)->client-(request)->server-(response)->client->...


Кстати, а asio_handler_allocate/asio_handler_deallocate как делается?
Re[3]: performance: boost::asio vs sync sockets
От: kaa.python Ниоткуда РСДН профессионально мёртв и завален ватой.
Дата: 14.01.14 11:34
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Где же Asio "overarchitectured"? Достаточно простой и неинтрузивный интерфейс.


Видимо, вернее не видимо, а однозначно судя по твоим постам в C++, у нас разное понимание overarchitectured.

KP>>впрочем, и для высоких нагрузок не очень подходит.

EP>Почему? Есть какие-то принципиальные моменты?

Есть. 1) Overarchitectured и как следствие малопригоден для исправления ошибок в типичной разнородной команде. 2) На больших нагрузках начинают возникать странности с поведением (точнее начинал, когда в последний раз им пользовался). В то же время на чем-то небольшом и не нагруженном довольно уместен.
Re[2]: performance: boost::asio vs sync sockets
От: Аноним  
Дата: 14.01.14 11:43
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, Аноним, Вы писали:


А>>делаю один вызов async_read(), на который вызывается handle_read(). Читаю из handle_read() данные с помощью socket->read_some(), если неполные данные вызываю опять async_read() один раз, если полные — вызываю async_send() и отправляю ответ клиенту, в handle_write() — обработчике, снова вызываю async_read().


EP>Это опечатка? read_some — блокирующий.

Да, описка, read_some на клиенте используется. На сервере async_receive. Я привел немного кода здесь
Автор:
Дата: 14.01.14
.
Re[4]: performance: boost::asio vs sync sockets
От: Evgeny.Panasyuk Россия  
Дата: 14.01.14 11:51
Оценка:
Здравствуйте, kaa.python, Вы писали:

EP>>Где же Asio "overarchitectured"? Достаточно простой и неинтрузивный интерфейс.

KP>Видимо, вернее не видимо, а однозначно судя по твоим постам в C++, у нас разное понимание overarchitectured.

Всё же интересно где именно он "overarchitectured".
Что конкретно не нравится, как это сделать по-другому, в каких местах есть лишняя "архитектура"?

KP>На больших нагрузках начинают возникать странности с поведением (точнее начинал, когда в последний раз им пользовался).


Такие места нужно выявлять и отправлять в trac.
Re[2]: performance: boost::asio vs sync sockets
От: Аноним  
Дата: 14.01.14 11:55
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, Аноним, Вы писали:


А>>client-(connect)->server-(connection established)->client-(request)->server-(response)->client->...


EP>Кстати, а asio_handler_allocate/asio_handler_deallocate как делается?

Никак, сейчас поискал, я так понимаю, что на каждый чих (вызов async_write/async_send) вызываются по-умолчанию new/delete для хранения объекта обработчика, right? Видимо, тогда имеет смысл что-то кастомное сделать.
Re[4]: performance: boost::asio vs sync sockets
От: Abyx Россия  
Дата: 14.01.14 12:00
Оценка:
Здравствуйте, kaa.python, Вы писали:

KP>Overarchitectured и как следствие малопригоден для исправления ошибок в типичной разнородной команде.

не понял, asio малопригоден для исправления ошибок? это как?
In Zen We Trust
Re[3]: performance: boost::asio vs sync sockets
От: Evgeny.Panasyuk Россия  
Дата: 14.01.14 12:04
Оценка:
Здравствуйте, Аноним, Вы писали:

А>>>client-(connect)->server-(connection established)->client-(request)->server-(response)->client->...

EP>>Кстати, а asio_handler_allocate/asio_handler_deallocate как делается?
А>Никак, сейчас поискал, я так понимаю, что на каждый чих (вызов async_write/async_send) вызываются по-умолчанию new/delete для хранения объекта обработчика, right?

Да, правильно.

А>Видимо, тогда имеет смысл что-то кастомное сделать.


Вот тут пример.
Re[5]: performance: boost::asio vs sync sockets
От: SkyDance Земля  
Дата: 14.01.14 22:37
Оценка: +1
А>(буфер хранится на уровне инстанса класса, state — POD тип, пуляется по значению в handle_read)

А> char *buf = &this->recv_buf[state.received_data_size];

А> size_t buf_size = this->recv_buf.size() — state.received_data_size;

Какой размер буфера? Он растёт до нужного размера? Есть подозрение, что из сокета не вычитывается весь поток, из-за чего asio сигналит постоянно (поди и загрузка CPU 100% по всем ядрам).
Еще я не заметил синхронизации. У вас один io_service на все потоки. Хендлеры надо оборачивать в strand, иначе там битва за сокеты случается. С непредсказуемым поведением и 100% загрузкой процессора.

А> catch (...)

А> {

Это, конечно же, опечатка, так? Или вы в самом деле ловите (...)? И что, туда что-то падает? ЧТО?!

А> if (state.received_data_size < state.msg_size)

А> {
this->>async_read(state);
А> return;
А> }

Вы читаете протокол вида "frame_size->frame_body"? Попробуйте переписать по такому принципу:

// Start the first asynchronous operation for the connection.
void Connection::start()
{
    <... ssl code removed...>
    // start reading now
    socket_.async_read_some(boost::asio::buffer(&frame_size_, sizeof(frame_size_)),
            strand_.wrap(std::bind(&Connection::handle_read_frame_size, shared_from_this(),
        std::placeholders::_1, std::placeholders::_2)));
}

void Connection::handle_read_frame_size(const boost::system::error_code& _ec,
            std::size_t _bytes)
{
    if (!_ec && _bytes == sizeof(frame_size_))
    {
                // allocate storage for frame body using frame_size_
                <...>
        socket_.async_read_some(boost::asio::buffer(frame_body_, _bytes),
            strand_.wrap(std::bind(&Connection::handle_read_frame_body, shared_from_this(),
            std::placeholders::_1, std::placeholders::_2)));
    }else
    {
        connection_manager_.stop(shared_from_this());
    }
}

void Connection::handle_read_frame_body(const boost::system::error_code& _ec,
            std::size_t _bytes)
{
    if (!_ec)
    {
        if (_bytes != frame_size_)
            boost::throw_exception(ConnectionError());

        // parse complete frame and form response
        std::string response = session_.handle((const char*)frame_body_.data(), frame_size_);

        // send response
        auto self(shared_from_this());
        boost::asio::async_write(socket_, boost::asio::buffer(response.c_str(), response.size()),
            [this, self](boost::system::error_code _ec, std::size_t)
            {
                // if connection is to terminate (or already broken), drop it now
                if ( _ec)
                    connection_manager_.stop(self);
            }
    );
    }else
    {
        connection_manager_.stop(shared_from_this());
    }
}


Это из реального проекта с нагрузкой огого, правда, не на Windows, но должно и там работать. Лямбды можно заменить на методы класса + bind.

А>Статистику по прерываниям я вижу только по логам perfmon'a (см картинки в первом сообщении) и, как я понимаю, генерируются они сетевой картой и их высоких уровень под нагрузкой — это тоже нормально. Я просто не понимаю, почему такая большая разница между двумя реализациями на одном и том же входном трафике.


В том и дело, что такая разница в количестве прерываний может говорить о двух вероятных проблемах: либо где-то в тесты вкралась ошибка (например, asio-вариант тестируется с на порядок большей нагрузкой), либо где-то есть лишние вызовы системных функций, проваливающихся в ядро. Что случается, например, при некорректном размере буфера, когда пришедший большой фрейм читается не за один раз целиком, а 100 раз по 10 байт.
Re[6]: performance: boost::asio vs sync sockets
От: SkyDance Земля  
Дата: 14.01.14 23:06
Оценка:
SD> boost::asio::async_write(socket_, boost::asio::buffer(response.c_str(), response.size()),
SD> [this, self](boost::system::error_code _ec, std::size_t)

Прошу прощения, здесь, конечно же, тоже через strand. В процессе выкусывания кода из проекта (response там отправляется worker threads асинхронно через post) и убирания лишних слоёв сам собой выкусился и strand. Упражнение — добавьте самостоятельно, не промахнитесь.
Re: performance: boost::asio vs sync sockets
От: Аноним  
Дата: 14.01.14 23:40
Оценка: 19 (4)
Здравствуйте, Аноним, Вы писали:

А>Собственно вопрос в том, почему может быть такой высокий уровень CPU interrupts и как вообще boost.asio "дружит" с большим кол-вом небольших сообщений, реально ли добиться более высокой производительности?


Всё, всем спасибо, проблема решена. Затык был в конфигурации сетевой карты, а именно в установленной опции Receive Side Scaling Queues, которая была равна 1. Что по сути было эквивалентно выключенной опции Receive Side Scaling (RSS), соответственно обработка трафика сетевой картой не скейлилась по ядрам. В случае же с блокирующим вызовом, похоже, что драйвер использовал то ядро, на котором был блокирующий вызов, поэтому там производительность была выше.
Re[6]: performance: boost::asio vs sync sockets
От: Аноним  
Дата: 15.01.14 00:01
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>Какой размер буфера? Он растёт до нужного размера? Есть подозрение, что из сокета не вычитывается весь поток, из-за чего asio сигналит постоянно (поди и загрузка CPU 100% по всем ядрам).

Да не, ради таких детских ошибок я бы суда не писал. Загрузка проценссора как раз низкая, кроме одного ядра. На самом деле проблема решена, я там написал ниже, дело не в реализации.

SD>Еще я не заметил синхронизации. У вас один io_service на все потоки. Хендлеры надо оборачивать в strand, иначе там битва за сокеты случается. С непредсказуемым поведением и 100% загрузкой процессора.

За какие сокеты битва? У меня handler на каждый инстанс, то есть один сокет -> один хэндлер. Оборачивать его нужно, как я понимаю, для защиты своих данных, но у меня там стоит гард на критической секции (в код не попало), так что не вижу никакого смысла еще и strand использовать.
А>> catch (...)
А>> {

SD>Это, конечно же, опечатка, так? Или вы в самом деле ловите (...)? И что, туда что-то падает? ЧТО?!

В чем проблема то? В винде, кстати, catch(...) может ловить и SEH.


SD>Это из реального проекта с нагрузкой огого, правда, не на Windows, но должно и там работать. Лямбды можно заменить на методы класса + bind.

ваша реализация всегда требует четырех системных вызовов, моя, на маленьких сообщениях, двух (как правило так и происходит на сообщениях в 2Кб). Так что не вижу причины переделывать. Переделывайте сами.
Re[2]: performance: boost::asio vs sync sockets
От: Evgeny.Panasyuk Россия  
Дата: 15.01.14 00:18
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Всё, всем спасибо, проблема решена.


Какая сейчас длительность отработки?
Кстати, а может лучше io_service per-thread?
Re[7]: performance: boost::asio vs sync sockets
От: SkyDance Земля  
Дата: 15.01.14 00:55
Оценка:
А>За какие сокеты битва? У меня handler на каждый инстанс, то есть один сокет -> один хэндлер.

В коде этого нет. Я же неспроста просил код.

А>В чем проблема то? В винде, кстати, catch(...) может ловить и SEH.


Для чего вы ловите SEH в этом месте? Что именно у вас там было поймано?

А>ваша реализация всегда требует четырех системных вызовов


Трех — запись идет целиком, без разделения на size/body.
В вашем варианте будет заметно усложнен парсер потока. В частности, придется применять кольцевой или плавающий буфер для хранения полученного не целиком фрейма. Заодно испытаете восторг с необходимостью масштабирования receive buffer в пользовательском коде. А если будете вычитывать все в свой буфер, рано или поздно доберетесь до реализации TCP congestion control. Ваше право, конечно.
Re[3]: performance: boost::asio vs sync sockets
От: Аноним  
Дата: 15.01.14 12:07
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, Аноним, Вы писали:


А>>Всё, всем спасибо, проблема решена.


EP>Какая сейчас длительность отработки?

На описанном тесте порядка 20секунд (25 тыс запросов в сек). Старый вариант на том же тесте обрабатывал 20 тыс запросов. Может позже еще пооптимизирую, но на данный момент этого достаточно.
EP>Кстати, а может лучше io_service per-thread?
Не уверен. В windows неплохой планировщик потоков, а здесь мне придется часть работы по планированию дублировать, и я сильно сомневаюсь, что у меня получится лучше, чем у стандартного планировщика ОС.
Re[8]: performance: boost::asio vs sync sockets
От: Аноним  
Дата: 15.01.14 12:14
Оценка:
Здравствуйте, SkyDance, Вы писали:

А>>За какие сокеты битва? У меня handler на каждый инстанс, то есть один сокет -> один хэндлер.


SD>В коде этого нет. Я же неспроста просил код.


А>>В чем проблема то? В винде, кстати, catch(...) может ловить и SEH.


SD>Для чего вы ловите SEH в этом месте? Что именно у вас там было поймано?

Ничего, ествественно, иначе бы это означало ошибку в коде, которую бы я сразу пофиксил. Просто не хочу, чтобы что-то ушло за пределы моего хендлера в бустовский код. Мне проще поймать все здесь, дропнуть соединение и залогировать ошибку, при этом сохранив все остальные соединения. Если я буду ловить на уровне потока, то, например, как я узнаю, какое соединение вызвало ошибку.

>ваша реализация всегда требует четырех системных вызовов

SD>Трех — запись идет целиком, без разделения на size/body.
SD>В вашем варианте будет заметно усложнен парсер потока. В частности, придется применять кольцевой или плавающий буфер для хранения полученного не целиком фрейма. Заодно испытаете восторг с необходимостью масштабирования receive buffer в пользовательском коде. А если будете вычитывать все в свой буфер, рано или поздно доберетесь до реализации TCP congestion control. Ваше право, конечно.
Я запись вообще не считал, но любая асинхронная операция требует двух вызовов — сама операция и нотификация: 2 * 2 = 4. Относительно остального, сложность может серьезно возрасти только если парсинг будет полностью асинхронным по отношению к приему данных, у меня этого нет, все происходит синхронно по отношению к чтению, поэтому никакого TCP congestion control на уровне моего кода не нужно, как и у вас, у меня, в случае заполненного буфера, просто происходит снижение вычитки данных и tcp стек сам снижает скорость передачи.
Re[9]: performance: boost::asio vs sync sockets
От: SkyDance Земля  
Дата: 15.01.14 22:33
Оценка:
А>Я запись вообще не считал, но любая асинхронная операция требует двух вызовов — сама операция и нотификация: 2 * 2 = 4.

Любая — и запись в том числе (write handler же у вас есть). Итого, чтение последовательно size/body фрейма в сравнении с вашим вариантом может делать на 50% больше вызовов, что, в контексте обработки, незаметно даже на огромном количестве мелких пакетов.

А>Относительно остального, сложность может серьезно возрасти только если парсинг будет полностью асинхронным по отношению к приему данных


Вы не привели свой код, поэтому непонятно, как вы работаете с ситуацией, когда frame size передан некорректно. Например, там указано 3 Гб как длина фрейма. Будете все 3 Гб читать? Early Validation — принципиально необходимая вещь, за исключением случаев внутренних протоколов.

А>у меня этого нет, все происходит синхронно по отношению к чтению


Вы синхронно обрабатываете все пришедшие данные? В том же потоке, где читали?
В таком варианте у вас что вагон потоков, что asio с неблокирующими вызовами работают практически с одинаковой эффективностью. Можно было и не переделывать, существенной прибавки производительности не произойдет. Я почему-то думал, что вы пересмотрели схему в пользу традиционных network/worker threads, когда обработка отвязана от сетевых коммуникаций и выполняется отдельными тредами в непрерывном режиме.
Re: performance: boost::asio vs sync sockets
От: abrarov Россия http://asio-samples.blogspot.com/
Дата: 15.02.14 14:38
Оценка: 24 (4)
Здравствуйте, Аноним, Вы писали:

А>Есть простой сервер на winsock, который был реализован в спешке на коленке, где на каждое соединение создается отдельный поток, все вызовы, естественно, блокирующие. Сейчас пытаюсь как-то все это дело оптимизировать, решил попоробовать использовать boost.asio, но возникли проблемы на одном из тестовых сценариев.


Видел, что проблема уже решена, но "добавлю свои пять копеек":
  1. М/б что-то интересное найдете у меня: http://asio-samples.blogspot.ru/.
  2. На Windows в случае использования Boost.Asio с включенным IOCP (compile-time поведение по умолчанию) IMHO наиболее производителен вариант single instance of io_service + пул потоков (кол-во потоков == кол-ву логических процессоров), так как в этом случае используется родное для Windows IOCP-ное (оптимизированное) планирование (FIFO) потоков (см. http://asio-samples.blogspot.ru/2012/07/asio-performance-test.html).
  3. С epoll/kqueue, как и с любым реактором, видимо, наибольшую производительность показывает вариант instance of io_service per work thread.
  4. Режим работы сервера (один из вышеперечисленных) можно выбирать в runtime — см. asio_samples, проект echo_server.
  5. При интенсивном IO (много операций, а не просто большой объем данных) важно учесть, что Asio использует 1-2 аллокации на 1 IO-операцию. Поэтому вполне закономерно может помочь (как уже писали выше) Custom Memory Allocation (Allocation example).
Programs must be written for people to read, and only incidentally for machines to execute
Re[2]: performance: boost::asio vs sync sockets
От: abrarov Россия http://asio-samples.blogspot.com/
Дата: 15.02.14 14:49
Оценка:
Забыл добавить для любителей велоспидов/libev/libuv:

  1. Конечно, специализированное решение всегда выигрывает. Вопрос только в том, насколько быстро получается его написать, насколько оно выигрывает у Boost.Asio (особенно при использовании custom memory allocation + C++11 move semantic), и насколько качественное (например, в плане расширения функционала) оно получается (особенно в поддержке).
  2. Boost.Asio позволяет кроссплатформенно (насколько это вообще возможно в случае асинхронного сетевого IO) писать клиент/сервер.
  3. Если нужен full duplex IO, то ничего лучше boost::asio::io_service::strand я еще не видел. В этом случае при неиспользовании Asio, Вам придется писать свой аналог. Не факт, что он получится лучше.
  4. Как минимум на Windows Asio учитывает некоторые багофичи IOCP и защищает программиста от них.
Programs must be written for people to read, and only incidentally for machines to execute
Re[3]: performance: boost::asio vs sync sockets
От: smeeld  
Дата: 15.02.14 15:48
Оценка:
Здравствуйте, abrarov, Вы писали:

A>Забыл добавить для любителей велоспидов/libev/libuv:


A>

    A>
  1. Конечно, специализированное решение всегда выигрывает. Вопрос только в том, насколько быстро получается его написать, насколько оно выигрывает у Boost.Asio (особенно при использовании custom memory allocation + C++11 move semantic), и насколько качественное (например, в плане расширения функционала) оно получается (особенно в поддержке).
    A>
  2. Boost.Asio позволяет кроссплатформенно (насколько это вообще возможно в случае асинхронного сетевого IO) писать клиент/сервер.
    A>
  3. Если нужен full duplex IO, то ничего лучше boost::asio::io_service::strand я еще не видел. В этом случае при неиспользовании Asio, Вам придется писать свой аналог. Не факт, что он получится лучше.
    A>
  4. Как минимум на Windows Asio учитывает некоторые багофичи IOCP и защищает программиста от них.
    A>

boost::asio проигрывает прежде всего по гибкости. Вот для передачи
больших файлов предпочтительней использовать sendfile, в boost::asio реализация
с sendmsg, как прикрутить sendfile в boost::asio?
Но если нужен кроссплатформ, то без разговоров.
Re[4]: performance: boost::asio vs sync sockets
От: abrarov Россия http://asio-samples.blogspot.com/
Дата: 15.02.14 16:10
Оценка:
S>boost::asio проигрывает прежде всего по гибкости. Вот для передачи
S>больших файлов предпочтительней использовать sendfile, в boost::asio реализация
S>с sendmsg, как прикрутить sendfile в boost::asio?

На Windows TransmitFile прикручивается на раз. Может, и для sendfile(2) можно что-то придумать... как минимум, сделать feature request автору Asio (*nix для него — основная платформа).
Programs must be written for people to read, and only incidentally for machines to execute
Re[5]: performance: boost::asio vs sync sockets
От: smeeld  
Дата: 15.02.14 16:24
Оценка: :)
Здравствуйте, abrarov, Вы писали:

>как минимум, сделать feature request автору Asio (*nix для него — основная платформа).


Нет, как минимум это запихнуть senfile в .ipp исходники.
И вообще, например, nginx-полный кроссплатформ, но никакого boost::asio,
для каждой ОС свой набор исходников, с выбором между ними при компиляции.
Re[6]: performance: boost::asio vs sync sockets
От: abrarov Россия http://asio-samples.blogspot.com/
Дата: 15.02.14 16:45
Оценка:
S>И вообще, например, nginx-полный кроссплатформ, но никакого boost::asio,
S>для каждой ОС свой набор исходников, с выбором между ними при компиляции.

Разве nginx уже поддерживает IOCP на Windows? Если нет, то это не такой уж и кроссплатформ (без IOCP Windows неинтересна).
Programs must be written for people to read, and only incidentally for machines to execute
Re[7]: performance: boost::asio vs sync sockets
От: smeeld  
Дата: 15.02.14 16:52
Оценка:
Здравствуйте, abrarov, Вы писали:


A>Разве nginx уже поддерживает IOCP на Windows? Если нет, то это не такой уж и кроссплатформ (без IOCP Windows неинтересна).


Вот и не знаю, смотрел как в nginx реализован asio для solaris (poll), linux (epoll), freebsd (kqueue),
но в win ветку не заглядывал, а что, там не IOCP?
Re[8]: performance: boost::asio vs sync sockets
От: abrarov Россия http://asio-samples.blogspot.com/
Дата: 15.02.14 17:04
Оценка:
S>но в win ветку не заглядывал, а что, там не IOCP?

Смотрел исходники пару лет назад — IOCP не было (и судя по Google — нет и сейчас, в общем-то это Вы вспомнили про nginx, поэтому Вам и отвечать на этот вопрос ). Тогда пришел к выводу, что IOCP будет сложно вставить туда из-за архитектуры самого nginx (ориентация на паттерн реактор).
nginx — это же C. Не хотел бы я писать большое (да даже и маленькое) решение на C (обработка ошибок выносит мозг). C++/Java — еще можно.
Programs must be written for people to read, and only incidentally for machines to execute
Re[8]: performance: boost::asio vs sync sockets
От: abrarov Россия http://asio-samples.blogspot.com/
Дата: 15.02.14 17:13
Оценка:
S>Вот и не знаю, смотрел как в nginx реализован asio для solaris (poll), linux (epoll), freebsd (kqueue),

Офтоп: почему на solaris не используют Solaris completion ports? Даже Asio использует на Solaris /dev/poll.
Programs must be written for people to read, and only incidentally for machines to execute
Re[9]: performance: boost::asio vs sync sockets
От: smeeld  
Дата: 15.02.14 17:31
Оценка:
Здравствуйте, abrarov, Вы писали:

completion ports[/url]? Даже Asio использует на Solaris /dev/poll.

В nginx для Solaris 10 есть поддержка events port, выбирается при компиляции.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.