Основная проблема наши студентов в том что они считают что где-то там далеко всё иначе :) Сразу вспоминается статья Steve Yegge, где он упоминал о том какие выпускники приходят устраиваться в гугл/амазон/где он там ещё работал, вроде и Ph.D и выпустились из МТИ, а даже базовых знаний то не особо.
>Да, приложение работает не с этими буферами
Я просто с этого начал, так как хотел подчеркнуть бессмысленость aio в связке с сокетами ;)
>У вас client-per-thread?
нет, у меня много клиентов на тред, но так как я всё равно не смогу одним тредом работать в двух местах :) то зачем мне больше одного буффера(iovec, чтобы было проще отцеплять заполненые и необработаные) на тред…
Если в данный момент времени нехватает данных(пришло пол пакета), то я отцепляю из вектора полузаполненный буффер и отдаю клиенту, а когда нужно опять делать сискол — цепляю его первым в iovec… Нет смысла городить пустые буффера на каждого клиента :)
>И я не совсем понял насчет
>>что мы ко всему этому на каждый запрос чтения выделяем буффер
когда вы делаете WSARead — вы сразу же получаете результат или ждёте пока IOCP не скажет что он заполнен? Если второй вариант, то да — вы создаёте пустой и бессмысленый буффер ;)
У неблокирующего варианта я сразу получаю результат и могу по нему работать. А отработав — вернуть буффер, чтобы им попользовались остальные клиенты ;)
ну и по количеству сисколов :)
у меня вектор буфферов(буффер = странице памяти) на тред имеет размер = SO_RCVBUF
и когда меня оповещают о том что у сокета появились какие-то данные в буффере, я почти уверен что мне будет достаточно одного readv :)
При использовании aio я просто не могу себе позволить выделять такие большие буффера на каждый сокет. И сколько тогда раз придётся просить ядро заполнить мне буффер, а потом дожидаться ответа?!
так, хочу разобраться :)
у ядра есть свои буфферы для сокетов(которые достаточно хорошо реализованы и кушают память только по потребности), когда они заполняются — нужно оповестить приложение, которое жаждит знать о том что в сокет поступили данные. Так вот зачем нам тут aio? Если неблокирующий сискол на чтение(есть даже какие-то подвижки в сторону zero-copy — vmsplice) сможет сейчас же забрать всё что находится в буффере ядра и продолжить выполнение.
aio же подразумевает то что мы ко всему этому на каждый запрос чтения выделяем буффер, о заполнении которого нас оповещают. Но это же во много хуже по сравнению с неблокирующим вариантом, где мне достаточно одного буффера на тред(при условии что мы сразу обработаем данные, в реальности всё конечно же чуточку сложнее).
в чём преимущество у aio с сетью(не файловый io, там всё сложнее и поэтому вполне разумно)?! я вижу только недостатки у подобного подхода.
я наверное один из тех кто неправильно понял как работают IOCP :)
не могли бы вы вкратце описать как прочитать из сокета?
ну например на линуксе это выглядело бы так:
создаём сокет
добавляем IN/OUT события для данного сокета
loop:
дожидаемся событий
читаем из сокета
обрабатываем полученные данные
goto loop;
Мне почему-то казалось, что когда используется модель IOCP, то нужно делать асинхронные вызовы и передавать буффер?
как-то так:
создаём сокет
аллокэйтим буффер
loop:
делаем асинхронный запрос на чтение и передаём буффер
дожидаемся событий
обрабатываем полученные данные
goto loop;
>Помимо структуры OVERLAPPED вам потребуется передавать в поток еще кое-какую IO-информацию — например, адрес буфера, его длину или даже сам буфер.
Не убогонько ли для масштабируемых серверов? На каждый слабоактивный сокет выделять целый буффер…
В нашем юниксовом селе обычно дело обстоит примерно так(далее про неблокирующие сокеты) — есть у нас один большой вектор с указателями на 4кб буфферы(обычно равен максимальному размеру буффера сокета, чтобы одним сисколом readv всё утащить в юзерспэйс). Ну и когда приходят какие-то пакеты, если они маленькие, то сразу обрабатываются и наш большой векторо-буффер сразу опять в строю, если не можем сразу обработать, то передаём занятые 4кбайтовые буффера во владение данных связаных с клиентом и аллокэйтим ещё чистеньких страничек для следующего readv ;)
p.s. не писал серверов под винду, если не прав — поправьте, тк самому интересно узнать.
>1.1) goto — зло. аргументировать?
>1.2) макросы — зло :-) аргументировать?
Не надо :) Этими аргументами из книжек по плюсам воспитывают крестовых новобранцев, но мы то с вами уже понимаем в каких ситуациях их использование оправдано ;) Мы не живём в идеальном миру о котором мечтают идеологи плюсов, нам приходиться решать реальные проблемы с тормозными, багнутыми цпп компиляторами итд.
>вот на счёт тормознутости — очень не уверен.
я про zero-cost (zcx) исключения, которые с помощью libunwind'а реализованы(про виндовые средства разработки ничего не знаю). Там большой оверхед только когда происходит исключение, зато в обычной ситуации всё шустренько.
>Вопрос, что и когда(!) с ними делать.
Если пишите «exception-safe» код, то да… Но моя практика общения с типичными кодерами, вроде меня, показала что писать «exception-safe» код на ++ многие не умеют.
А иначе код ничем не будет отличаться от варианта на Си.
>3.1) На сколько я понимаю. В С++ исключения разрабатывались… универсальными, но всё же больше невосстанавливающимися, чем восстанавливающимися. Достаточно вспомнить про проблемы с исключениями в деструкторах.
Проблем у них много, но это не значит что от исключений приложение должно умирать (или мы про какое-то другое восстановление?)
>Скорее всего код получится тормознее из-за кучи if'ов, если используются zcx exceptions(вроде так их обозвали в gcc — плохо разбираюсь в этой теме).
Плохо сформулировал :) Код на Си с ifами получится тормознее, чем на крестах с zero-cost исключениями.
>В С++ вы можете заключить всё это дело в один большой try и отделить обработку исключений
Втыкаем label exception: отвечающий за обработку ошибок и после каждого write делаем if..goto прямиком туда (некоторые заварачивают всё в красивые макросы). Скорее всего код получится тормознее из-за кучи if'ов, если используются zcx exceptions(вроде так их обозвали в gcc — плохо разбираюсь в этой теме).
И с райтом плохой пример, так как он возвращает всякие EAGAIN(или вы их тоже эксепшенами ловите?)… но это редкое исключение из правил :)
>а обработку ошибок (исключений) оставить на усмотрение пользователя вашей библиотеки.
В наших кодинг гайдлайнс уже много лет нет пунктика — игнорировать ошибки ;)
>Надо ли восстанавливаться после исключений — это большой и неоднозначный вопрос.
хм… всегда казалось, что в большинстве случаев надо восстанавливаться.
Но я про другой «exception-safe»
>(Я — разделение нормального хода программы и обработки ошибок; разве это разделение делает код запутанным?)
На Си обычно тоже разделяют ход програмы и обработку ошибок. Тут уже больше зависит от того кто пишет код.
>Мне не кажется, что конструкторы, указатели или исключения являются какими-то «хитростями»…
Ну конструкторы и указатели не являются хитростями, а вот писать «exception-safe» код на Си++ — это уже для меня шаманство, из-за которого мои програмы текут, падают итд :) Я понимаю, что это недостаток моих знаний, но сколько ++ников реально умеет писать «exception-safe» код?
>Это делает код запутанным и трудным для понимания и поддержки.
А мне казалось что это делает код понятным, так как не надо держать в голове все хитрости крестов.
Может у меня память плохая, но когда делаю что-то на крестах, то не в состоянии в голове удерживать все эти мелочи, которые описаны в куче in-depth книг. А как у вас с этим?
вот ещё один такой www.blerp.com
Я просто с этого начал, так как хотел подчеркнуть бессмысленость aio в связке с сокетами ;)
>У вас client-per-thread?
нет, у меня много клиентов на тред, но так как я всё равно не смогу одним тредом работать в двух местах :) то зачем мне больше одного буффера(iovec, чтобы было проще отцеплять заполненые и необработаные) на тред…
Если в данный момент времени нехватает данных(пришло пол пакета), то я отцепляю из вектора полузаполненный буффер и отдаю клиенту, а когда нужно опять делать сискол — цепляю его первым в iovec… Нет смысла городить пустые буффера на каждого клиента :)
>И я не совсем понял насчет
>>что мы ко всему этому на каждый запрос чтения выделяем буффер
когда вы делаете WSARead — вы сразу же получаете результат или ждёте пока IOCP не скажет что он заполнен? Если второй вариант, то да — вы создаёте пустой и бессмысленый буффер ;)
У неблокирующего варианта я сразу получаю результат и могу по нему работать. А отработав — вернуть буффер, чтобы им попользовались остальные клиенты ;)
у меня вектор буфферов(буффер = странице памяти) на тред имеет размер = SO_RCVBUF
и когда меня оповещают о том что у сокета появились какие-то данные в буффере, я почти уверен что мне будет достаточно одного readv :)
При использовании aio я просто не могу себе позволить выделять такие большие буффера на каждый сокет. И сколько тогда раз придётся просить ядро заполнить мне буффер, а потом дожидаться ответа?!
у ядра есть свои буфферы для сокетов(которые достаточно хорошо реализованы и кушают память только по потребности), когда они заполняются — нужно оповестить приложение, которое жаждит знать о том что в сокет поступили данные. Так вот зачем нам тут aio? Если неблокирующий сискол на чтение(есть даже какие-то подвижки в сторону zero-copy — vmsplice) сможет сейчас же забрать всё что находится в буффере ядра и продолжить выполнение.
aio же подразумевает то что мы ко всему этому на каждый запрос чтения выделяем буффер, о заполнении которого нас оповещают. Но это же во много хуже по сравнению с неблокирующим вариантом, где мне достаточно одного буффера на тред(при условии что мы сразу обработаем данные, в реальности всё конечно же чуточку сложнее).
в чём преимущество у aio с сетью(не файловый io, там всё сложнее и поэтому вполне разумно)?! я вижу только недостатки у подобного подхода.
не могли бы вы вкратце описать как прочитать из сокета?
ну например на линуксе это выглядело бы так:
создаём сокет
добавляем IN/OUT события для данного сокета
loop:
дожидаемся событий
читаем из сокета
обрабатываем полученные данные
goto loop;
Мне почему-то казалось, что когда используется модель IOCP, то нужно делать асинхронные вызовы и передавать буффер?
как-то так:
создаём сокет
аллокэйтим буффер
loop:
делаем асинхронный запрос на чтение и передаём буффер
дожидаемся событий
обрабатываем полученные данные
goto loop;
Не убогонько ли для масштабируемых серверов? На каждый слабоактивный сокет выделять целый буффер…
В нашем юниксовом селе обычно дело обстоит примерно так(далее про неблокирующие сокеты) — есть у нас один большой вектор с указателями на 4кб буфферы(обычно равен максимальному размеру буффера сокета, чтобы одним сисколом readv всё утащить в юзерспэйс). Ну и когда приходят какие-то пакеты, если они маленькие, то сразу обрабатываются и наш большой векторо-буффер сразу опять в строю, если не можем сразу обработать, то передаём занятые 4кбайтовые буффера во владение данных связаных с клиентом и аллокэйтим ещё чистеньких страничек для следующего readv ;)
p.s. не писал серверов под винду, если не прав — поправьте, тк самому интересно узнать.
>1.2) макросы — зло :-) аргументировать?
Не надо :) Этими аргументами из книжек по плюсам воспитывают крестовых новобранцев, но мы то с вами уже понимаем в каких ситуациях их использование оправдано ;) Мы не живём в идеальном миру о котором мечтают идеологи плюсов, нам приходиться решать реальные проблемы с тормозными, багнутыми цпп компиляторами итд.
>вот на счёт тормознутости — очень не уверен.
я про zero-cost (zcx) исключения, которые с помощью libunwind'а реализованы(про виндовые средства разработки ничего не знаю). Там большой оверхед только когда происходит исключение, зато в обычной ситуации всё шустренько.
>Вопрос, что и когда(!) с ними делать.
Если пишите «exception-safe» код, то да… Но моя практика общения с типичными кодерами, вроде меня, показала что писать «exception-safe» код на ++ многие не умеют.
А иначе код ничем не будет отличаться от варианта на Си.
>3.1) На сколько я понимаю. В С++ исключения разрабатывались… универсальными, но всё же больше невосстанавливающимися, чем восстанавливающимися. Достаточно вспомнить про проблемы с исключениями в деструкторах.
Проблем у них много, но это не значит что от исключений приложение должно умирать (или мы про какое-то другое восстановление?)
Плохо сформулировал :) Код на Си с ifами получится тормознее, чем на крестах с zero-cost исключениями.
Втыкаем label exception: отвечающий за обработку ошибок и после каждого write делаем if..goto прямиком туда (некоторые заварачивают всё в красивые макросы). Скорее всего код получится тормознее из-за кучи if'ов, если используются zcx exceptions(вроде так их обозвали в gcc — плохо разбираюсь в этой теме).
И с райтом плохой пример, так как он возвращает всякие EAGAIN(или вы их тоже эксепшенами ловите?)… но это редкое исключение из правил :)
>а обработку ошибок (исключений) оставить на усмотрение пользователя вашей библиотеки.
В наших кодинг гайдлайнс уже много лет нет пунктика — игнорировать ошибки ;)
>Надо ли восстанавливаться после исключений — это большой и неоднозначный вопрос.
хм… всегда казалось, что в большинстве случаев надо восстанавливаться.
Но я про другой «exception-safe»
Ну и может кто-то ещё не видел FQA :)
C++ FQA >> exceptions
На Си обычно тоже разделяют ход програмы и обработку ошибок. Тут уже больше зависит от того кто пишет код.
>Мне не кажется, что конструкторы, указатели или исключения являются какими-то «хитростями»…
Ну конструкторы и указатели не являются хитростями, а вот писать «exception-safe» код на Си++ — это уже для меня шаманство, из-за которого мои програмы текут, падают итд :) Я понимаю, что это недостаток моих знаний, но сколько ++ников реально умеет писать «exception-safe» код?
А мне казалось что это делает код понятным, так как не надо держать в голове все хитрости крестов.
Может у меня память плохая, но когда делаю что-то на крестах, то не в состоянии в голове удерживать все эти мелочи, которые описаны в куче in-depth книг. А как у вас с этим?
man tee