Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Сети > Оценка эффективности ASIO сервера


Автор: rumit7 20.10.2011, 21:12
Здравствуйте!

В данный момент активно занялся изучением возможностей Boost::ASIO. Впечатления от самой библиотеки только самые положительные, давно мне так сильно ничего не нравилось, наверно с тех самых пор, как я познакомился с С++, лет так 8 назад)

Изучая http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/examples.html, http://sourceforge.net/projects/asio-samples/files/asio-samples/, а также другие примеры в сети, задумался над выработкой критериев для сравнения различных моделей построения асинхронного сервера, таких как (простите, не знаю как лучше перевести на русский):
  • io_service per cpu;
      
  • single io_service + thread pool;
      
  • one io_service for session manager + one io_service for all sessions;
      
  • ...

На данный момент меня интересует - преимущества и недостатки каждой из моделей, на основе чего можно было бы сделать выбор под конкретные требования сервера.. Я так понимаю, наиболее важные критерии это: 
  • скорость обработки запроса;
      
  • сложность реализации и поддержки;
      
  • отказоустойчивость;
      
  • ресурсопотребляемость;
      
  • что-то еще.

Как правильнее оценить? Что обычно применяют в таких случаях? Какие-то утилиты вроде Jmeter? Или что-то еще? Обращаюсь к Вам с просьбой помочь разобраться в теме..

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

Код

#include <cstdlib>
#include <cstring>
#include <iostream>

#include <string>
#include <deque>
#include <vector>

#include <boost/ref.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/make_shared.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

namespace ba = boost::asio;

typedef boost::shared_ptr<ba::io_service> io_service_ptr;
typedef boost::shared_ptr<ba::deadline_timer> deadline_timer_ptr;


enum { max_length = 1024 };


void start_work(const io_service_ptr & io_service, ba::ip::tcp::resolver::iterator iterator, ba::deadline_timer::duration_type * used_time);

size_t get_thread_num(int argc, char* argv[]);


