All streams
Search
Write a publication
Pull to refresh
51
0.2
Valentin Nechayev @netch80

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

Send message
> Это в каком виде? В виде, не соответствующем реальности? А зачем? Какой прок от недостоверной истории?

Должна ли история сохранять все опечатки и душевные метания программиста? Я лично живу в мире, где лучше, что автор выставляет некоторое «предложение», и только с момента его принятия оно становится частью истории, независимо от того, какие внутренние процессы привели к нему.

Ссылки на статьи после этого — забавны, но не имеют отношения к текущему обсуждению, и, по-моему, вообще ни о чём. Если требуется рабочее состояние после каждого коммита, то это должна обеспечить автоматизированная проверка. Например, у нас к Gerritʼу для этого был прикручен Jenkins. Если коммиты были отправлены под другую версию, и корректировка под новое API была сделана потом одним рывком, а не на каждый участвующий, то проверка не прошла, и это уже вопрос административный, допускать такое вообще или нет. Но хуже то, что с merge, которое предложено взамен, если это именно merge, то по любому получим неработающую версию после слияния, а если тут же править результат слияния, то это будет уже не честное слияние, а доработка. По-моему, тут идеального выхода нет вообще.

> Оно всего-лишь создаёт скрытую локальную ветку для незаконченной работы. Это даже в SVN не сложно реализовать,

Локальную — в SVN?

> Чувствую у вас не редки ситуации, когда у вас на машине «всё работает», а у того, кто пульнёт ваши изменения, всё сломается, ибо вы то забыли закоммитить, то закоммитили чего лишнего.

Бывают — там, где нет автоматизированной проверки коммита. (Сейчас я расслабился, потому что основную компоненту веду один. Раньше за счёт центрального Gerrit с code review такое не проходило, и приходилось заботиться сразу.) Но доля таких ситуаций всё равно в разы меньше, чем грязи в процессе, когда вначале коммитится со всеми локальными мотлохами, а потом они вычищаются (а часто даже тупо забываются, пока не всплывают на продакшене). Так что чисто статистически от этого тоже польза.

> Я так и не уловил этого тонкого различия. Почему вы противопоставляете часть (голова) целому (ветка)? Ветка метро — это всё множество последовательно соединённых станций, или только лишь конечная станция?

Потому что Hg позволяет быть нескольким безымянным головам одновременно. А это уже в случае метро не одна ветка, а несколько, вплоть до всей сети.

> Ничего удивительного. Git — это этакий прокачанный CVS, который отслеживает не отдельные файлы, а группы файлов, и плевать он хотел на историю их содержимого (которое кочует между файлами).

Я не понял логики этого замечания. CVS следил за каждым файлом отдельно, а Git, оказывается, «прокачанный CVS», но за одним файлом уже не следит.
Вот именно, что эти аналоги очень неполные.

> ShelveExtension

Нет аналога --keep-index. Также непонятно, относится это только к файлам, которые уже под контролем, или вообще ко всем. В Git это управляется опциями, можно stashʼем убрать вообще всё, можно только то, что в известных ему файлах.

> hg commit --interactive

В Git я могу набирать индекс по частям и постепенно, анализируя, что получилось, возвращая части и снова добавляя, сравнивая диффы между рабочей копией и индексом, и что попало в сам индекс, могу что-то подправлять прямо в самих диффах (в варианте add -e), могу сложить его в stash. (Как правило, нужно в таких диффах удалять дополнительную отладку или специфические локальные опции, но иногда бывает и то, что должно идти в следующий коммит.)

hg commit --interactive это набор подмножества патчей одним махом. Если они меня не устраивают в таком виде — начинается некрасивая работа по бэкапу файлов и ручной вычистке ненужного.

И описанные тут «могу» и «приходится вручную» относятся к реальным и регулярным случаям.

> А про «легче переходить», скорее всего, обычно имеется в виду более согласованный синтаксис команд в Mercurial,

Аргументация сторонников Hg тут, как правило, была иной — им нравилась хотя бы частичная эмуляция стиля линейной последовательной истории, начиная с номеров коммитов для локального репо. Синтаксис команд звучал как аргумент, но не для лёгкости перехода. Я не психоаналитик, чтобы раскапывать тут реальные связи, поэтому ограничился наблюдаемым.
Основные и чрезвычайно ценные вкусности Git — это interactive rebase, index и stash.

Rebase позволяет формировать цепочки изменений в том виде, в котором они должны быть представлены независимо от внутренних процессов разработчика; он может коммитить каждую секунду, может для себя делать что-то в таком стиле, но на публичную фиксацию и/или командное взаимодействие подаётся уже продукт, а не сырьё. Качественного аналога в Hg нет.

