Pull to refresh
48
0
Valentin Nechayev @netch80

Программист (backend/сети)

Send message

ибо дочерние fd выглядят коряво

Коряво, угу. Но работает и имеет свои преимущества. Да, я видел твои замечания про линуксовый подход с xxxfd_create(), но согласись, что в этом случае резко упрощается учёт потраченных ресурсов, для всех сторон. В случае kevent, как увидеть количество, например, таймеров, навешенных на одну конкретную kqueue, процессов, и т.д.? Видеть их всех в виде дескрипторов - просто и достаточно надёжно, то есть вполне себе юниксовый подход "всё есть файл".

(Мне интересно, сколько вообще ведутся споры на эту тему? С 2000, когда появилась kqueue?)

просто устранили эту проблему архитектуры.

Я увидел пассаж:

An endpoint implementing the socket API specified in [RFC6458] MUST NOT indicate user message interleaving support unless the user has requested its use (e.g., via the socket API; see Section 4.3). This constraint is made since the usage of this chunk requires that the application is capable of handling interleaved messages upon reception within an association.

То есть мало того что переложили на юзера, заставив его агрегировать чанки, так ещё и всё равно не дали регулировки по потокам.

Я тут, конечно, ужален проблемами одного почти провального проекта, где из-за аналогичной глупости в Erlang оказалось практически нереально обеспечить стабильный поток проходящих данных, и дую на воду, но для меня теперь вариант "хлебай что дают из пожарного шланга", который реализуется таким подходом, не подходит для чего-то серьёзнее песочницы. (Там было ещё хуже: один вход у "процесса" на всё включая канал регулировки. Тут хотя бы можно назад передавать сообщения типа "добавляю окно на 100кбайт для потока 8" и они могут быть прочтены вперёд собственно данных.)

Так это ж разные уровни - ACK оно по факту приема пакета, который будет положен в буферы сокета (и его нельзя не акнуть, ибо иначе congestion control вскинется), а уже потом, позже, приложение будет решать, какой вычитать.

Если один буфер на все потоки - это уже не то, что нужно. Нужны раздельные.
Повторюсь, если этого нет, то фактически нет разницы с тем, что просто в одном потоке тегировали каждое сообщение ещё и одним байтиком перед ним (или в userdata уложили тот тег). Заодно и деление на чанки точно так же делается на уровне юзера, в пределах того же соединения.

А вот реально раздельное управление потоком по всем потокам (русский тут чего-то зажат в омонимию, per-stream flow control) ты на одном соединении не сделаешь, тут что TCP что SCTP надо сейчас несколько порождать, а тогда ещё и заботиться, чтобы пришло именно на нужный серверный процесс, указать серверу, что это соединение надо ставить в комплект вот этому... марудно. Вот потому, я так понимаю, и стали запускать SPDY с потомками - когда поняли, что от последней попытки (SCTP) ждать хорошего всё равно нельзя.

на практике-то захочется иметь иерархию стримов (даже в HTTP/2 её делали), как в SST, где ребенок borrow'ит окно у родителя, а тот у бабушки...

Я честно не понял ещё смысла в этом навороте. Поищу при случае.

Заметки тоже почитаю.

Однако, "выбрать только из конкретного потока" плохо ложится как на BSD Sockets API, так и на struct sockbuf в ядре...

BSD sockets в базовом виде и так не дают всего, что может SCTP, и есть поэтому свой специфичный набор вызовов. Ещё один добавить - не проблема. Или можно было бы сделать, например, дочерние fd, привязанные к конкретному потоку, чтобы над ними уже вызывать хоть простейший read().

Про struct sockbuf я не в курсе, что там не так.

И если userland API нет для этой возможности, то уже неважно, есть там I-DATA или нет. Я ещё не прочитал RFC, если там что-то не понравится - выскажу отдельно.

Когда потоков много, общий ACK будет гораздо эффективнее, а при правильной реализации это мешать не должно.

А она тут в принципе может быть, эта "правильная" реализация, если у нас вот сейчас настроение выбрать всё из потока 9 не выбирая остальных, даже если для них приёмные буфера забиты?

Обычной задаче и сейчас хватает десятка-двух даже на современном Linux. При том, что 4 с ходу занято на стандартные дескрипторы и "text".

