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


Автор: phprus 26.1.2011, 22:02
Здравствуйте!

UDT ( http://udt.sourceforge.net/ ) - это протокол предназначенный для передачи данных по высокоскоростным сетям.
Подскажите пожалуйста, существует ли реализация модуля для Boost.ASIO, поддерживающего UDT? Пробовал искать, но готовых решений не находил.

Может быть здесь кто-нибудь встречал нечто подобное или хотя-бы документацию по добавлению поддержки нового протокола в Boost.ASIO?

Автор: GrayCardinal 27.1.2011, 09:43
phprus
Извини, а на кой черт ЭТУ вещь прикручивать к бусту ? 

Автор: phprus 27.1.2011, 12:20
GrayCardinal
А в чем проблема с Boost'ом?

Есть готовое приложение, которое использует Boost и в частности Boost.ASIO для сетевой подсистемы и в него нужно добавить поддержку UDT, при этом желательно без переписывания всей сетевой подсистемы.

P.S> Я задал этот вопрос еще и на forum.sources.ru (да простят меня модераторы за мультифорумный кросспостинг) и там мне ответили: http://forum.sources.ru/index.php?showtopic=324283 

Автор: boostcoder 27.1.2011, 12:44
Цитата(phprus @  27.1.2011,  12:20 Найти цитируемый пост)
Я задал этот вопрос еще и на forum.sources.ru (да простят меня модераторы за мультифорумный кросспостинг) и там мне ответили

я даже знаю кто это smile 

Автор: phprus 23.7.2011, 13:02
Прошло пол года и у меня дошли руки реализовать такое расширение для Boost 1.47.

Правда результат тестирования меня несколько разочаровал. На канале в 1 Гбит/с (ping 4-6 мс, из них порядка 50-100Мбит/с занято трафиком соседнего vlan) средняя скорость передачи данных по TCP получилась порядка 800Мбит/с (в пиках до 930), а UDT показал среднюю скорость порядка 580Мбит/с (в пиках до 920).

При этом, если TCP набрал максимальную скорость за 3 секунды, то UDT на это потребовалось 40 секунд и провалы на графике скорости, вызванные потерями пакетов, в случае TCP гораздо короче по времени и скорости (2с, до 200Мбит/с), чем у UDT (6-10c, до 400Мбит/с).

На обоих концах канала - CentOS 5.5, TCP испытывалось при помощи iperf, UDT при помощи входящих в дистрибутив демок. Статистика снималась путем чтения значений RX, TX соответствующего сетевого интерфейса 1 раз в секунду.

А так как мое приложение в своей работе по сути обменивается сообщениями через сеть (до 40Мбайт на сообщение), то такой медленный разгон UDT на стандартных настройках может свести на нет почти все преимущества UDT.

P.S. К осени-зиме обещается 10GE оборудование и можно будет проверить применимость TCP/UDT в случае передачи данных на более высокоскоростных линиях связи.

P.P.S. Код модифицированной ASIO для UDT в ближайшее время выложу для всеобщего обозрениязакидывания помидорами.

Автор: boostcoder 23.7.2011, 13:06
phprus, в какой-то теме, я Вам пытался сказать о том, что TCP в моей задаче и на моем оборудовании отлично справляется. я не видел нужды завершать реализацию UDT для asio.
помимо того, я проводил тесты демками что в дистре, и не увидел большого выигрыша. он был. но незначительный. потому забросил.

Добавлено через 2 минуты и 1 секунду
Цитата(boostcoder @  23.7.2011,  13:06 Найти цитируемый пост)
я проводил тесты демками что в дистре

ах да. я еще и свои клиент и сервер написал. чтоб было. и все так же. смысла во времязатратах на на реализацию UDT я не обнаружил.

Добавлено через 3 минуты и 48 секунд
в общем, я тоже немного разочаровался в том, что мои результаты тестов не подтверждают тех что выложены на офф. сайте :(

Автор: phprus 23.7.2011, 22:14
boostcoder, судя по тому, что написано на официальном сайте UDT, заявленная эффективность должна проявляться на высокоскоростных линиях связи с большими пингами. Например, при трансконтинентальной передаче данных. А у меня рабочая инфраструктура всего 400км в длину и 5мс RTT, да и канал всего 1GE.

Когда появится оборудование 10GE буду проводить повторные тесты, вдруг что-то изменится и авторы на самом деле правы, а это мы со своим "допотопным" железом что-то делать пытаемся smile) .


Цитата(boostcoder @  23.7.2011,  16:06 Найти цитируемый пост)
смысла во времязатратах на на реализацию UDT я не обнаружил.

Для меня отрицательный результат тоже результат. Так как эта часть была указана в описании работ по грантам (я в научной организации работаю), следовательно даже если эта часть в итоге использоваться не будет, то проверить эффективность все равно надо.

P.S.> Но нет худа без добра smile Когда делал эту реализацию немного лучше разобрался во внутренностях ASIO.

Автор: phprus 29.7.2011, 16:47
Цитата(boostcoder @  23.7.2011,  16:06 Найти цитируемый пост)
ах да. я еще и свои клиент и сервер написал. чтоб было. и все так же. смысла во времязатратах на на реализацию UDT я не обнаружил.

Скажи пожалуйста, можно будет посмотреть твою реализацию клиента и сервера UDT?

Автор: boostcoder 29.7.2011, 16:53
сервер. код клиента почти ничем не отличается от того что в примере.
Код


#include <iostream>
#include <boost/thread.hpp>
#include <string.h>
#include <udt.h>

/***************************************************************************/

struct thread_args {
   thread_args()
      :run(true),
      connections(0),
      per_sec_req(0),
      per_sec_ask(0),
      epoll_fd(0)
   {}

   volatile bool run;
   volatile int connections;
   volatile int per_sec_req;
   volatile int per_sec_ask;
   int epoll_fd;
   boost::mutex mutex;
   void add(UDTSOCKET s) {
      boost::mutex::scoped_lock locker(mutex);
      UDT::epoll_add_usock(epoll_fd, s);
   }


};

void epoll_thread(thread_args&);

/***************************************************************************/

int main(int argc, char** argv) {
   (void)argc;
   (void)argv;

   UDT::startup();

   const int port = 9000;

   UDTSOCKET sock = UDT::socket(AF_INET, SOCK_DGRAM, 0);
   if ( UDT::INVALID_SOCK == sock ) {
      std::cout << "UDT::socket(): " << UDT::getlasterror().getErrorMessage() << std::endl;
      return 1;
   }

   sockaddr_in addr;
   memset(&addr, 0, sizeof(addr));
   addr.sin_family      = AF_INET;
   addr.sin_port        = htons(port);
   addr.sin_addr.s_addr = INADDR_ANY;

   if ( UDT::ERROR == UDT::bind(sock, (sockaddr*)&addr, sizeof(addr)) ) {
      std::cout << "UDT::bind(): " << UDT::getlasterror().getErrorMessage() << std::endl;
      return 1;
   }

   if ( UDT::ERROR == UDT::listen(sock, 10) ) {
      std::cout << "UDT::bind(): " << UDT::getlasterror().getErrorMessage() << std::endl;
      return 1;
   }

   /**  */
   thread_args args;
   args.run = true;
   args.epoll_fd = UDT::epoll_create();
   boost::thread thread(&epoll_thread, boost::ref(args));
   boost::thread stat(
      [&args]() {
         while ( args.run ) {
            std::cout
            << "connections: " << args.connections << std::endl
            << "requests   : " << args.per_sec_req << std::endl
            << "answers    : " << args.per_sec_ask << std::endl
            << std::endl;
            args.per_sec_req = args.per_sec_ask = 0;
            boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
         }
      }
   );

   sockaddr_storage clientaddr;
   int addrlen = sizeof(clientaddr);

   UDTSOCKET client;
   while ( args.run ) {
      if ( UDT::INVALID_SOCK == (client = UDT::accept(sock, (sockaddr*)&clientaddr, &addrlen)) ) {
         std::cout << "UDT::accept(): " << UDT::getlasterror().getErrorMessage() << std::endl;
         args.run = false;
         thread.join();
         break;
      }
      args.connections++;
      args.add(client);
   }

   UDT::epoll_release(args.epoll_fd);
   UDT::cleanup();
}

/***************************************************************************/

void epoll_thread(thread_args& args) {
   std::set<UDTSOCKET> readfds;
   char buf[32] = "\0";

   while ( args.run ) {
      if ( 0 == UDT::epoll_wait(args.epoll_fd, &readfds, 0, 10) ) continue;

      for ( std::set<UDTSOCKET>::iterator it = readfds.begin(); it != readfds.end(); ++it ) {
         bool block = false;
         UDT::setsockopt(*it, 0, UDT_RCVSYN, &block, sizeof(bool));
         UDT::setsockopt(*it, 0, UDT_SNDSYN, &block, sizeof(bool));

         if ( UDT::ERROR == UDT::recvmsg(*it, buf, sizeof(buf)) ) {
            std::cout << "UDT::recv(): " << UDT::getlasterror().getErrorMessage() << std::endl;
         }
         if ( UDT::ERROR == UDT::sendmsg(*it, buf, sizeof(buf)) ) {
               std::cout << "UDT::send(): " << UDT::getlasterror().getErrorMessage() << std::endl;
         }
         {  boost::mutex::scoped_lock locker(args.mutex);
            args.per_sec_req++;
            args.per_sec_ask++;
         }
      }
   }
}

/***************************************************************************/


Автор: phprus 30.7.2011, 17:13
boostcoder
Спасибо!

Автор: boostcoder 1.8.2011, 22:00
phprus, скажи, у тебя действительно нет предела пропускной способности из-за того что сервер все свои ресурсы тратит на аллокации?
я, к примеру, в своем проекте реализовал для всех хэндлеров http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/overview/core/allocation.html, и нагрузка на проц уменьшилась в 2.5 раза!
я в какой-то теме уже писал о том, что половина ресурсов затрачивалось только на new и delete.
все же, уже в который раз убеждаюсь в том, что asio создавалась не дауном, и там нет ничего лишнего или каких-либо ограничений smile 

Автор: phprus 2.8.2011, 12:52
boostcoder
На гигабите нету. До тормозов из-за распределителя памяти еще очень далеко.
Правда у меня и обращений к new/delete в самих обработчиках практически нет (но почитать про Custom Memory Allocation я все равно планирую). А вот тормоза из-за излишних копирований данных были и один маленький баг из-за которого были эти копирования влиял на производительность уменьшая ее почти в 5 раз.

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

Автор: boostcoder 2.8.2011, 14:41
Цитата(phprus @  2.8.2011,  12:52 Найти цитируемый пост)
у меня и обращений к new/delete в самих обработчиках практически нет

каждый boost::bind() выполняет new/delete.

Автор: phprus 2.8.2011, 15:12
Цитата(boostcoder @  2.8.2011,  17:41 Найти цитируемый пост)
каждый boost::bind() выполняет new/delete. 

Интересно. Не знал об этой детали реализации.

Я правильно понимаю, что пример http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/example/allocation/server.cpp показывает, как можно избежать таких new/delete?
Пожалуй надо более детально разобраться в этом механизме.

Автор: boostcoder 2.8.2011, 15:17
Цитата(phprus @  2.8.2011,  15:12 Найти цитируемый пост)
Я правильно понимаю, что пример http://www.boost.org/doc/libs/1_47_0/doc/h...tion/server.cpp показывает, как можно избежать таких new/delete?

да. но в нем не хватает почему-то InvocationStrategy...
вот пример на базе того кода: http://liveworkspace.org/code/d8f9abec20e6d54bd590d53f53f373a4

Добавлено через 2 минуты и 32 секунды
я еще планирую по этому же принципу реализовать ReusingAllocators/ReusingSessions.

Автор: phprus 2.8.2011, 16:14
boostcoder
Спасибо за пример!

Ты случайно не знаешь, как выведено число 1024 в качестве размера для aligned_storage?

Автор: boostcoder 2.8.2011, 16:31
Цитата(phprus @  2.8.2011,  16:14 Найти цитируемый пост)
не знаешь, как выведено число 1024 в качестве размера для aligned_storage?

не знаю :(
я тоже задался этим вопросом, но все мои гугления ни к чему ни привели..
в общем, я в метод allocate() добавил вывод size и варнинга в логгер, в случае если запрашивается объем больше чем есть у аллокатора. но как показывает тест, у меня всегда выделяется 32 байта smile 
чтоб понять от чего зависит объем, нужно лезть в изучение boost::bind().

Автор: boostcoder 2.8.2011, 16:48
Цитата(boostcoder @  2.8.2011,  16:31 Найти цитируемый пост)
нужно лезть в изучение boost::bind().

хотя что-то мне подсказывает, что выделяемый объем будет равен "sizeof(declspec(boost::bind(...)))"...

Автор: boostcoder 2.8.2011, 20:29
кстати, если некоторые хэндлеры гарантировано не выполняются одновременно, то для них можно использовать один аллокатор.
т.е. если на сокете происходит чтение одновременно с записью - то аллокаторов нужно два.
это очень критично. иначе получишь segfolt`ы и access_violation`ы о причинах которых будешь долго раздумывать smile 

Автор: phprus 2.8.2011, 20:42
Скажи пожалуйста, а если из одного хэндлера вызывается асинхронная операция с другим хэндлером (на одном сокете), то это считается одновременно?

Автор: boostcoder 2.8.2011, 20:53
Цитата(phprus @  2.8.2011,  20:42 Найти цитируемый пост)
то это считается одновременно?

если я тебя правильно понял, ты говоришь о такой ситуации:
Код

void start_read(...) {
   boost::bind(
      &this_type::on_read,
      this,
      ...
   );
}


void on_read(...) {
   boost::bind(
      &this_type::start_write,
      this,
      ...
   );
}

если да - то это не считается одновременно. ибо в доке сказано:
Цитата

The implementation guarantees that the deallocation will occur before the associated handler is invoked, which means the memory is ready to be reused for any new asynchronous operations started by the handler.

что и подтверждает вывод этого кода: http://liveworkspace.org/code/d8f9abec20e6d54bd590d53f53f373a4

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