Может что-то другое и было, но свеча - ровно на том же принципе. Твёрдый парафин плавится теплом пламени, поднимается по фитилю, там испаряется и сгорает снаружи, уже газообразный.
не, там ацетилен генерировался прямо в лампе из карбида и воды. Просто он (ацетилен) горит очень ярко, поэтому девайс гораздо эффективнее простой свечи.
А зачем root? Чтобы младшие порты пробрасывать? А может лучше без рута, и пусть там всё на портах >1024 ? Просто сочетание root + пароль, торчащее в сеть, как-то сразу попахивает…
Если за lookup находится память, в которую нет доступа, то можно не проверять условие. Просто при попытке прочитать оттуда случится исключение. Но там другие моменты вылезут (запретить доступ с точного адреса обычно нельзя; можно по границе страницы)
Если событие редкое и неожиданное - можно не проверять (на каждой итерации, или даже вообще). Без проверки код получится более предсказуемым (для оптимизатора) и линейным. А на случай, если таки бяка случится - ловить её вне оптимизированного кода, как исключение. Несработавшее исключение очень легковесно (по сути зарезервировали место = заинкрементели регистр-указатель кадра на стеке). А при срабатывании - ну да, пойдёт “во все тяжкие”, начиная с подкачки из бинаря страничек, где находится код обработчиков и т.д.
Они существуют, если ваши “плавающие” числа составлены по IEEE754. Программистам на С/С++ никто не запрещает придумывать свои стандарты. Особенно если не open-source. Знаете ли, очень удобно 4-байтные флоты сравнивать на “больше-меньше” как 4-байтные целые. Одна (дешёвая!) инструкция процессора! И даже если они знаковые - можно в самый старший бит поместить знак. И сами числа как-то внутри (нестандартно!) преобразовать, чтобы целочисленное больше/меньше давало верный результат “здесь и сейчас”. А вот позже - когда лично Вам понадобилось какое-то число - его дружелюбно преобразуют в стандарт IEEE754. И даже если по ходу какие-то inf/nan возникли - их тоже могут предоставить ).
я бы добавил - можно попробовать забенчить код. Если какая-то ветка ну совсем никак и никем не выполняется - можно посмотреть более детально на предмет её эксклюзивности (и мёртвости заодно). Как вариант, если она исключительно редка, и при этом тестируется в условии - переместить её в “исключения”. Одним условием меньше ( = код быстрее). А если уж до неё реально добрались через исключение - значит, там уже что-то совсем всё плохо; о скорости можно особо не думать.
в данном конкретном случае получился очень эффективный цикл. В ассемблере буквально 5 инструкций, из которых последняя - jump в начало цикла. Т.е. с точки зрения эффективности оно сильно выигрывает. С точки зрения отладки - где-то последние полтора года абстракция отладочных символов поднялась до уровня, где хорошо отслеживаются инлайны в самых разных местах; и даже несколько вложенных “вызовов” инлайн-функций вполне отображаются (в GDB 12+) как кадры стека (которых реально не существует. Отладчик просто знает, где, что и как за инлайнилось, и позволяет “шагать” по несуществующим кадрам, как будто там реально была передача управления). В самом корне - это ошибка программиста. Он не предусмотрел случай “удаляем несуществующий элемент”. Но зато сколько скиллов это дало мне (как исследователю бага) - я прямо рад. Случай нифига не банальный ). “а, ну тут всё ясно, будем думать дальше” - вообще не прокатило! Хорошее комбо, где ub в одном конкретном месте смешалось с шаблонами и инлайнами. Программист лопух, компилятор - молодец! (и это тот кейс, когда, возможно, помогли бы санитайзеры… Хотя если они динамические - им тоже сложно предусмотреть, именно кейс “элемента нет в таблице”. Это бы и ассерт поймал…)
assert - это макрос, который в релизной сборке полностью удаляется со всем содержимым. Он вроде поможет в отладочной сборке, но по сути тоже не нужен - если бы его не было, в отладочной сборке следующий шаг - разыменование.
Вот такое дивное UB поймали полтора года назад. В функции FindEntry последний цикл превратился в “вечный”. При этом разглядывание данных показало, что нет, хранилище таблицы не заполнено, по логике цикл ДОЛЖЕН был закончиться.
UB здесь возникло именно из-за совокупности двух функций. Обе функции в одном юните. Компилятор заинлайнил FindEntry внутрь FindAndDelete. А там логика такая: зовём FindEntry, получаем указатель на элемент, его разыменовываем… Если элемент не найден - FindEntry вернёт nullptr. Но ведь разыменовывать nullptr - это UB!
И дальше пошла жара…
assert там роли не играет (только больше запутывает). По факту - мы сразу разыменовываем указатель, возвращённый функцией. Значит - он НЕ МОЖЕТ быть nullptr (потому что это - ub. Можно разыменовать и упасть, а можно просто вообще устранить эту ветку. ub - значит, карт-бланш компилятору в этом случае!)
Шаг номер два - раз nullptr нам разыменовать не дают - значит, FindEntry тоже никогда не возвращает nullptr (ну, зачем держать мёртвый код…). Поэтому компилятор внимательно выкинул все ветви, которые приводят к возврату nullptr.
Самое критичное - он выкинул критерий завершения цикла. while ( m_pHash[iIndex].IsUsed(*this) ) превратилось в while ( true ) потому что иначе мы вылетим из цикла, а там return nullptr, а этого быть не может.
Ну и сам “выстрел” - попытка удалить несуществующий элемент. Разыменования nullptr при этом не происходит, всё просто остаётся в вечном цикле…
Синтаксис в разных БД разный. У Mysql строго вопросики, у Pgsql иной. В мантикоре из-за наличия списков сделали ещё ?VEC?. Протоколу всё равно, он просто передаёт символы, не углубляясь в детали.
Список не вставляется в текст запроса. Он отправляется либо в пэйлоад команды execute, либо заранее, через команду long_blob. В этом можно убедиться, например, рассматривая фактический обмен данными с помощью tcpdump/wireshark.
У MySQL свои собственные подготовленные запросы. Но мантикора - не MySQL. Никто не мешает использовать протокол MySQL, но слать через него собственные вариации, что, собственно, и сделано.
Списки валидируются - там, действительно, допускаются ТОЛЬКО числа (включая "не-числа" inf, nan), запятые и пробелы.
Про General Log - это частная особенность MySQL. В мантикоре для этого есть query.log, и можно при желании увеличить verbosity общего лога. Но надо понимать, бинарный протокол всё же разнообразнее, чем содержимое, прилетающее с пэйлоадом команды MYSQL_COM_QUERY(3) - т.е. текстовые запросы.
Prepared statements - это про MYSQL_COM_STMT_PREPARE, MYSQL_COM_STMT_EXECUTE, MYSQL_COM_STMT_SEND_LONG_DATA, MYSQL_COM_STMT_CLOSE и MYSQL_COM_STMT_RESET. Речь именно об этих командах протокола
MySQL поддерживает два типа prepared statements. Один - это через протокол. Буквально, в бинарном протоколе есть подмножество команд, которые реализуют perepare, execute и опционально long_data. Через консольный mysql клиент "пощупать" их невозможно, он эти команды не шлёт. Но вот некоторые другие клиенты, как оказалось, буквально ВСЮ работу делают через prepared statements. Даже какой-нибудь "show status" они шлют как prepare + execute + delete prepared. Собственно, именно эта часть протокола и реализована.
А то, о чём вы говорите - это другой тип, "синтаксический сахар" поверх обычного текстового протокола. Этот тип не поддерживается, и его не реализовывали.
Про то, чтобы запрашивать у компилятора максимальный размер стека функции - да, компилятор, возможно, что-то ответит. Но, увы, этот ответ не будет универсальным.
Практический пример - вы узнали размер в compile time, ок. А потом собранное приложение запускаете, внезапно, через qemu на другой архитектуре. Например, бинарь amd64 запускаете на arm64. И там, внезапно, размер стека оказывается другим; не тем, который подсказал компилятор. Особенно заметна разница при "холодном" запуске функции. Эмулятор что-то там делает - возможно, транспилирует код под актуальную архитектуру - и этот шаг кушает стек.
При трении переносится какое-то количество электронов (т.е. какой-то заряд, измеряемый в кулонах). А разность потенциалов создаётся при разделении заряженных материалов. Поскольку они заряжены разноимённо - они притягиваются, и если их принудительно разделять - мы совершаем работу, которая "запасается" в виде потенциальной энергии, или разности потенциалов. Собственно, генератор Ван-Де-Граафа работает именно на этом принципе. Берём чуть-чуть заряда, и уносим его подальше. При этом совершаем работу и увеличиваем разность потенциалов.
Думаю, ряд материалов как раз характеризует то самое возможное количество электронов, а не разность потенциалов.
про високосные годы - опущена важная деталь: в каком календаре считаются эти годы? В юлианском просто тупо каждый 4-й год - високосный. А вот эти всякие "кратный 100, но не кратный 400 - НЕвисокосный" - это григорианские особенности. Мы с ними живём, но всё же это культурное наследие; его НУЖНО упоминать, чтобы всех приземлить в единую "систему координат"
Это не проблема. В смысле, он в самом деле НЕ СМОЖЕТ ничего заинлайнить. Например, у вас есть тред-пулл и очередь задач (корутин). Там заведомо динамический список задач, задачи из списка придётся распознавать в рантайме, и вот в момент вызова использование указателя на функцию вместо виртуального вызова экономит этот самый лукап.
Если нужно динамическое определение объекта, а виртуализация кажется слишком дорогой - можно хранить явный указатель на функцию в мембере. Так по крайней мере на один лукап по памяти меньше: в классическом виртуальном объекте мы сперва лезем по его адресу, извлекаем адрес tvm, а потом идём в tvm и извлекаем адрес метода. А с указателем на функцию мы сразу извлекаем его, и никакого "избыточного слоя" tvm нет.
Может что-то другое и было, но свеча - ровно на том же принципе. Твёрдый парафин плавится теплом пламени, поднимается по фитилю, там испаряется и сгорает снаружи, уже газообразный.
не, там ацетилен генерировался прямо в лампе из карбида и воды. Просто он (ацетилен) горит очень ярко, поэтому девайс гораздо эффективнее простой свечи.
У msvc stl ещё разные ABI для релизной и отладочной версий. И это, порой, удивляет весьма внезапно.
А зачем root? Чтобы младшие порты пробрасывать? А может лучше без рута, и пусть там всё на портах >1024 ? Просто сочетание root + пароль, торчащее в сеть, как-то сразу попахивает…
Если за lookup находится память, в которую нет доступа, то можно не проверять условие. Просто при попытке прочитать оттуда случится исключение. Но там другие моменты вылезут (запретить доступ с точного адреса обычно нельзя; можно по границе страницы)
Если событие редкое и неожиданное - можно не проверять (на каждой итерации, или даже вообще). Без проверки код получится более предсказуемым (для оптимизатора) и линейным. А на случай, если таки бяка случится - ловить её вне оптимизированного кода, как исключение. Несработавшее исключение очень легковесно (по сути зарезервировали место = заинкрементели регистр-указатель кадра на стеке). А при срабатывании - ну да, пойдёт “во все тяжкие”, начиная с подкачки из бинаря страничек, где находится код обработчиков и т.д.
один “плюс” пишу, пять в уме )
Они существуют, если ваши “плавающие” числа составлены по IEEE754. Программистам на С/С++ никто не запрещает придумывать свои стандарты. Особенно если не open-source. Знаете ли, очень удобно 4-байтные флоты сравнивать на “больше-меньше” как 4-байтные целые. Одна (дешёвая!) инструкция процессора! И даже если они знаковые - можно в самый старший бит поместить знак. И сами числа как-то внутри (нестандартно!) преобразовать, чтобы целочисленное больше/меньше давало верный результат “здесь и сейчас”. А вот позже - когда лично Вам понадобилось какое-то число - его дружелюбно преобразуют в стандарт IEEE754. И даже если по ходу какие-то inf/nan возникли - их тоже могут предоставить ).
я бы добавил - можно попробовать забенчить код. Если какая-то ветка ну совсем никак и никем не выполняется - можно посмотреть более детально на предмет её эксклюзивности (и мёртвости заодно). Как вариант, если она исключительно редка, и при этом тестируется в условии - переместить её в “исключения”. Одним условием меньше ( = код быстрее). А если уж до неё реально добрались через исключение - значит, там уже что-то совсем всё плохо; о скорости можно особо не думать.
в данном конкретном случае получился очень эффективный цикл. В ассемблере буквально 5 инструкций, из которых последняя - jump в начало цикла. Т.е. с точки зрения эффективности оно сильно выигрывает. С точки зрения отладки - где-то последние полтора года абстракция отладочных символов поднялась до уровня, где хорошо отслеживаются инлайны в самых разных местах; и даже несколько вложенных “вызовов” инлайн-функций вполне отображаются (в GDB 12+) как кадры стека (которых реально не существует. Отладчик просто знает, где, что и как за инлайнилось, и позволяет “шагать” по несуществующим кадрам, как будто там реально была передача управления). В самом корне - это ошибка программиста. Он не предусмотрел случай “удаляем несуществующий элемент”. Но зато сколько скиллов это дало мне (как исследователю бага) - я прямо рад. Случай нифига не банальный ). “а, ну тут всё ясно, будем думать дальше” - вообще не прокатило! Хорошее комбо, где ub в одном конкретном месте смешалось с шаблонами и инлайнами. Программист лопух, компилятор - молодец! (и это тот кейс, когда, возможно, помогли бы санитайзеры… Хотя если они динамические - им тоже сложно предусмотреть, именно кейс “элемента нет в таблице”. Это бы и ассерт поймал…)
assert - это макрос, который в релизной сборке полностью удаляется со всем содержимым. Он вроде поможет в отладочной сборке, но по сути тоже не нужен - если бы его не было, в отладочной сборке следующий шаг - разыменование.
Вот такое дивное UB поймали полтора года назад. В функции FindEntry последний цикл превратился в “вечный”. При этом разглядывание данных показало, что нет, хранилище таблицы не заполнено, по логике цикл ДОЛЖЕН был закончиться.
UB здесь возникло именно из-за совокупности двух функций. Обе функции в одном юните. Компилятор заинлайнил FindEntry внутрь FindAndDelete. А там логика такая: зовём FindEntry, получаем указатель на элемент, его разыменовываем… Если элемент не найден - FindEntry вернёт nullptr. Но ведь разыменовывать nullptr - это UB!
И дальше пошла жара…
assert там роли не играет (только больше запутывает). По факту - мы сразу разыменовываем указатель, возвращённый функцией. Значит - он НЕ МОЖЕТ быть nullptr (потому что это - ub. Можно разыменовать и упасть, а можно просто вообще устранить эту ветку. ub - значит, карт-бланш компилятору в этом случае!)
Шаг номер два - раз nullptr нам разыменовать не дают - значит, FindEntry тоже никогда не возвращает nullptr (ну, зачем держать мёртвый код…). Поэтому компилятор внимательно выкинул все ветви, которые приводят к возврату nullptr.
Самое критичное - он выкинул критерий завершения цикла.
while ( m_pHash[iIndex].IsUsed(*this) )превратилось вwhile ( true )потому что иначе мы вылетим из цикла, а там return nullptr, а этого быть не может.Ну и сам “выстрел” - попытка удалить несуществующий элемент. Разыменования nullptr при этом не происходит, всё просто остаётся в вечном цикле…
Синтаксис в разных БД разный. У Mysql строго вопросики, у Pgsql иной. В мантикоре из-за наличия списков сделали ещё ?VEC?. Протоколу всё равно, он просто передаёт символы, не углубляясь в детали.
Список не вставляется в текст запроса. Он отправляется либо в пэйлоад команды execute, либо заранее, через команду long_blob. В этом можно убедиться, например, рассматривая фактический обмен данными с помощью tcpdump/wireshark.
У MySQL свои собственные подготовленные запросы. Но мантикора - не MySQL. Никто не мешает использовать протокол MySQL, но слать через него собственные вариации, что, собственно, и сделано.
Списки валидируются - там, действительно, допускаются ТОЛЬКО числа (включая "не-числа" inf, nan), запятые и пробелы.
Про General Log - это частная особенность MySQL. В мантикоре для этого есть query.log, и можно при желании увеличить verbosity общего лога. Но надо понимать, бинарный протокол всё же разнообразнее, чем содержимое, прилетающее с пэйлоадом команды MYSQL_COM_QUERY(3) - т.е. текстовые запросы.
Prepared statements - это про MYSQL_COM_STMT_PREPARE, MYSQL_COM_STMT_EXECUTE, MYSQL_COM_STMT_SEND_LONG_DATA, MYSQL_COM_STMT_CLOSE и MYSQL_COM_STMT_RESET. Речь именно об этих командах протокола
MySQL поддерживает два типа prepared statements. Один - это через протокол. Буквально, в бинарном протоколе есть подмножество команд, которые реализуют perepare, execute и опционально long_data. Через консольный mysql клиент "пощупать" их невозможно, он эти команды не шлёт. Но вот некоторые другие клиенты, как оказалось, буквально ВСЮ работу делают через prepared statements. Даже какой-нибудь "show status" они шлют как prepare + execute + delete prepared. Собственно, именно эта часть протокола и реализована.
А то, о чём вы говорите - это другой тип, "синтаксический сахар" поверх обычного текстового протокола. Этот тип не поддерживается, и его не реализовывали.
Про то, чтобы запрашивать у компилятора максимальный размер стека функции - да, компилятор, возможно, что-то ответит. Но, увы, этот ответ не будет универсальным.
Практический пример - вы узнали размер в compile time, ок.
А потом собранное приложение запускаете, внезапно, через qemu на другой архитектуре. Например, бинарь amd64 запускаете на arm64. И там, внезапно, размер стека оказывается другим; не тем, который подсказал компилятор. Особенно заметна разница при "холодном" запуске функции. Эмулятор что-то там делает - возможно, транспилирует код под актуальную архитектуру - и этот шаг кушает стек.
При трении переносится какое-то количество электронов (т.е. какой-то заряд, измеряемый в кулонах).
А разность потенциалов создаётся при разделении заряженных материалов. Поскольку они заряжены разноимённо - они притягиваются, и если их принудительно разделять - мы совершаем работу, которая "запасается" в виде потенциальной энергии, или разности потенциалов. Собственно, генератор Ван-Де-Граафа работает именно на этом принципе. Берём чуть-чуть заряда, и уносим его подальше. При этом совершаем работу и увеличиваем разность потенциалов.
Думаю, ряд материалов как раз характеризует то самое возможное количество электронов, а не разность потенциалов.
про високосные годы - опущена важная деталь: в каком календаре считаются эти годы?
В юлианском просто тупо каждый 4-й год - високосный. А вот эти всякие "кратный 100, но не кратный 400 - НЕвисокосный" - это григорианские особенности. Мы с ними живём, но всё же это культурное наследие; его НУЖНО упоминать, чтобы всех приземлить в единую "систему координат"
А разве T-mobile не является сам по себе "виртуальным" оператором? Может, в статье речь о Т2?
Это не проблема. В смысле, он в самом деле НЕ СМОЖЕТ ничего заинлайнить. Например, у вас есть тред-пулл и очередь задач (корутин). Там заведомо динамический список задач, задачи из списка придётся распознавать в рантайме, и вот в момент вызова использование указателя на функцию вместо виртуального вызова экономит этот самый лукап.
Если нужно динамическое определение объекта, а виртуализация кажется слишком дорогой - можно хранить явный указатель на функцию в мембере. Так по крайней мере на один лукап по памяти меньше: в классическом виртуальном объекте мы сперва лезем по его адресу, извлекаем адрес tvm, а потом идём в tvm и извлекаем адрес метода. А с указателем на функцию мы сразу извлекаем его, и никакого "избыточного слоя" tvm нет.