То, что в статье, описывает, что что-то явно подломано. Явная утечка или не запускающийся GC...

(Вариант 100 тестов впараллель я не рассматриваю, так вряд ли будут делать.)

В IDEшках сижу в JetBrainsʼовских, но некоторые вещи удобнее делать через консоль (классический vim; neo варианты не оценил пока). Перекос на 100% что в одну, что в другую сторону - это нехорошо.

Вот Gitʼом в IDEшках так и не научился, не просто "закоммитить всё и в сообщении сказать up", а более сложные вещи.

Слово "боль" в заголовке статьи совершенно неуместно, нет там никакой боли. Просто другие методы и привычки, которые лучше для своего случая.

(Уходя в оффтопик) Ну вообще-то определённая доля насилия со стороны стандартизаторов всегда есть. Например, исключение формы "ложить" из всего ряда (положить, разложить, etc.) - выглядит совершенно неестественно. Я за неимением точных данных верю в легенду, что её исключили, чтобы задавить выражение "ложить х.й". Впрочем, не помогло, тот же предмет стали класть, а не ложить:)
И исключение формы "ихний" немного логично при проблемах выбора - как правильнее "ейный" или "еёный", как писать "егойный" и т.п. - легче оказалось всё скопом запретить, чем разбираться.
Или, почему "февраль" принят в стандарт, при исходном ближе к "феврарь" (Februarius с византийским переходом б->в), а "колидор" - не принимают, изволь говорить "коридор".
Или почему "протвин", которое ближе к исходному Bratpfanne, не допускается, зато стандартизовано "противень", которое выглядит искусственным выгибоном.
Примеры можно долго продолжать.

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

Думаю на старте 3 млн не хватит

Естественно. Железо живёт не год, а минимум 3, а скорее 5 лет. Разве что диски живут 1-2 года. Если покупаете свою железку - сразу эти 5 лет и платите. Если в облаке - то по настроению.

Посмотрите на цену AWS EC2 reserved instances на 3 года вперёд. Уже на мелких вы получите падение цены в 2 раза по сравнением со стандартным on-demand, на крупных - в три раза. А что это за цена?
Я считал уже давно, лет 10 прошло (ну и летит время...), но цена AWS EC2 при закупке на 3 года ("reserved instances") как раз была процентов на 10 больше, чем свой сервер аналогичной конфигурации с установкой в приличный ДЦ, всё в США. В Украине, может, в 2 раза дешевле сам ДЦ, но не железо. Да, это цена гибкости и их страховка на изменение спроса (вдруг что-то произойдёт и каждому бухгалтеру потребуется сервер с AVX1024 и квантовым ИИ процессором;))
Сейчас пересчитывать не хочу, но не думаю, что картина сильно изменилась, скорее наоборот, конкурентов стало больше. А считать облачные провайдеры умеют.

Поэтому - если пока не знаете, что нужно и сколько - берёте on demand. Уверены в надёжности необходимости на год, два, три - покупаете reserved. Вот если это по объёму начинает переваливать, условно, 10 физических серверов - и цена хотя бы на part-time админа нужного качества становится окупаемой - ищете отдельный ДЦ и ставите свои железяки, пока он раскачается, как раз будет время смигрировать.

Главное - что этот переход за счёт таких мер можно делать плавным, а не просто "всё в облаке на on-demand за дикие деньги" vs. "всё на чердаке собственного здания, собственные админы делают всё, от дренажа подвала и смазывания генератора до управления конфигами кубера".

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

это не опечатка и это не копирование строк это копирование данных по массивам указателей. Немного специфичная опирация

"Немного"? Я сильно сомневаюсь, что вы найдёте хоть одну реальную задачу, в которой нужен был бы такой приём, и которая не решалась бы иначе - проще и понятнее. Только не надо изобретать на ходу:)

Я уже тут говорил - 5-й режим, @-(Rn), вообще ни разу не видел вживую, кроме шуточек, а 3-й, @(Rn)+, если не считать применение к регистру PC - в очень редких случаях. И такого копирования там не было.

Еще по командам MOVS и другим специализированным в X86: как там у них с переворотом массивов, сложением массивов, форммированием массивов с чередованием данных из нескольких массивов.

Сложение без переноса - смысла мало, а про перенос мы уже обсуждали, без двухаргументной ADC больно. Остальные задачи встречаются, но достаточно ограниченно, и дополнительный INC или DEC это дёшево. На супер-преимущество автоинкремента/автодекремента не тянет.
И это без дополнительной косвенности. А с такой косвенностью - тот же вопрос, был ли хоть один реальный пример такого употребления на данных командах?

Я прочёл Ваш пример именно как копирование строк, то есть *a++ = *b++. У вас там вторая звёздочка, мне показывает **a++ = **b++. Я считаю её опечаткой, потому что практического смысла в такой операции при двойном разыменовании не вижу; натянутые искусственные примеры не в счёт. Поэтому отвечаю на то, что прочёл.

Если ответить максимально буквально - MOVSB, одна команда. Причём, если с вариантами REP MOVSB, то это полное копирование участка памяти с заданной длиной, а если REPNE MOVSB - то ещё и с остановкой после нулевого байта. Так что ваш пример оборачивается против вас:)

(Начиная с где-то 486 рекомендуют явный цикл, а при наличии SIMD - его методами, когда за итерацию копируется 16, 32, 64, 128 байт, в зависимости от степени развёрнутости цикла. (Можете посмотреть набор реализаций, например, в glibc.) Но это сильно позже и потому за пределами темы.)

Но если рассматривать задачу в её исходном виде - какая часть реального кода в среднем современном приложении занимается копированием строк в массовом порядке? Я думаю, слишком малая, чтобы считать пользу от прямой реализации в духе MOVB (R2)+,(R3)+ чем-то ценной.

Такое ощущение что никто считать не умеет.Все затраты известны бери и считай и делай выводы.

Статья скорее о том, что в нынешнее время многие из ЛПР вообще не знают (или давно забыли), что облакам есть альтернативы. Им надо напоминать, и регулярно.

С другой стороны, надо показывать и промежуточные варианты. Закупка наперёд снижает затраты, выделенные (dedicated) хосты - аналогично, но держать их на чужом ДЦ с техподдержкой от этого ДЦ может быть удобнее, чем самому заботиться о всех технических деталях.

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

Дешевле - вряд ли, сравнимо - да, но отсутствие головной боли тоже чего-то стоит.

Если смотреть в среднем по коду - не очень. Во всяком случае, чтобы что-то решалось на PDP-11 влёгкую, а на x86 в разы (минимум в три) сложнее - такого нигде нет.

Intel же в это время iAPX 432 разрабатывал, 386-й был лишь как затычка, чтобы рынок не потерять.

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

Можно было и догадаться

Я именно так и догадался.

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

Нет, им не требуется модификация аргумента.

А на практике для большинства целочисленной арифметики в реальных программах за глаза хватает и 32-бит.

Стиль x86 дал возможность делать и короткие вычисления на двойных словах, и длинные без ограничения и извращений, одним и тем же средством, за линейное время.

Всмысле порча аргумента?

Вы ж сами написали - adc b1, adc b2...

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

Вот именно, никаких регистров не хватит.

И O(N^2) для сложения - это таки диверсия.

Не коротко, но однооборазно - с ростом разрядностей просто добавляются ADC в арифметической прогрессии.

Адище O(N^2), ещё и с порчей одного аргумента. Вот уж точно при числах длиннее от ≈6 слов применять ту схему, что по моей ссылке.

Но за показ трюка спасибо, сложу в кунсткамеру...

Подозреваю, что о полуавтоматическом портировании ассемблерного кода с 8086 тоже думали.

Это ничем не ограничивает смену кодировки. Уже в 32 битах, например, команды IN, INS, OUT, OUTS, все прочие строковые (MOVS, SCAS...), HLT, CMC, PUSHA, POPA, BOUND, CBW, CWD, ENTER, LEAVE, INTO, CLI, STI, CLD, STD можно было сделать двух-, а некоторые даже трёхбайтовыми, хуже бы от этого не было. XLAT вообще можно было убрать уже тогда. Всё это освободило бы огромную группу кодов верхнего уровня. И это список не полный. В освободившиеся можно было бы вложить не только всякие SIMD, как началось в 90-х, но, например, варианты на 16 регистров, когда припекло бы.
И тут не надо было бы даже удваивать декодер - и так есть различия в поведении одинаковых кодов, а небольшое количество исключений его бы не раздуло.

С 32->64 примерно то же самое.

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

А вот установку флага Z в MOV можно использовать для выхода из цикла копирования строк

Если нулевой байт - да. Но сами по себе NUL-terminated строки это специфический подход.

В любом случае, заметьте, все архитектуры, зачатые уже начиная с середины 70-х и не находящихся под явным влиянием PDP-11 (как VAX или M68k), MOV не меняет никакие флаги. Ну и дальше простановка флагов сокращается (в ARM её уже можно отменять для большинства команд). Не думаю, что столько умных людей сделали ошибку:)

Я не полностью процитировал:

Ну вот я про ручной сбор флагов переноса писал здесь, код на C и ссылка на код для MIPS. Что мешало сделать двухаргументную ADC, пусть даже только на регистрах?

Дле первого свдига - сброс бита переноса и циклический сдвиг (через бит переноса) вправо. Продолжение просто арифметическими сдвигами.

Именно, костыли. Или вот такое делаете, или после более быстрого ASH[C] маскируете.

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

Ну а AMD строил свою amd64 реально в состоянии полной нищеты. То, что оно выстрелило - чудо, но проблемы создания перешли в легаси.

В x86 с двухадресными в памяти вообще швах,

И это оказалось неплохо, потому что сильно упростило построение микроопераций при out-of-order. Уже с наворотами PDP-11 и VAX это сложнее, а с тем, что навернули в m68k - вчетверне. Лучше бы ещё меньше (см. SystemZ, где в основном блоке операций есть источники в памяти, но не приёмники), но и так уже было легче.

Надо просто уметь использовать особенности архитектуры. В данном случае можно можно при необходимости экономить на команде TST

Этого я не понял. Как на ней можно экономить и что это даст, например, для флага carry?

А зачем флаг переноса сбрасывать?

Кто его сбрасывал, вы о чём?

Нет только логического сдвига вправо.

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

инструкции фиксированного размера легко раздекодировать параллельно, так что это не тормозит пайплайн

Да, но есть недостаток - полученное не масштабируется. Что будет, если (когда) у ARM закончатся опкоды?

Я тут вообще подумал, что имело бы смысл отличать код начала от кода продолжения. Например, группы по 4 байта, в не-первой всегда 4 старших бита 1111. Некоторое усложнение кодирования параметров, но зато возможности для расширения будут неограничены, и выборка команд - увидел 1111 - ага, отсюда не декодируем. Уверен, что не я первый придумал эту идею, так что больше интересно, почему нигде ещё не реализовано;\

А если в кодировке оставить один dst, но писать при этом ещё и в регистр dst +1 (и возможно в следующие) ?

Эээ... ну можно, да. (И на уровне ассемблера форсировать его явное указание, чтобы сократить ошибки.)

Но тут есть две другие проблемы. Одна сохраняется - такая команда будет требовать большей ширины шины между АЛУ и блоком регистров, или же больше тактов, чтобы по существующей шине писать ещё один регистр. Другая - что в алгоритм распределения регистров в компиляторе придётся добавлять костыль для пары регистров. Они такое сильно не любят. Есть архитектуры, где пары регистров традиционно есть (SystemZ, PDP-11, VAX), было в SIMD для ARM/32, где сделали сложную иерархию (D0=S0+S1, D1=S2+S3, Q0=D0+D1, и так далее), но для ARM/64 это повторять не стали.

Алгоритмы распределения регистров это и так сплошной закат солнца вручную, задача NP-полная, все существующие реализации что-то крутят частично. Совсем равных регистров нет, даже если сама ISA считает их одинаковыми, то конвенция вызова это ломает (есть callee-saved, есть аргументы и результаты, есть scratch - минимум 3 группы). Стопка диссеров, защищённых на локальных оптимизациях этой задачи, и статей помельче, наверно, уже скоро дорастёт до Эйфелевой башни. Люди, которые этим занимаются, ой как не оценят идею добавлять им головной боли. Да и зачем? Если есть один чётко определённый ни под что больше не занятый регистр с не сильно плотным использованием, пытаться это вынести в общие - как-то мало смысла...

1
23 ...

Information

Rating
7,156-th
Location
Киев, Киевская обл., Украина
Date of birth
Registered
Activity