Stash позволяет формировать внятную последовательность изменений в типичном (в моих краях и задачах) случае, когда, начиная какую-то мелкую правку, обнаруживаешь, что вслед требуется цеплять что-то более крупное, а там и докатываться до больших рефакторингов. Изменения, которые стронули лавину, откладываются, и так может быть несколько раз. И даже без этого, после приёма новых чужих изменений из аплинка, он помогает их аккуратно скрестить. (Аналог — говорят про queues, но это только частичная замена, и говорят, что их выпилят за концептуальную несовместимость.)

Index (с add -i, add -e) или помогает stashʼу, или позволяет отправлять только часть изменений; например, в моих задачах типично, что я навешиваю в локальных функциональных и интеграционных тестах расширенную отладку, которая не должна попадать аплинку; также могут быть адаптации под локальную платформу, и т.п. (Замечу, что Tortoise клиенты дают такое для всех поддерживаемых SCM; но «из коробки» в Hg и SVN этого нет.)

Теперь про «ветки» в Hg — тут есть ряд злобных засад. В Hg понятие ветки это смешение нескольких разнородных сущностей в одну не сильно приятную (именно из-за смешения) кашу:
  1. «Голова» процесса развития, которых может быть несколько в пределах одной рабочей копии или «ветки» в другом понимании. При операциях push, pull передаются все эти головы. В некоторых местах это называется напрямую словом head.
  2. Некий установленный локальным режимом тег под названием «branch», который приклеивается к каждому новому changesetʼу, и больше ничего не делает. Именно его чаще всего называют «веткой».
  3. Полный пучок п.1, между которыми тоже можно переключаться.


Основное неприятное тут то, что именно авторы Hg принципиально поддерживают этот бардак. (И я ещё не вспомнил bookmarks, которые тоже ветки, но иначе.) Прямая цитата из родной вики: «The term branch is sometimes used for slightly different concepts. This may be confusing for new users of Mercurial.» Но в результате те, кто хвалят ветки Hg за их персистентность, имеют в виду только ветки-2 и только факт явной пометки в истории. Но на самом деле эти пометки не являются ветками — это теги ченжсетов. (А почему именно один такой тег и только branch, если механизм таких тегов в принципе существует, почему он не расширяем?)
Аналог «веток-2» (тегов коммитов) для Git делается не прямо, но через pre-commit hook можно редактировать сообщение коммита. Но если такое делают, то лучше вписывать не какой-то непонятный branch name, а ссылку на тикет, в рамках которого это делается; на моей прошлой работе такое делали — такой хук вылавливал номер тикета из имени локальной ветки (в смысле Git).

По всему описанному, для меня пробы работы под Hg закончились только возмущением «и как в этом бардаке можно что-то делать?», после продирания через грязные коммиты с посторонним мусором, зависшие головы и невозможность без дикого напряга привести всё это в порядок (в итоге я перекинул всё в Git и успокоился). Некоторые из этих возможностей Hg, однако, выглядят иногда полезно. Безымянные головы (но при условии — только в рабочей копии, и нельзя делать push, пока их больше одной, и не по умолчанию). Или автоприклеиваемые к сообщению коммита строки (лучше — набор с именами и содержимым). Но и без них отлично работается.

И ещё одно — очень часто говорят, что на Hg легче переходить после CVS и SVN, чем на Git. Мне получилось строго противоположно — после CVS Git оказался лёгким и простым, а с SVN и Hg резко не сложилось.
> вместо screen — лучше tmux — ctrl+a я использую часто, а вот ctrl+b — не используем и хорошо подходит для контрольной последовательности.

Всё почти наоборот. screen'у элементарно заменить переключающую последовательность — опции типа -e ^ba (для Ctrl+B) и так далее; есть и умолчание для screenrc. Я не пользуюсь Ctrl+A, у меня в зависимости от места Ctrl+F, B, N, местами ещё более редкие.

tmux'у с некоторых пор это тоже делается, но сильно более громоздко — например, для новой сессии надо сказать

tmux new-session \; set-option prefix ^n


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

Ещё из специфичного, что нужно мне — при общем utf-8, есть контексты, в которых надо запускать терминалы под koi8. Screen умеет перекодировки в обе стороны (и я ему ещё и koi8-u добавлял), tmux — нет.
> Также напрашивается очевидный вывод, что в настоящее время не стоит использовать glue record для домена .RU

Совсем не использовать не получится — где-то они таки должны быть. И использовать для своего домена — совершенно нормально.

