![]() |
|
Модераторы: Daevaorn |
![]()
|
|
| boostcoder |
|
|||
![]() pattern`щик ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 5458 Регистрация: 1.4.2010 Репутация: 49 Всего: 110 |
всем бодрого утра.
итак. имеется протокол ввода-вывода. пакет состоит из заголовка фиксированного размера в котором содержится всякая инфа, размер тела данных, и, собственно, тело. в данный момент, чтение пакета происходит следующим образом: 1)читается заголовок, 2)из заголовка узнается объем тела, 3)читается тело. тут, мне не нравится то, что для чтения одного пакета приходится выполнять две операции чтения. а это довольно длинная цепь вызовов, в придачу, завершающаяся системным вызовом. пришла такая идея: сторона, которая запрашивает данные, не должна читать их напрямую из сокета. алгоритм: для каждого сокета выделяется буфер. для каждого буфера нужны два итератора: 1)итератор записи, 2)итератор чтения. сокет и буфер должны находится на одном уровне. условно, назовем этот уровень нижним. так же, у нас есть уровень, обращающийся к нижнему за данными. назовем его верхним уровнем. начальное состояние: сокет открыт. буфер пуст. чтения не происходит. итак. верхний уровень делает запрос к нижнему чтоб получить порцию данных. нижний уровень проверяет, имеется ли в буфере достаточно данных, и если да - верхнему уровню передается массив(пока не решил как, тупо копированием из буфера нижнего уровня в буфер верхнего, или путем передачи итератора(ов)). если это первое обращение за данными - выполняем запрос в сокет, и таким образом запускаем цикл пополнения буфера. тут, нужен контроль следующих условий: 1. итератор чтения не должен перегнать итератор записи. 2. итератор записи не должен перегнать итератор чтения. при такой реализации мы получаем следующую информацию основанную на позициях итераторов: 1. позиция итератора чтения нам говорит о том, сколько данных было прочтено. 2. дистанция от итератора чтения до итератора записи = объем доступных в буфере данных. 3. дистанция от итератора записи до итератора чтения = пространство доступное для записи. при создании операции асинхронного чтения, в качестве буфера передаем итератор записи, а объем определяем согласно пункту 3 предыдущего параграфа. операция асинхронного чтения читает из сокета все доступные данные, но не более чем было запрошено. в общем, это изложение мысли как есть, без проработки и связи. наверняка что-то не учел, не додумал.. собственно тема создана для того, чтоб выслушать мнение форумчан, и, возможно, выявить "узкие" места и недочеты. всем спасибо. Это сообщение отредактировал(а) boostcoder - 9.11.2011, 11:10 |
|||
|
||||
| bsa |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 63 Всего: 196 |
Ты уверен, что именно на чтение сокета убивается большая часть ресурсов процессора? Что-то мне подсказывает, что это не так... Скажи, а как именно данные оправляются? Есть подозрение, что отправляются они так же, как принимаются - сначала заголовок, а затем тело. Таким образом, есть у меня подозрение, что сначала выполнится операция чтения, возвращающая только заголовок, а уже затем чтение тела, так как придут в разных пакетах... Или я не прав (в сетях я не большой гуру)?
|
|||
|
||||
| boostcoder |
|
|||
![]() pattern`щик ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 5458 Регистрация: 1.4.2010 Репутация: 49 Всего: 110 |
||||
|
||||
| mabrarov |
|
|||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 100 Регистрация: 12.1.2011 Где: Казань Репутация: 8 Всего: 9 |
Утра бодрого и Вам, boostcoder.
asio::buffered_read_stream и asio::buffered_write_stream и, наконец, asio::buffered_stream. Это позволит сэкономить на обращениях к сокету. В принципе, Вы этого и хотели. Но я предпочитаю иной вариант, потому что каждый async-запрос к buffered_stream все равно выливается в копии handler и пр. мелочевку. Для примера возьму чтение (алгоритм для записи выводим как-то по аналогии). Итак, берем кольцевой буфер (ma::cyclic_buffer), размер которого >= максимальный размер сообщения (в идеале >>>). В каждой итерации пытаемся читать из сокета - (async_)read_some - столько, сколько есть/осталось свободного места в буфере. Отдельным объектом (class message_parser) парсим то, что считалось в буфер. При этом, на выходе message_parser::parse_some м/б [0..n] сообщений. message_parser есть КА и хранит текущее состояние парсинга с тем, чтобы при каждом последующем вызове не парсить всю последовательность с начала. Вообще в message_parser::parse_some каждый раз передаются только новые (вновь поступившие) данные (buffer_sequence или, точнее, ma::cyclic_buffer::const_buffers_type). Если message_parser::parse_some (худший случай) длительный, то его можно проводить параллельно, но это уже больше похоже на извращение. Чтение (async_read_some) всегда выполняется в фоне. Т.е. сначала начинаем следующую итерацию чтения (start_async_read_some), а уже потом вызываем message_parser::parse_some. Поэтому, если уж message_parser::parse_some длительный, то эффективнее будет async_read_some + asio::null_buffers + неблокирующий режим. Такой режим, между прочим, считается последним спасением при высоких нагрузках и IOCP - видел англоязычную запись в каком-то блоге, посвященном IOCP и высоким нагрузкам (до сих пор найти не могу - может кто подскажет?). Еще такой момент - все, что прошло через message_parser::parse_some, может 1. считаться "свободным", т.е. освобождается в кольцевом буфере под запись новых данных (ma::cyclic_buffer::commit) или 2. message_parser::parse_some может дополнительно сообщать, сколько байт от начала переданной ему buffer_sequence можно считать "проглоченными парсером", т.е. "свободными". Если не устраивает кольцевой буфер, то можно взять обычный. Но тогда в случае [2] (см. выше) нужно предусмотреть shift - сдвиг распарсенных, но не извлеченных из буфера данных в начало буфера с тем, чтобы обеспечить ненулевой свободный остаток в конце буфера. Можно делать сдвиг не всегда, а только в тех случаях, когда "свободный остаток в конце буфера" < threshold (я так и делал когда-то). Все вышеописанное есть тот же самый buffered_stream, но с выделением логики парсинга из логики чтения. "100-пудов" это всем было известно и без меня. Но захотелось заодно и обсудить. P.S. ma::cyclic_buffer. Это сообщение отредактировал(а) mabrarov - 9.11.2011, 11:20 |
|||
|
||||
| newbee |
|
|||
![]() Бревно ![]() ![]() Профиль Группа: Участник Сообщений: 703 Регистрация: 24.8.2011 Репутация: 4 Всего: 19 |
ОП прочла по диагонали, по-моему ты слишком усложняешь. Скорее всего цпу забивает процесс разбора пакета, а не чтения из сокета. Но даже если так, можно сделать много проще, ведь скорость обмена данными у тебя очень велика. Читаешь из сокета большой (в идеале заведомо больший, чем возможная максимальная длина пакета*) кусок данных, натравливаешь на него парсер. Опять читаешь буфер, продолжаешь парсить, и т.д. Процесс можно пустить в два потока: один парсит имеющийся буфер, другой читает следующий буфер. Если парсер будет сильно не успевать за читалкой, можно сделать пул парсеров.
*под пакетом я имею в виду твой фрейм поверх IP/UDP/TCP/etc. -------------------- You're face to face With man who sold the world |
|||
|
||||
| mabrarov |
|
|||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 100 Регистрация: 12.1.2011 Где: Казань Репутация: 8 Всего: 9 |
"Если что" (если я непонятно выразился), я имел в виду то же самое. У Вас получилось описать это проще Это сообщение отредактировал(а) mabrarov - 9.11.2011, 11:29 |
|||
|
||||
| newbee |
|
|||
![]() Бревно ![]() ![]() Профиль Группа: Участник Сообщений: 703 Регистрация: 24.8.2011 Репутация: 4 Всего: 19 |
mabrarov, когда я начинала писать, твоего сообщения еще не было. Так что это не плагиат
-------------------- You're face to face With man who sold the world |
|||
|
||||
| mabrarov |
|
|||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 100 Регистрация: 12.1.2011 Где: Казань Репутация: 8 Всего: 9 |
Да какой уж тут плагиат. Не "алгоритм Дейкстры" же. Просто у меня получилось запутанно. Вдруг кого смутит. |
|||
|
||||
| baldina |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3433 Регистрация: 5.12.2007 Где: Москва Репутация: 32 Всего: 101 |
те 12 процентов видимо из-за системного вызова, а не разбора заголовка. тем не менее буферизация и параллельная работа с буфером мне кажется более удачной идеей чем прыжки с сокетами и итераторами |
|||
|
||||
| mabrarov |
|
|||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 100 Регистрация: 12.1.2011 Где: Казань Репутация: 8 Всего: 9 |
То, что описал boostcoder, и еcть буферизация. Тот же самый asio::buffered_stream. |
|||
|
||||
| boostcoder |
|
|||
![]() pattern`щик ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 5458 Регистрация: 1.4.2010 Репутация: 49 Всего: 110 |
нельзя читать больше, чем доступно для чтения без блокировки. иначе есть риск того, что я не смогу обработать первый пришедший пакет из-за того, что мы попросили прочитать 200 пакетов. это тоже есть. и от этого не избавится. я же хочу избавится от одного асинхронного чтения, что, по предсказанию профайлера, подарит мне более 12 процентов освободившихся ресурсов. угу. и из-за всего предшествующего. т.е. байндеры, io_service, new/delete, и т.д. про asio::buffered_stream никогда не читал. почему-то... в общем, сейчас "переварю" мысли/идеи.... |
|||
|
||||
| mabrarov |
|
|||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 100 Регистрация: 12.1.2011 Где: Казань Репутация: 8 Всего: 9 |
У Вас async_read_some или async_read? async ли вообще? Потому что async-операции вполне позволяют Вам разбирать то, что уже пришло, параллельно чтению. Это сообщение отредактировал(а) mabrarov - 9.11.2011, 12:27 |
|||
|
||||
| boostcoder |
|
|||
![]() pattern`щик ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 5458 Регистрация: 1.4.2010 Репутация: 49 Всего: 110 |
async_read() дело в том, что разбирать я начинаю в хендлере. а хендлер вызовется только тогда, когда будет прочитано указанное кол-во байт. т.е. к примеру мы указываем прочитать 200 байт, а в сокете есть только 100. так вот эти 100 я не могу обработать, потому что не вызывается хендлер. тут наверное правильней использовать async_read_some()... значит нужно менять архитектуру. Добавлено @ 13:04 почитал я asio::buffered_stream(если можно это назвать чтением)... в доке, вообще нет никакого описания поведения или принципа работы. ни каким образом получает данные, ни кто такой Arg полез в исходники. Это сообщение отредактировал(а) boostcoder - 9.1.2012, 15:49 |
|||
|
||||
| bsa |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 63 Всего: 196 |
Я давно заметил, что Asio отличается особой полнотой и глубиной описания... Не то что всякие Qt... |
|||
|
||||
| mabrarov |
|
||||||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 100 Регистрация: 12.1.2011 Где: Казань Репутация: 8 Всего: 9 |
Вот именно эта часть вообще недокументированна. Вроде бы раньше это были служебные (внутренние для Asio) классы. Сам все хочу почитать их исходники, но никак не сделаю это последовательно и целиком. Arg - это параметр для конструктора lower_layer. В случае, если lower_layer есть asio::ip::tcp::socket, то Arg - это asio::io_service&. Тут все аналогично asio::ssl::stream. Это вообще общая техника для оберток в Asio. Добавлено @ 13:55
Естественно. У Qt есть коммерческая версия + Trolltech/Nokia. Там есть кому писать и что платить "писателям". Ну напишете наконец Chris-у совместную "петицию" с указанием, что непонятно и где дописать/уточнить/поправить. В рассылке по Asio пока не было такого письма. В одной "российской" компании на Y мне (asio samples) сказали: "А чего там писать? И так все понятно - проще некуда". И в Qt встречаются плохо документированные места. Попадалось использование !QObject raw pointer без объяснения, кто будет владеть ресурсом и кто будет его удалять/освобождать. Добавлено @ 13:58
Можно попробовать asio::async_read + asio::transfer_at_least(минимальный размер пакета). В той же компании Y не используют asio::async_xxx (мне так сказали - сам не проверял Это сообщение отредактировал(а) mabrarov - 9.11.2011, 14:10 |
||||||
|
|||||||
![]()
|
| Правила форума "С++:Общие вопросы" | |
|
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
| 1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |