engelbart
Mike
Жги
engelbart
Если вас не затруднит
engelbart
Но в целомс я часто про эти аллокации встречаю, хотелось бы в деталях понять.
Mike
Сколько можно про js object notation слушать)
Nikolay
вобщем возьмем обычный TCP сервер и к нему клиента. надо обменятся данными. Открываем сокет как TCP (он потоковый в отличии от UDP). Обычно как делают с JSON - сериализнули в массив байтов и отправили в сокет сначало размер данных которые будем слать и сам сериализованный JSON (или еще чет кроме JSON) в массиве байт: [type: byte] [byteArrayLength: long] [array: []byte] - такая вот последовательность. на другом конце принимаем 8 байт размера и потом аллоцируем буфер такого размера и в него вычитываем, после этого десериализуем. вот аллокация буфера она лишняя получается. msgpack можно читать прям из сокета не передавая никаких дополнительных длинн пакета - сериализуешь прямо в сокет и читаешь прям обьект попутно десериализуя из сокета. Там сам формат располагает к этому - посмотрите спецификацию понятно будет, если вкратце то считав из сокета один байт первый ты знаешь сколько байт будет дальше за ним, потихоньку читаешь и превращаешь в обьект сразу.
Nikolay
в формате json нет данных о том сколько байт идет дальше, в msgpack эта инфа есть прям в его формате представления, поэтому никаких буферов аллоцировать не надо чтобы принять большой msgpack. кидаешь json по TCP на мегабайт - аллоцируешь мегабайт буфер в него читаешь потом десериализуешь, а если кидать msgpack то кидаешь его просто так как есть а на другом конце вычитываешь прям в реальный обьект без буфера на мегабайт
Daniel
про json - чушь
Daniel
в нем есть открывающая и закрывающая скобки
Daniel
поэтому размер слать не надо
Daniel
и про msgpack слегка гонево
Daniel
алоцировать не надо не из-за размера, а из-за того, что протокол бинарный
Daniel
в пакете на своих местах ровно те байты ровно в том количестве, что требуются для соответствующего типа данных
Daniel
поэтому он zero-copy
Nikolay
он не zero-copy ни разу это раз - тут совет разобраться что такое zero-copy к ним относятся такие форматы как capnproto, советую глянуть реализацию или хотябы поверхностно разобраться что такое zero-copy
engelbart
Ну вот видите, а вы говорили не пятница дескать, зря пройдет.
А ВОТ ТЕПЕРЬ ПАБЛИК
шо то в го чате напряженка какая то
Nikolay
остальное тоже у тебя смешалось и люди и кони. Я хотел избежать хотябы в этот раз лишних обьяснений. json не имеет в формате своем никаких данных сколько дальше идет байт, поэтому быстро считать из сокета тупо не возможно. теоретически можно читать побайтово из сокета JSON на стейт машине - будет невероятно медленно, поэтому потоковых десериализаторов под JSON я не видел, которые вешают прям на TCP stream.
Daniel
Nikolay
да хоть на C# - проще для восприятия. есть реализация zero-copy сериализатора capnproto
Nikolay
от одного крутого товарища, могу ссылочку сейчас нагуглить
Daniel
engelbart
Ну мене кажется даже я понял. ну втсретили скобку открывающуюся, и что делать то, понятно что придется читать в буфер. и возможно буфер ещё и увеличивать пока не встретим закрывающую
Daniel
не надо
Daniel
я, вообще-то, хорошо понимаю, как работает tcp
Daniel
видимо, даже лучше тебя
Daniel
даже если ты будешь из сокета вычитывать по одному байту - реальное чтение будет из буфера, в который пришел пакет
Nikolay
ладно, буду обьяснять и по tcp тогда, чтоб остальные кто прочтет не ввелись в заблуждение. начнем с простого сейчас, но предварительно кину ссылку на zero-copy
engelbart
#msgpack , я тут себе закладочку поставлю, если вы не против, а то усну в процессе, потом из за флуда не найти
Nikolay
https://capnproto.org/news/2014-12-15-capnproto-0.5-generics-msvc-java-csharp.html
Nikolay
тут и на С# от Marc Gravell и на Java, что полагаю будет несколько ближе тебе
Nikolay
теперь про TCP: этот протокол осуществляет надежную передачу потока (!!!) байт с одной стороны в другую. то что под капотом это бьется на пакеты с сиквенсом и прочими делами по определенным правилам, дальше упаковывается в IP пакет и дальше в L2 пакет это и так всем понятно - любой нынче кто хочет все это может увидеть каким-нибудь Wireshark без проблем или еще чего. Но на выходе у TCP сокета поток байт бесконечный до закрытия соединения (Называется TCP streams) - есть логическое начало с открытием соединения и есть логический конец после закрытия и все что можно делать с ним это читать из этого потока и в него писать, а также спрашивать у API если ли чего сейчас там в буфере стрима в данный момент, если нет то ждать пока будет например. Передавая какой-то массив данных чтобы из этого массива сделать логическую единицу нам нужно либо передать размер и потом вычитывать этот размер, либо использовать такие же потоковые сериализаторы как msgpack, либо мудрить что-то свое. Как обычная работа с TCP сокетом устроена? Берем узнаем есть че прочитать и читаем сколько есть. Если не рвать соединение то где конец одной логической единицы? Как его узнать? Ответ: никак, если не передать эту информацию в этом же потоке - нужно описывать свою логику ОТ и ДО. Как отослать 10 файлов через TCP сокет? Ответ: шлем его длинну и потоком все его содержимое, на другом конце принимаем длинну и вычитываем ровно столько из сокета, потом ждем опять длинну следующего и вычитываем этот размер из сокета. Это блин очень просто для понимания. И да важный момент - если данных в стриме сейчас нет это не значит что это конец передачи - это вполне себе штатная ситуация для сетей как медленный канал или медленно отправитель отсылает или еще чего. Надеюсь после этого обьяснения вопросов не останется, а если останутся - гугл и книжки в помощь, а лучше немного практики и самому разок написать что-то поверх TCP голого
Roman
теперь про TCP: этот протокол осуществляет надежную передачу потока (!!!) байт с одной стороны в другую. то что под капотом это бьется на пакеты с сиквенсом и прочими делами по определенным правилам, дальше упаковывается в IP пакет и дальше в L2 пакет это и так всем понятно - любой нынче кто хочет все это может увидеть каким-нибудь Wireshark без проблем или еще чего. Но на выходе у TCP сокета поток байт бесконечный до закрытия соединения (Называется TCP streams) - есть логическое начало с открытием соединения и есть логический конец после закрытия и все что можно делать с ним это читать из этого потока и в него писать, а также спрашивать у API если ли чего сейчас там в буфере стрима в данный момент, если нет то ждать пока будет например. Передавая какой-то массив данных чтобы из этого массива сделать логическую единицу нам нужно либо передать размер и потом вычитывать этот размер, либо использовать такие же потоковые сериализаторы как msgpack, либо мудрить что-то свое. Как обычная работа с TCP сокетом устроена? Берем узнаем есть че прочитать и читаем сколько есть. Если не рвать соединение то где конец одной логической единицы? Как его узнать? Ответ: никак, если не передать эту информацию в этом же потоке - нужно описывать свою логику ОТ и ДО. Как отослать 10 файлов через TCP сокет? Ответ: шлем его длинну и потоком все его содержимое, на другом конце принимаем длинну и вычитываем ровно столько из сокета, потом ждем опять длинну следующего и вычитываем этот размер из сокета. Это блин очень просто для понимания. И да важный момент - если данных в стриме сейчас нет это не значит что это конец передачи - это вполне себе штатная ситуация для сетей как медленный канал или медленно отправитель отсылает или еще чего. Надеюсь после этого обьяснения вопросов не останется, а если останутся - гугл и книжки в помощь, а лучше немного практики и самому разок написать что-то поверх TCP голого
+много
Roman
Вот честно
Roman
И нет в tcp способа узнать что данные отправлены или получены
Roman
Точнее, отправленные данные получены
Daniel
что приложение данные вычитало - это, конечно, никак не выяснить
Daniel
но при чем ту json и отправка длины?
Daniel
у json есть маркер конца (как и у smtp, например)
Daniel
не надо, я в курсе
Nikolay
если вкратце это невероятно медленно, даже если лепить промежуточный стрим с буфером, который немного прироста даст производительности
Nikolay
чтение будет побайтовым на стейт машине - никак иначе
Daniel
а оно все равно таким будет, извини
Daniel
такой уж формат
Nikolay
да, именно формат
Daniel
конечно, по сисколу на байт - это будет жестко
Daniel
но в go это, слава создателям, не так
Nikolay
да там можно через доп буфер читать, что несколько ускорит, но один хрен медленно. и то и то ручками делал сам - и парсер json и msgpack, который вешал прям на стрим через буферизированный поток для ускорения сетевого взаимодействия
Daniel
что медленно-то?
Nikolay
поэтому выше я написал че делаем если шлем json и чего делаем если берем msgpack
Nikolay
передача + десериализация без лишней аллокации - вот что медленно
Daniel
json сам по себе никто не шлет, это правда. или http, или websocket
Daniel
и тот, и другой снабжают сообщение размером.
Nikolay
а я про потоковую передачу
Nikolay
выше тему эту начал
🏳️ Phil
Daniel
но, вообще-то, никто не мешает с одной стороны json в сокет засунуть, а с другой - вычитать
Nikolay
я хотел избежать обьяснений почему так никто не делает
Nikolay
поэтому написал как делают и чем хорош при этом msgpack
Daniel
Nikolay
и?
Daniel
"никто не делает" и "в стандартной либе есть соответствующий метод" - противоречат друг другу, правда?
Nikolay
ща все обьясню)
Daniel
кому?!
Daniel
если мне - не надо, я в курсе
engelbart
Я б послушал
Daniil
Я бы тоже. А то у меня целый TCP сервер общается по JSON
Oleh
кто то работал с go-sciter ? норм вещь или дрянь редкостная?
Daniel
серёжа
Ну по крайней мере сишный интерфейс там оооочень странный
Nikolay
если мне - не надо, я в курсе
ну нет так нет. Для остальных: то что по ссылочке выше это тоже самое, что я описывал несколько выше - чтение с буфером из потока JSON. Буфер несколько ускоряет вычитывание из потока за счет более быстрого освобождения буфера источника, а если источник это TCP сокет то чем быстрее буфер сокета освободим тем быстрее он сможет наполнится новыми данными. Процесс десериализации JSON медленнее чем передача допустим по гигабитному каналу в хороших условиях и чтобы не деградировать коннект до скорости десериализатора втыкают буфер, что несколько ускоряет процесс. А дальше - под капотом там все таже машина состояний с посимвольным чтением из промежуточного буфера - это невероятно медленно. В сравнении: msgpack передает информацию о размере строки - чтобы понять где ее конец в массиве байт не надо бежать посимвольно попутно ловя закрывающую кавычку у строки со всеми вытекающими - тупо мы заранее знаем какая длина у строки вычитываем это кол-во байт и преобразуем в строку. Поэтому целять JSON на поток TCP это я хз что может быть хуже, разве только XML. Для ускорения JSON обычно жертвуют памятью - быстро из сокета целиком вычитывают полный JSON и потом его потрошат в обьект, но доп аллокации если говорим про Go
серёжа
Распаковываю msgpack array, надо какие-то энумы проверять в цикле с распаковкой, я так и не разобрался чем отличаются MSGPACK_SUCCESS и MSGPACK_CONTINUE
Nikolay
дополню - доп аллокации в контексте Go говорят о мусоре и жоре памяти. На один процесс лично я ловил потребление процессом памяти более 10 гигов при этом скорость была более-менее, пробовал лепить на стрим JSON - долго и убого, никуда не годится. sync.pool не канают, пакеты разного размера, считать перцентели и динамически подгонять тоже особого понту нет. берем msgpack получаем сразу прирост по скорости и снижаем атоматом потребления памяти - Profit. Как я полагаю нить суждения уже потеряна, но началось с того что был вброс о том что mgpack «плохо», решил рассказать где он - это хорошо
engelbart
А как он сам в структуры ложится, что то такое выше было. У него схема есть ? Валидация ?