Скорее всего, система формирования зоны .ru допускает, что регистраторы домена 2 указывают glue-записи для домена 1. По-нормальному glue-записи внутри какого-то домена могут указываться только регистраторами этого же домена, независимо от того, кто иной использует их как NS. Это не гарантия полного избавления от проблемы неверных IP адресов, но позволяет значительно упростить диагностику и локализацию. В .ua, для сравнения, эта политика форсирована, как и (насколько я помню) в .com, .org и аналогичных местах.
Проблему следует лечить непосредственно у администратора зоны, регистратор тут не поможет, если чужие glue-записи будут залипшими из данных другого регистратора.

Специфика BIND9 здесь может быть в том, что он воспринимает glue-записи от чужого домена в additional, но использует их только для дальнейшей рекурсии, не сохраняя в основном кэше, и внутренне привязывая только к тому домену, для которого они были получены. Это лучше, чем более ранняя тотальная анархия с приёмом любых подобных записей (ещё лет 8 назад выяснение «откуда взялась эта запись в кэше?» могла выливаться в полноценный детективный роман), но по сравнению с тотальным игнорированием чужих glue-записей может давать описанные проблемы. К слову, указание BIND9 недостаточно подробно (например, существенные изменения в алгоритме были с переходом 9.3 -> 9.4).
Мне тут подсказали, что техзадание обычно пишут тем же методом. Так что всё в порядке.
Сначала общее впечатление от статьи: непонятно, зачем она в таком виде. Кто с этой тематикой не знаком в принципе — ничего в описанном не поймёт. Кто знаком — будет активно возражать. Большинство упомянутых утверждений следовало бы вводить постепенно, по ходу. Кстати, в этом случае и контекст помогал бы их правильно и уместно сформулировать именно для своей цели.

> недостатком является большое зависимости производительности процессора от шины

В статье очень много таких странных грамматических несогласований, и орфографических ошибок («она пригодиться нам в статьях с практикой.»). А тут вообще непонятно, что имелось в виду:

> Так же несколько инструкций в начале кода будет занято инструкциями для Таблицы прерываний.

«Инструкции для таблицы прерываний» это вектора прерываний, что ли? Тогда они обычно не инструкции, если не применён стиль i8080. Если же данный стиль — то лучше было описать в статье с реализацией прерываний.

Рекомендую тщательно вычитать и исправить.

По отдельным пунктам:

> CISC — Complex Instruction Set Computer — ее особенность в увеличенных количествах действий за инструкцию.

Основные признаки CISC:
1. Ориентация на написание ассемблерного кода человеком, или компилятором, транслирующим типичные операции ЯВУ дословно, без их переработки. Во времена слабых компиляторов (считаем, до середины 80-х) это неизбежно.
2. Как следствие (1) — стремление к ортогональности операций и адресации операндов, и предоставление средств для идиоматических действий типа «доступ по адресу, снимаемому с вершины стека».
3. Экономия памяти под инструкции. Этот признак не имеет причиной концепцию CISC, но современен ему (основные разработки шли в условиях дорогой, но быстрой памяти, сравнимо со скоростью процессора). Отсюда — обязательность разной длины инструкций.

Яркие примеры — PDP-11, VAX, M68K. Упоминать здесь x86 не совсем адекватно. У x86, например, у основного блока команд допускается не более одного операнда в памяти (исключения вроде MOVS крайне редки). В этом смысле, x86 — полу-RISC. Ещё жёстче это, например, в линии S/360...z/Arch в блоке RX-команд — в памяти только второй источник, но не первый источник, он же приёмник.

Просто о количествах действий тут говорить нельзя. Например, можно увеличить указатель стека, сняв один из операндов со стека, но не делают инструкции, в которых увеличивается указатель стека, когда стек вообще не участвует. Линия с явным заданием нескольких одновременных действий это EPIC (многими считается как вариант VLIW), но это другая линия.

> Reduced Instruction Set Computer — Архитектура с уменьшенным временем выполнения инструкций (из расшифровка RISC можно подумать, что это уменьшенное количество инструкций, но это не так)

Не так, но и не по-вашему. RISC это уменьшенная сложность одной инструкции. А по второй расшифровке — Rational Instruction Set Computer — оптимизация построения на типичные применения с отбрасыванием лишнего, и на современные компиляторы с их логикой работы.
Время выполнения одной инструкции может быть в типичном случае даже выше, за счёт подъёма всех до 4-5 тактов, но это компенсируется простотой построения параллельного исполнения.

> Архитектура фон Неймана

Есть и другой метод называния — принстонская архитектура (а фон Неймана — общее у принстонской и гарвардской).

> У данной архитектуры большим недостатком является большое зависимости производительности процессора от шины.

Это существенно только для реализаций, исполняющих всю команду за один такт. Такие используются, да, в очень «мелких» реализациях, но как только мы выходим за пределы встраивания процессора в двухкопеечную монету, общая шина к памяти перестаёт быть узким местом. На нынешнем x86 она не является таковым. На мелких RISC конфликты доступа за память только немного притормаживают конвейер.

> В x86 регистров достаточно мало.

Было мало. В современных имеем, даже считая 32-битку,
1) 8 основных (eax, ebx, ecx, edx, esp, ebp, esi, edi)
2) 8 FP или MM
3) 8 XMM/YMM/ZMM
4) 8 предикатных (K0-K7) (AVX512)
и значительную часть целочисленных операций можно использовать на них всех, и тем более эти регистры можно применять для временного сохранения значений без доступа к памяти.
В 64 битах добавляется ещё 8 основных и 8 XMM/YMM/ZMM. Это уже больше, чем 32.

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

Только при изменении этого окна.

> надо послать ей «нулевые данные»

«Нулевые данные» невозможно послать средствами уровня сокетов. И противоположная сторона не обязана это подтверждать (она воспринимает такое просто как дежурный ACK). Единственный гарантированный способ получить ответ другой стороны — послать реальные данные.

В случае TCP — только на уровне сессии можно это лечить надёжно и кроссплатформенно (keepaliveʼами протокола уже выше уровнем, нежели TCP, тогда можно вводить собственный таймаут).
> Однажды решил он обыграть хостера в «интеллектуальном поединке».

Универсальный вариант для решения таких проблем — выделение времени на нестандартные/дополнительные хотелки по принципу token bucket.
Например: в целом на всех клиентов — до полчаса в рабочий день от каждого саппортера, если нет авралов, но до 2 часов в день в пике.
На одного клиента — до 5 минут в день, в пике до часа, и если есть время из пункта в целом на всех клиентов. Потратил свой час (подсказывает кэп) — дальше по 5 минут в день, если будет тратить каждый день:) Пул запросов рассматривается как очередь (FIFO), запросы «протухают» за неделю, если не подтверждены повторным сообщением от клиента.

Преимущества такого подхода:
1. Чётко прописанный алгоритм (можно опубликовать и вывесить на сайте).
2. Гибкая буферизация перегрузки за счёт постепенного вползания в перегрузку, которая детектируется простейшими методами и поддаётся статистическому контролю.
3. Обучение юниоров и интернов на реальных ситуациях с реальными людьми :) (из можно и до половины времени направлять на решение этих доп. вопросов)

И тот «хитрож…мудрый» клиент в этом случае просто получил бы часть такого резервного времени, если есть. Если он один такой — ну, молодец, получил бонус и дал саппорту чуть больше опыта. Если бы было много — он бы столько ресурсов не получил.

Что не решается, очевидно — проблема плачей типа «ну и что, я самый больной клиент на свете, мне нужно варенье в первую очередь», нужна сила воли, чтобы удержаться от перекосов (ну или желание сделать таки выбор по своим критериям, а не по громкости плача). И начальство может говорить «это же значит, что сотрудники недозагружены», тогда надо тщательно объяснять, что разумная недозагрузка это как раз нормально, потому что даёт гибкость и перспективу.

> с другой — все эти «мелочи», когда они идут потоком, ну просто нереально отвлекают сисадмина.