int main(int argc, char* argv[])
{
    try
    {
        if(argc != 3 && argc != 4)
        {
            std::cerr << "Usage: blocking_tcp_echo_client <host> <port> <thread_num = 2>" << std::endl;
            return 1;
        }
        
        /// 1. Resolve connection
        ba::io_service io_service;
        ba::ip::tcp::resolver resolver(io_service);
        ba::ip::tcp::resolver::query query(ba::ip::tcp::v4(), argv[1], argv[2]);
        ba::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
        
        /// 2. The running time calculation
        std::string str_expires = boost::posix_time::to_simple_string(
            ba::deadline_timer::traits_type::now() 
                + boost::posix_time::seconds(4)
        );
        
        /// 3. The number of threads to be used
        const size_t thread_num = get_thread_num(argc, argv);

        // The pool of io_services, timers.
        std::deque<io_service_ptr> io_services;
        std::deque<deadline_timer_ptr> deadline_timers;
        
        std::vector<ba::deadline_timer::duration_type> used_time(thread_num);
        
        for(size_t i = 0; i < thread_num; ++i)
        {
            io_service_ptr ios = boost::make_shared<ba::io_service>();
            
            deadline_timer_ptr dt = boost::make_shared<ba::deadline_timer>(
                boost::ref(*ios),
                boost::posix_time::time_from_string( str_expires.c_str() )
            );
            
            dt->async_wait( boost::bind(&start_work, ios, iterator, &used_time[i]) );
            
            io_services.push_back(ios);
            deadline_timers.push_back(dt);
        }
        
        /// Create a pool of threads to run all of the io_services.
        boost::thread_group thread_group;
        
        for(size_t i = 0; i < thread_num; ++i)
        {
            thread_group.create_thread( boost::bind(&ba::io_service::run, io_services[i]) );
        }

        // wait until all thread will finished
        thread_group.join_all();
        
        /// Calculate time used by all threads
        ba::deadline_timer::duration_type all_time;
        
        for(size_t i = 0; i < thread_num; ++i)
        {
            all_time += used_time[i];
        }
        
        std::cout << "END with " << all_time << std::endl;
    }
    catch(std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}


void start_work(const io_service_ptr & io_service, ba::ip::tcp::resolver::iterator iterator, ba::deadline_timer::duration_type * used_time)
{
    ba::deadline_timer::time_type tm1 = ba::deadline_timer::traits_type::now();
        
    //std::cout << "Thread started at " << tm1 << std::endl;

    for(int i = 0; i<1000; i++)
    {
        ba::ip::tcp::socket s(*io_service);
        ba::connect(s, iterator);

        for(int j = 0; j<100; j++)
        {
            char data[max_length];
            
            ba::write(s, ba::buffer(data, max_length));

            size_t reply_length = ba::read(s, ba::buffer(data, max_length));
        }
    }

    ba::deadline_timer::time_type tm2 = ba::deadline_timer::traits_type::now();
    
    //std::cout << "Thread end at " << tm2 << std::endl;
    //std::cout << "Time took : " << tm2-tm1 << std::endl;
    
    *used_time = tm2-tm1;
}


size_t get_thread_num(int argc, char* argv[])
{
    if(argc == 4)
        return boost::lexical_cast<size_t>( argv[3] );
    else
        return 2;
}

 

Автор: Lazin 20.10.2011, 22:24
Цитата(rumit7 @  20.10.2011,  21:12 Найти цитируемый пост)
io_service per cpu;

если вы хотите что-бы все запросы от одного клиента выполнялись на одном процессоре, синхронизация в этом случае может быть проще, но только если обработчики запросов из разных io_service-ов не лезут в общую память за данными, если лезут то преимуществ никаких нет.
если вы хотите быстро и масштабируемо и, при этом без синхронизации - то не нужно использовать asio, asio это proactor, а вам нужно смотреть на библиотеки, реализующие reactor, в ace например есть, что-бы вся бизнес логика была в одном потоке, на одном ядре, а весь I/O был асинхронным

Цитата(rumit7 @  20.10.2011,  21:12 Найти цитируемый пост)
single io_service + thread pool;

запросы от одного клиента могут выполняться на разных процессорах и, может быть, даже параллельно(что-бы они не выполнялись параллельно используют strand), преимущество этого подхода перед первым - балансировка нагрузки между процессорами

Цитата(rumit7 @  20.10.2011,  21:12 Найти цитируемый пост)
one io_service for session manager + one io_service for all sessions;

не понял

Цитата(rumit7 @  20.10.2011,  21:12 Найти цитируемый пост)
...

архитектура сервера это не то, сколько io_service-ов вы будете использоваться, это несколько более широкая тема, может быть вам вообще не стоит связываться со stateful архитектурой а забабахать sateless архитектуру без всяких сессий, но зато с преферансом и гимназистками?


Цитата(rumit7 @  20.10.2011,  21:12 Найти цитируемый пост)
скорость обработки запроса;

asio это тонкая надстройка над io completion ports, по крайней мере под виндой, поэтому можно с уверенностью сказать, что скорость обработки запроса зависит от скорости обработки запроса smile 

Цитата(rumit7 @  20.10.2011,  21:12 Найти цитируемый пост)
сложность реализации и поддержки;

it depends, явно проще чем использовать голый win api

Цитата(rumit7 @  20.10.2011,  21:12 Найти цитируемый пост)
отказоустойчивость;

отказоустойчивость, это свойство системы сохранять работоспособность после отказа некоторых ее частей, этого не достигнуть просто используя какую либо библиотеку smile

Цитата(rumit7 @  20.10.2011,  21:12 Найти цитируемый пост)
ресурсопотребляемость;

некоторое количество памяти на каждую асинх. операцию ввода вывода, где-то ведь должен храниться ваш callback, чем их больше, тем больше тратится ресурсов. поэтому потребление ресурсов несколько непредсказуемо, чем больше всего происходит одновременно, чем больше асинхронных операций в процессе, тем больше нужно ресурсов

Автор: boostcoder 21.10.2011, 01:05
rumit7, слишком уж абстрактно все у вас описано. можно долго распинаться описывая для вас то, что перепечатано во множестве книжек.
поконкретнее вопросы, пожалуйста.

Автор: rumit7 21.10.2011, 06:11
Цитата(boostcoder @ 21.10.2011,  01:05)
rumit7, слишком уж абстрактно все у вас описано. можно долго распинаться описывая для вас то, что перепечатано во множестве книжек.
поконкретнее вопросы, пожалуйста.


На данный момент меня интересует - скорость обработки запроса. Т.е. каким образом правильнее организовать сравнение по скорости обработки различных примеров, приводимых в ASIO Examples, ASIO Samples. Будут ли достоверными данные предоставляемые выше приведенной самописной тестовой тулзой? 

Автор: rumit7 21.10.2011, 06:46
Цитата

если вы хотите что-бы все запросы от одного клиента выполнялись на одном процессоре, синхронизация в этом случае может быть проще, но только если обработчики запросов из разных io_service-ов не лезут в общую память за данными, если лезут то преимуществ никаких нет.


Я так понимаю здесь возможно использование memory pool на каждый проц и плюс к этому использование custom memory allocation, чтобы исключить обращение к общей памяти при выполнении асинхронных операций? 

Цитата

если вы хотите быстро и масштабируемо и, при этом без синхронизации - то не нужно использовать asio, asio это proactor, а вам нужно смотреть на библиотеки, реализующие reactor, в ace например есть, что-бы вся бизнес логика была в одном потоке, на одном ядре, а весь I/O был асинхронным


Спасибо. Пока моя цель - "быстро и масштабируемо", но по возможности с использованием ASIO. А там будем делать выводы..

Цитата

Цитата

io_service for session manager + one io_service for all sessions

не понял


Это модель применена в ASIO Samples, когда один io_service (+ 1 или 2 потока) используется для акцепта клиентов, создания сессий и управления ими, а второй io_service (+ по потоку на каждый проц) используется всеми сессиями (т.е. взаимодействие с клиентами). 

Цитата

архитектура сервера это не то, сколько io_service-ов вы будете использоваться, это несколько более широкая тема


Да я это понимаю. На данный момент я бы хотел выяснить: какой из моделей способов использования ASIO библиотеки дает больший перформанс и в каких случаях? Просто сравнивать примеры эхо серверов это глупо, яный пень в реальном проекте гораздо больше взаимодействий между различными факторами.. Но нужно же как-то выбрать. Следовательно нужно выполнить сравнение прототипов, думаю на первом этапе по скорости обработки клиентов. Здесь у меня начинается ступор - как лучше организовать сравнительный тест?

Цитата

может быть вам вообще не стоит связываться со stateful архитектурой а забабахать sateless архитектуру без всяких сессий, но зато с преферансом и гимназистками?


Спасибо за Ваш ответ!

Автор: rumit7 21.10.2011, 07:42
Цитата(Lazin @ 20.10.2011,  22:24)
Цитата(rumit7 @  20.10.2011,  21:12 Найти цитируемый пост)
io_service per cpu;

если вы хотите быстро и масштабируемо и, при этом без синхронизации - то не нужно использовать asio, asio это proactor, а вам нужно смотреть на библиотеки, реализующие reactor, в ace например есть, что-бы вся бизнес логика была в одном потоке, на одном ядре, а весь I/O был асинхронным

Из документации Boost::ASIO:

Цитата

As you already know, the asio library provides a guarantee that callback handlers will only be called from threads that are currently calling boost::asio::io_service::run(). Consequently, calling boost::asio::io_service::run() from only one thread ensures that callback handlers cannot run concurrently.


Это ведь значит, что и с использованием ASIO можно добиться "что-бы вся бизнес логика была в одном потоке, на одном ядре, а весь I/O был асинхронным"?

Автор: boostcoder 21.10.2011, 07:44
Цитата(rumit7 @  21.10.2011,  06:46 Найти цитируемый пост)
Это модель применена в ASIO Samples, когда один io_service (+ 1 или 2 потока) используется для акцепта клиентов, создания сессий и управления ими, а второй io_service (+ по потоку на каждый проц) используется всеми сессиями (т.е. взаимодействие с клиентами).

я и сам использую реализацию из https://sourceforge.net/projects/asio-samples/ построенную на этой архитектуре, и, мое имхо - это лучшая архитектура. т.к. обладает высокой скоростью accepta+reusing_sessions, что позволит пережить большинство DDOS атак, так и тем что второй io_service существует в единственном экземпляре, что позволяет при использовании strand`ов избавится от прямой необходимости в синхронизации.


ну а тестирование_производительности/сравнение_производительности - это очень субъективно и зависимо... smile

Добавлено через 3 минуты и 30 секунд
Цитата(rumit7 @  21.10.2011,  07:42 Найти цитируемый пост)
Это ведь значит, что и с использованием ASIO можно добиться "что-бы вся бизнес логика была в одном потоке, на одном ядре, а весь I/O был асинхронным"?

да. при использовании двух и более io_service`ов. но в этом случае всю синхронизацию придется обеспечивать руками. а в этом случае, не факт что такая архитектура будет обладать большей производительностью.

Автор: rumit7 21.10.2011, 08:08
Цитата(boostcoder @ 21.10.2011,  07:44)
я и сам использую реализацию из https://sourceforge.net/projects/asio-samples/ построенную на этой архитектуре, и, мое имхо - это лучшая архитектура. т.к. обладает высокой скоростью accepta+reusing_sessions, что позволит пережить большинство DDOS атак, так и тем что второй io_service существует в единственном экземпляре, что позволяет при использовании strand`ов избавится от прямой необходимости в синхронизации.

...

да. при использовании двух и более io_service`ов. но в этом случае всю синхронизацию придется обеспечивать руками. а в этом случае, не факт что такая архитектура будет обладать большей производительностью.

Я хочу объеденить два подхода: показанный в примерах https://sourceforge.net/projects/asio-samples/ и io_service на каждый проц. 

Идея заключается в том, чтобы по возможности локализовать обращение к памяти внутри одного проца (потока). Для этого выделять память, как для объектов сессий, так и для каждой асинхронной операции (custom memory allocation) - память из memory pool? При этом, чтобы исключить синхронизацию между потоками, создать для каждого потока свой memory pool. Возможно использовать вместо shared_ptr - intrusive_ptr. 

При все этом не потерять преимущества архитектуры использованной в asio-samples, насколько это вообще технически возможно..


А вот чтобы сравнить две модели мне и нужно иметь какую-то тестовую тулзу + сценарий выполнения тестов..

Автор: boostcoder 21.10.2011, 08:20
Цитата(rumit7 @  21.10.2011,  08:08 Найти цитируемый пост)
Для этого выделять память, как для объектов сессий, так и для каждой асинхронной операции (custom memory allocation) - память из memory pool?

грубо говоря - да.
но можно иначе: кол-во сессий обычно ограниченно сверху. т.е. можно просто создать пул сессий, к примеру по максимуму, 65К, и поместить их в какой-то recycled-buffed/queue. каждая сессия содержит свои аллокаторы для custom_memory_allocation. таким образом полностью пропадает необходимость в memory_pool для служебных данных(собственно asio-samples и их echo_server так(ну, почти так) и реализован). но остаются еще буфера для пользовательских данных...

Автор: rumit7 21.10.2011, 08:27
Цитата(boostcoder @ 21.10.2011,  08:20)
грубо говоря - да.
но можно иначе: кол-во сессий обычно ограниченно сверху. т.е. можно просто создать пул сессий, к примеру по максимуму, 65К, и поместить их в какой-то recycled-buffed/queue. каждая сессия содержит свои аллокаторы для custom_memory_allocation. таким образом полностью пропадает необходимость в memory_pool для служебных данных(собственно asio-samples и их echo_server так и реализован). но остаются еще буфера для пользовательских данных...

Да у меня возникла такая-же идея при изучении примеров из asio-samples. 

Если, как Вы говорите, преаллоцировать память для всех сессий, то соответственно упростится та схема, которую я описал выше. Следовательно остается только memory_pool-ы для пользовательских данных..

Над этим нужно подумать.. Спасибо!

Автор: boostcoder 21.10.2011, 08:40
Цитата(rumit7 @  21.10.2011,  08:27 Найти цитируемый пост)
Следовательно остается только memory_pool-ы для пользовательских данных..

тут можно поступить следующим образом, влоб: т.к. кол-во асинхронных операций так же ограничено сверху двумя, то в объекте сессии, так же можно предаллоцировать их, выбрав при этом необходимый объем. к примеру 32к.
и того: WA+RA+TA+WB+RB*65к=~4Gb
где:
WA - allocator for write handlers(http://asio-samples.svn.sourceforge.net/viewvc/asio-samples/trunk/include/ma/echo/server/session.hpp?revision=523&view=markup)
RA - allocator for read handlers(http://asio-samples.svn.sourceforge.net/viewvc/asio-samples/trunk/include/ma/echo/server/session.hpp?revision=523&view=markup)
TA - allocator for timer handlers(http://asio-samples.svn.sourceforge.net/viewvc/asio-samples/trunk/include/ma/echo/server/session.hpp?revision=523&view=markup)
WB - allocator for write buffer
RB - allocator for read buffer

но, в реале сессий все же меньше.

Добавлено @ 08:50
собственно, такой способ преаллокации_всего дает два плюса: 1)устраняет проблему фрагментации памяти, 2)устраняет run-time оверхед memory_poll`ов.

Up.
и 4Gb для подобных задач - совсем не много. на самом деле, сессий все же меньше 65к, т.к. часть портов зарезервирована_системой/занята_другими_программами.

Автор: rumit7 21.10.2011, 09:03
Цитата(boostcoder @ 21.10.2011,  08:40)
Цитата(rumit7 @  21.10.2011,  08:27 Найти цитируемый пост)
Следовательно остается только memory_pool-ы для пользовательских данных..

тут можно поступить следующим образом, влоб: т.к. кол-во асинхронных операций так же ограничено сверху двумя, то в объекте сессии, так же можно предаллоцировать их, выбрав при этом необходимый объем. к примеру 32к.
и того: WA+RA+TA+WB+RB*65к=~4Gb
где:
WA - allocator for write handlers
RA - allocator for read handlers
TA - allocator for timer handlers
WB - allocator for write buffer
RB - allocator for read buffer

но, в реале сессий все же меньше.

Спасибо! Вот только как эта массовая предаллокация скажется на других задачах выполняющихся на сервере? 

У нас например возможна ситуация, когда на одном и том-же железе работать будет как наш сервер-приложение, так и стороннее приложение, в взаимодействии с которым мы фактически и выполняем работу..

Цитата

Добавлено @ 08:50
собственно, такой способ преаллокации_всего дает два плюса: 1)устраняет проблему фрагментации памяти, 2)устраняет run-time оверхед memory_poll`ов.


Но ведь использование memory-pool также решает проблему фрагментации памяти, а run-time оверхед не такой уж и большой, если исключить синхронизацию между потоками!? При этом memory pool можно настроить таким образом, чтобы лишняя память освобождалась..

Автор: boostcoder 21.10.2011, 09:07
Цитата(rumit7 @  21.10.2011,  09:03 Найти цитируемый пост)
Вот только как эта массовая предаллокация скажется на других задачах выполняющихся на сервере?

никак smile вот только памяти должно быть в достатке. иначе свопинг сожрет всю! производительность. но сервера, обычно, имеют минимум 8Gb. обычно 16.

Цитата(rumit7 @  21.10.2011,  09:03 Найти цитируемый пост)
Но ведь использование memory-pool также решает проблему фрагментации памяти

и
Цитата(rumit7 @  21.10.2011,  09:03 Найти цитируемый пост)
memory pool можно настроить таким образом, чтобы лишняя память освобождалась

 = противоречие.

Цитата(rumit7 @  21.10.2011,  09:03 Найти цитируемый пост)
а run-time оверхед не такой уж и большой

это зависит... но он есть.
мое скромное имхо - я бы вовсе не заморачивался с экономией памяти, т.к. 16Gb это не какой-то космический объем.

Автор: rumit7 21.10.2011, 09:19
Цитата

Цитата(rumit7 @  21.10.2011,  09:03 Найти цитируемый пост)
Но ведь использование memory-pool также решает проблему фрагментации памяти

и
Цитата(rumit7 @  21.10.2011,  09:03 Найти цитируемый пост)
memory pool можно настроить таким образом, чтобы лишняя память освобождалась

 = противоречие.


Здесь я имел ввиду следующее - если в пик активности сервера памяти в memory pool не достаточно, то соот. выделяется еще один дополнительный блок (может быть даже х2 размера от предыдущего) и так циклически; после того как активность спадает, то из всего этого выделенного пространства будет реально использоваться куда меньше.. Так вот этот излишек можно вернуть (разумеется оставить с запасом), чтобы избежать свопинга..

Цитата

мое скромное имхо - я бы вовсе не заморачивался с экономией памяти, т.к. 16Gb это не какой-то космический объем.


Спасибо большое!

Автор: mabrarov 21.10.2011, 09:28
Цитата(rumit7 @ 20.10.2011,  21:12)

  • io_service per cpu;
  • single io_service + thread pool;  
  • one io_service for session manager + one io_service for all sessions;  
  • ...

На данный момент меня интересует - преимущества и недостатки каждой из моделей, на основе чего можно было бы сделать выбор под конкретные требования сервера.

Стратегию "io_service per CPU" вроде как используют в Yandex. Она позволяет исключить миграцию, например, экземпляров ma::echo::server::session между кэшами разных ядер/процессоров. Т.е. все ma::echo::server::session, относящиеся к одному экземпляру io_service, теоретически могут влезть (и не вылазить от туда в течение всей работы) в кэш одного ядра. Кроме того, это избавляет от необходимости защищать ma::echo::server::session при помощи io_service::strand  (который тоже не бесплатно дается и ОС и процессору).

Я подумывал дать возможность в echo_server использовать такую стратегию для ma::echo::server::session (указывая это как параметр CLI). Но пока не решил вопрос с рапределением ma::echo::server::session по набору экземпляров io_service. Здесь нужна какая-то статистика с каждого io_service, которая тоже будет отнимать ресурсы и время.

Стратегии "single io_service + thread pool" и "one io_service for session manager + one io_service for all sessions" почти одно и то же. Во втором случае ma::echo::server::session_manager вынесен отдельно аля SEDA. Это сделано для гарантии того, что даже при максимальной загрузке ma::echo::server::session_manager (простейшая DOS-атака) на обработку сессий будет потрачено не менее определенной части всего процессорного времени, выделенного на процесс сервера.

Ну и да, тестировать и еще раз тестировать. "Золотая тропа" известна: make it build and work, make it work right, if need (!) make it (more) fast. Стратегия "one io_service for session manager + one io_service for all sessions" остановилась на втором шаге. Возможно, на третьем шаге стратегия "io_service per CPU" победит. Однако, эффективная реализация "io_service per CPU" м/б сложнее "one io_service for session manager + one io_service for all sessions".

Автор: mabrarov 21.10.2011, 09:50
http://asio-samples.blogspot.com/2011/09/echoserver-revisited.html?showComment=1319179597404#c1866277865684306536.

Автор: Lazin 21.10.2011, 10:07
Цитата(mabrarov @  21.10.2011,  09:28 Найти цитируемый пост)
Стратегию "io_service per CPU" вроде как используют в Yandex. Она позволяет исключить миграцию, например, экземпляров ma::echo::server::session между кэшами разных ядер/процессоров. Т.е. все ma::echo::server::session, относящиеся к одному экземпляру io_service, теоретически могут влезть (и не вылазить от туда в течение всей работы) в кэш одного ядра. Кроме того, это избавляет от необходимости защищать ma::echo::server::session при помощи io_service::strand  (который тоже не бесплатно дается и ОС и процессору).

стратегию io_service per cpu можно использовать, если одна сессия часто обращается к одним и тем же данным, что-бы кэширование имело смысл, и при этом не выполняет ничего тяжелого. Под тяжелым я имею ввиду продолжительные вычисления, если одна сессия займет процессора на 1мс то ни одна другая сессия, выполняемая на этом io_service-е не сможет ничего сделать в течении этой милисекунды. А в случае пулла потоков это не так, одна из сессий заняла поток на 1мс, все остальные продолжают работать на оставшихся N потоках.

Цитата(mabrarov @  21.10.2011,  09:28 Найти цитируемый пост)
Стратегии "single io_service + thread pool" и "one io_service for session manager + one io_service for all sessions" почти одно и то же. Во втором случае ma::echo::server::session_manager вынесен отдельно аля SEDA. Это сделано для гарантии того, что даже при максимальной загрузке ma::echo::server::session_manager (простейшая DOS-атака) на обработку сессий будет потрачено не менее определенной части всего процессорного времени, выделенного на процесс сервера.

Для того, что-бы это было похоже на SEDA нужно еще много чего сделать smile Вообще, хотелось бы узнать какую задачу пытается решить ТС.

Цитата(mabrarov @  21.10.2011,  09:28 Найти цитируемый пост)
Ну и да, тестировать и еще раз тестировать. "Золотая тропа" известна: make it build and work, make it work right, if need (!) make it (more) fast. Стратегия "one io_service for session manager + one io_service for all sessions" остановилась на втором шаге. Возможно, на третьем шаге стратегия "io_service per CPU" победит. Однако, эффективная реализация "io_service per CPU" м/б сложнее "one io_service for session manager + one io_service for all sessions".

Что-бы об этом рассуждать, нужно хоть что-нибудь знать о проблеме. Может быть не победит, может быть вообще победит thread per session smile

Добавлено через 1 минуту и 48 секунд
вообще, если клиентов не много, то асинхронный сервер будет иметь более низкую пропускную способность, нежели простой thread per client сервер выполняющий все операции ввода/вывода синхронно, ибо во втором случае будет меньше системных вызовов

Автор: boostcoder 21.10.2011, 10:16
Цитата(mabrarov @  21.10.2011,  09:28 Найти цитируемый пост)
SEDA

"staged event-driven architecture" ?

Автор: mabrarov 21.10.2011, 10:24
Цитата(boostcoder @ 21.10.2011,  08:40)
и 4Gb для подобных задач - совсем не много. на самом деле, сессий все же меньше 65к, т.к. часть портов зарезервирована_системой/занята_другими_программами.

Не путаем исходящие TCP соединения и входящие 8)
Кол-во входящих ограничено ресурсами ОС (и локальный порт у них у всех один - тот с которого соединение было accepted, т.е. тот, что у listening socket). 
Кол-во исходящих соединений ограничено еще и максимальным кол-вом открытых портов, http://en.wikipedia.org/wiki/Ephemeral_port.
Вот. Заодно и вспомнил теорию.

Добавлено @ 10:27
Цитата(Lazin @ 21.10.2011,  10:07)
стратегию io_service per cpu можно использовать, если одна сессия часто обращается к одним и тем же данным, что-бы кэширование имело смысл, и при этом не выполняет ничего тяжелого. Под тяжелым я имею ввиду продолжительные вычисления, если одна сессия займет процессора на 1мс то ни одна другая сессия, выполняемая на этом io_service-е не сможет ничего сделать в течении этой милисекунды. А в случае пулла потоков это не так, одна из сессий заняла поток на 1мс, все остальные продолжают работать на оставшихся N потоках.

Конечно, предполагается, что тяжелой работой будет заниматься другой пул потоков.

Добавлено @ 10:28
Цитата(boostcoder @ 21.10.2011,  10:16)
Цитата(mabrarov @  21.10.2011,  09:28 Найти цитируемый пост)
SEDA

"staged event-driven architecture" ?

Да

Добавлено @ 10:31
Цитата(Lazin @ 21.10.2011,  10:07)
Для того, что-бы это было похоже на SEDA нужно еще много чего сделать smile

Естественно, это далеко от SEDA в том виде, как она описана в теории. Но смысл аналогичный - разбиваем обработку на стадии. На каждой стадии м/б свой пул потоков.

Добавлено @ 10:33
Цитата(Lazin @ 21.10.2011,  10:07)
Что-бы об этом рассуждать, нужно хоть что-нибудь знать о проблеме. Может быть не победит, может быть вообще победит thread per session smile
Добавлено @ 10:09
вообще, если клиентов не много, то асинхронный сервер будет иметь более низкую пропускную способность, нежели простой thread per client сервер выполняющий все операции ввода/вывода синхронно, ибо во втором случае будет меньше системных вызовов

Опять, "естественно" да. Эта теория проштудирована много раз. Все же, думаю, здесь рассматривается случай с (большим) множеством клиентов.

Автор: rumit7 21.10.2011, 10:36
Цитата(Lazin @ 21.10.2011,  10:07)
стратегию io_service per cpu можно использовать, если одна сессия часто обращается к одним и тем же данным, что-бы кэширование имело смысл, и при этом не выполняет ничего тяжелого. Под тяжелым я имею ввиду продолжительные вычисления, если одна сессия займет процессора на 1мс то ни одна другая сессия, выполняемая на этом io_service-е не сможет ничего сделать в течении этой милисекунды. А в случае пулла потоков это не так, одна из сессий заняла поток на 1мс, все остальные продолжают работать на оставшихся N потоках.


Спасибо за пояснения!

Цитата

Вообще, хотелось бы узнать какую задачу пытается решить ТС.


Там я указал:
Цитата

На данный момент меня интересует - преимущества и недостатки каждой из моделей, на основе чего можно было бы сделать выбор под конкретные требования сервера..


Можно сказать ресерч возможностей каждой из моделей, чтобы иметь информацию для выбора под конкретные требования..

Цитата

Все же, думаю, здесь рассматривается случай с (большим) множеством клиентов.


Да. Моя вина - изначально не четко сформулировал вопрос..

Автор: mabrarov 21.10.2011, 10:38
Цитата(rumit7 @ 21.10.2011,  10:36)
Можно сказать ресерч возможностей каждой из моделей, чтобы иметь информацию для выбора под конкретные требования..

Оформить бы выводы в виде FAQ и засунуть в документацию Asio.

Автор: Lazin 21.10.2011, 10:51
Цитата(mabrarov @  21.10.2011,  10:24 Найти цитируемый пост)
Конечно, предполагается, что тяжелой работой будет заниматься другой пул потоков.

все равно это сложнее, нужно решить проблему балансирования нагрузки между отдельными io_service-ами

Цитата(mabrarov @  21.10.2011,  10:24 Найти цитируемый пост)
Опять, "естественно" да. Эта теория проштудирована много раз. Все же, думаю, здесь рассматривается случай с (большим) множеством клиентов.

большой - понятие растяжимое

Автор: mabrarov 21.10.2011, 10:56
Цитата(Lazin @ 21.10.2011,  10:51)
все равно это сложнее, нужно решить проблему балансирования нагрузки между отдельными io_service-ами

О том я и писал в своем первом сообщении.

Автор: mabrarov 23.10.2011, 09:18
http://www.jabberdoc.org/section13. Мало и, возможно, не то, что нужно было. Но интересно.

Автор: rumit7 23.10.2011, 10:54
Цитата(mabrarov @ 23.10.2011,  09:18)
http://www.jabberdoc.org/section13. Мало и, возможно, не то, что нужно было. Но интересно.

Спасибо за ссылку!

Я пока обнумываю все то, что здесь говорилось. В принципе есть идея того, как используя memory pool ускорить выделение памяти и помочь стратегии io_service per cpu в плане кеширования.. Есть ряд проблем, если удастся достичь положительного результата - отпишусь здесь.. Если нет, наверно промолчу, умнее казаться буду))

Автор: Олег2005 26.10.2011, 23:09
Модератор: хочу отметить высокий профессионализм участников темы - и пожелать им еще больших успехов в нашем непростом сетевом программировании 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)