Всё равно надо закладывать резерв времени на непредвиденное, и если нет явных внутренних проблем, то пусть это время пойдёт на клиента.
Кроме уже сказанного про теорию погрешности вычислений (которая уже очень хорошо развита) — IEEE754, в частности, добивался, чтобы была возможность гарантированно повторить одни и те же результаты — даже если они заведомо неточны — на любых соответствующих стандарту системах, и результаты должны совпадать. Любые подобные «незначительные ошибки в десятом знаке» нарушают гарантию воспроизводимости и ставят под сомнение все результаты расчётов.
Они постоянно появляются. Например, «замерзание» SkyLake, которое лечится новым микрокодом, или ошибки в TSX, которые не лечатся (соответственно, всё до SkyLake реально не умеет TSX). Да и вообще, новый микрокод всегда что-то лечит :)
Разница с описанной в статье ситуацией — что ведущие производители 1) поняли, что лучше сразу сказать об ошибке, чем долго и нудно заметать под ковёр и в итоге таки потерять в репутации, 2) научились продумывать методы программного лечения (даже если это выключение недоработанных режимов или активация более медленного аналога).
Take Five — один из классических вышибателей. Хотя против Misirlou (от Гликерии) не помогало, в ход пошла тяжёлая артиллерия — «Малиновая девочка».
В мире существует много опыта составления действительно электронных анкет, причём толковых, а не таких ужасных. Например, вопрос вида «лучшая сортировка» может иметь из 5 ответов 2 заведомо некорректных и 3 относительно нормальных (например, heapsort может входить как вариант). В зависимости от ответа можно менять набор последующих вопросов, а сами вопросы можно варьировать по стилю, случайным образом переставлять порядок вопросов и ответов, чтобы нельзя было угадать не зная тематику… Здесь, точно так же, как у типичного колл-центра, масса возможностей как составить инструкцию такому голосовому роботу предельно тупо, и достаточно возможностей сделать это умно, сразу получив прикидку на опыт и специфику кандидата. Но это же надо постараться сделать… а тут явно была реализация «на отцепись».
Ну я бы в таком случае ответил, что наиболее эффективный метод — это использовать готовую инструкцию POPCNT, а если у вас процессор её не умеет — дешевле его заменить :) Формально и для enterprise подхода — это было бы ещё правильнее (но не для попугая на входе).
Почему promise API несимметричен относительно деления на первичные функции (в new) и вторичные (во then/catch)?

В первичных надо получать аргументами resolve и reject, и вызывать их явно (проверил на promise 7.1.1 для nodejs — простой return со значением игнорируется, как будто промис незавершён).
Во вторичных надо возвращать значение или генерировать исключение, параметров resolve, reject нет.

Я бы понял логику, или что
1) у всех функций-коллбэков возврат значения работает как resolve с этим значением, а генерация исключения — как reject,
или что
2) все функции-коллбэки имеют три входных параметра — input, resolve, reject,

а лучше — и то, и другое (можно вернуть значение в конце, а можно — вернуть исключение и выйти; а ещё лучше — предусмотреть специальный тип исключения для resolve).

Также нет возможности написать .resolve(значение), и аналогично для reject — тоже было бы значительно удобнее (и извне, как уже обсуждают, и изнутри). (Тогда можно было бы вторым параметром передавать сам объект промиса, для вызовов его методов.)

Или я не вижу каких-то хитростей, которые решают это, и которые можно найти только с заметным опытом их использования?
> Проблема не в пропускной способности, а в сетевых задержках.

Я рассматривал в контексте передачи сравнимого с от «100,000 обращениями к оперативной памяти» и до «1,000,000,000 тактов» объёма передачи, там задержки уровня одного пакета уже не столь существенны, а скорость значительно больше влияет на общую задержку. В контексте коротких передач — да, безусловно, суммарные задержки важнее, и пример «512 байт из iRAM по сети vs. HDD локально, которому надо ещё переместить головки на полный диапазон» — отличная иллюстрация.
> Но ведь 100.000 обращений к оперативной памяти делается операциями, которые сами по себе займут 100.000 * x тактов?

64 чтения из областей, не входящих в одну выровненную 64-байтную область (как строка кэша), займут ~64*250 тактов. 64 чтения подряд из одной такой области займут, по описанной таблице, 64*4 такта. Достаточно просить prefetch на одну такую строку в начале чтения предыдущей, чтобы последовательное чтение не задерживало процессор. В реальности ситуацию ещё больше улучшают микросхемы памяти с несколькими одновременно открытыми строками и многоканальные контроллеры памяти.

> Как часто можно встретить ситуацию, когда 100.000 раз обращений к оперативки выгоднее, чем одно обращение к диску, особенно учитывая существование SSD и PCI-SSD?

При хоть сколь-нибудь продуманном доступе к памяти — считаем, практически всегда.

> Тем более, что сетевой сокет в гигабитной локалке, может быть быстрее, чем обращение к диску.

Гигабитный — по сравнению с SATA даже 1-м — уже нет :) Вот 10GB, или Infiniband'овские скорости — да, уже стоит упоминать.
Именно эта статья — нет, но аналогичные — да. Cетевой транспорт некоторого специфического протокола (средняя длина сообщения около 200 байт); внутреннее представление сообщения — или разложение в таблицу пар имя=значение, или как две строки (std::string, заголовок и тело) с манипуляцией над строкой с массовыми перемещениями частей при вставлении или удалении тегов. Без проблем медленной RAM — работа с разложенным представлением была бы в разы быстрее, но с учётом медленной памяти и кэширования — работа с цельными строками в ~2 раза быстрее. Ещё процентов 30 получается за счёт предаллокации строк на предполагаемую длину, вместо того, чтобы позволять рантайму несколько раз делать realloc.
12 ...
170

Information

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