Check Levi9 best QA positions to Backbase team!
×Закрыть

Приколы современных оптимизаторов С++ (шланг)

Последнее время тут были топики от «вайтивайти» в С++.

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

«Почему LLVM может вызвать никогда не вызываемую функцию?»
habrahabr.ru/...​031&utm_content=link2post

Лично меня уже пугают все -Ox.

👍НравитсяПонравилось0
В избранноеВ избранном0
LinkedIn

Похожие топики

Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Странно, что про most vexing parse не упомянули. Наверное, не придумали достаточно остроумной хохмы, чтобы приплести этот подводный камень C++ к хеллоуинской тематике.

дайте вгадаю, Clang + LLVM під віндовс ?

Windows-то при чём?

Нет.
Да, пример для шланга этот, но оное справедливо для всех компиляторов С++. Это особенности реакции оптимизаторов этих компиляторов на UB.

Так а сколько еще всяких не открытых багов в С++ компиляторах/оптимизаторах.
Помнится когда только вышел свеженький gcc 4.7 (который С+11 поддреживал)
я там нашел какокй то баг связанный с nullptr + std::function (ньюансов уже не помню) и тоже он возникал при включенной оптимизации.

Не боись, скоро С++20 начнут добавлять. Вообще весело станет.

В старые версии — не начнут :)

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

Это само собой. Я вот недавно попытался фрю 2.2.5 запустить под virtualbox. Она не согласилась :) 3.5.1 уже работает. GCC 2.7.2 не собирается под нынешние системы.
Но я-то имел в виду «застывший» набор тулзов для конкретной специфичной платформы.

Но я-то имел в виду «застывший» набор тулзов для конкретной специфичной платформы.

Я не встречался с таким никогда. Ну может застыть на год-три, но не более.

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

А ещё — огромный вклад в проблему вносят преподаватели языков.

А где же ты таких найдешь, что и теорию знают и практику идеально?
20 лет на подготовку преподавателя потратишь?
Это не возможно, так как за 20 лет еще 6 новых стандартов выпустят.

Вт почему я и повторю, если стандартизаторы не начнут использовать голову по назначению и не займутся рефакторингом стандарта они укокошат и С и С++. Время рефакторинга уже почти прошло и у С и С++ есть всё шансы просто не выдержать всех поддерживающих его костылей.

А где же ты таких найдешь, что и теорию знают и практику идеально?
20 лет на подготовку преподавателя потратишь?

Я не хочу тут ввязываться в тему реформы образования, но во всяких США давно склоняются в сторону, что преподавание (как минимум в вузе) это работа для практика, который занимается структурированием, оформлением и передачей своего опыта (и получает за это не 1/20 от коммерческой деятельности, а заметно больше). Всё, умолкаю.

Но даже тема качества преподавания не связана с тем, что _книги_ от вполне себе практиков тоже по большей части умалчивают эти аспекты.
Взял с полки: Шилдт, «C++ базовый курс», 3ed. — ни слова. Там же Страуструп, 3ed. — очень вскользь на одной странице (из тысячи).
Вот и получается, что хабр/rsdn/dou/etc. учат тому, что должно было быть вбито на уровне азов.
Доколе? ©

и не займутся рефакторингом стандарта они укокошат и С и С++. Время рефакторинга уже почти прошло и у С и С++ есть всё шансы просто не выдержать всех поддерживающих его костылей.

Надо таки проталкивать возможности локального ослабления этого проблемного давления. Форматы предложения я описывал рядом.

Вот и получается, что хабр/rsdn/dou/etc. учат тому, что должно было быть вбито на уровне азов.
Доколе? ©

Потому что сложность С и С++ возросла до безумия.
Нет уже варианта изучить С++ меньше чем за 10-15 лет, причем занимаясь только им.
Да, на хабрах и подобных гики расковыривают, как хобби все нюансы о которых не знают даже те, кто стандарт разрабатывает, но как ты такое в систему обучения введешь? Никак.

Потому что сложность С и С++ возросла до безумия.

Это не оправдание. Описанные проблемы — самые азы, в отличие от всяких variadic templates, которые действительно можно осваивать годами.

как ты такое в систему обучения введешь? Никак.

А такие нюансы и не нужны.

Нужны, но их знают единицы и узнают, когда вляпались.
Оптимизация указателя NULL и не оптимизация указателя 1.
Точки следования и их новый вариант — об это вообще только недавно заговорили, раньше даже в учебниках по С++ не упоминались.
Знаковое и беззнаковое переполнение.
Я чего стоит разрядность char<=short<=int<=long<=long long.
А многомерные массивы в С и строки, как массивы с ’\0′ в конце, а иначе жопа. И последующая жесть с юникодом и уродцем wchar_t.
А еще выравнивание совсем нетривиальное. Прагмы, ключики компилятора, ось — всё влияет на выравнивание, что получишь в конкретном месте.

Это так сходу вспомнилось, что сейчас на слуху. Без знания этого всего ты будешь делать ошибки в коде и долго и нудно искать их.

А тут еще сверху ООП с возможностями всего, метапрограммирвоание навороченое. Куча нюансов с исключениями, причем зависящих от ОС.
Кривая многопоточность и кривая работа с файловой системой и куча еще разных нововведений.
Вот классические лажи STL: auto_ptr и valarray.

Прошло 20 лет с момента возникновения С++ и только после 20 наконец-то осилили написать две либы для работы с матрицами в С++. До этого пытались миллион раз.

Оптимизация указателя NULL и не оптимизация указателя 1.
Точки следования и их новый вариант — об это вообще только недавно заговорили, раньше даже в учебниках по С++ не упоминались.
Знаковое и беззнаковое переполнение.

Это как раз то, что IMO надо рассказывать. Но это по сути база, а не супертонкости.

А еще выравнивание совсем нетривиальное. Прагмы, ключики компилятора, ось — всё влияет на выравнивание, что получишь в конкретном месте.

Выравнивание вообще за пределами стандарта, так что не удивительно. Заметная часть прочего названного — тоже.

Это как раз то, что IMO надо рассказывать. Но это по сути база, а не супертонкости.

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

Выравнивание вообще за пределами стандарта, так что не удивительно. Заметная часть прочего названного — тоже.

Но без него прога будет безумно глючить при коммуникации с чем-то. Писать на С или С++ без понимания и знания этого невозможно.

На десктопных платформах выключено по умолчанию.

Что именно выключено?

выравнивание

С утра было включено... когда успели?

Блин, да не связываться с переполнением, и всё.
Знать допустимый диапазон переменной (65к или 4М) — вроде ж не сложная задача.

разрядность char<=short<=int<=long<=long long

 — плох тот кросплатформенный проект, на котором нет своих Int8, Int16, Int32, ...

Под это описание 90% подподают. И я бы не сказал, что их безграмотные писали, но сложность сильно возрастает и просто этот момент уже опускают часто.

Знать допустимый диапазон переменной (65к или 4М) — вроде ж не сложная задача.

Иногда — очень сложная. Когда переплетаются факторы, часть которых от тебя не зависит.

— плох тот кросплатформенный проект, на котором нет своих Int8, Int16, Int32, ...

Сейчас, наоборот, хорош. Потому что stdint.h даже у MS.

std::int8_t, std::int16_t ...

Mike Gorchak Graphics Device Driver Developer в QNX Software Systems
21 час назад

en.wikipedia.org/wiki/Duff’s_device ? Кстати хороший тест на собеседовании — объяснить что делает этот код.

Нет уже варианта изучить С++ меньше чем за 10-15 лет

Очень сильное преувеличение, как по мне. Хотя, конечно, «изучить C++» — понятие растяжимое. Весь его учить от корки до корки никому не надо.
Но научиться писать качественный плюсовый код без UB вполне можно за пару лет, учитывая, что про UB и прочие весёлости есть куча статей (даже на том же хабре) и множество выступлений на всяких CppCon’ах.

Нt все такие умные, как ты. Я вот за 20 лет и всё одно многого не знаю, особенно с UB.

Вопрос к знатокам.
Можно ли запилить язык, который не имеет undefined behavior, и транслятор к этому языку в С. В итоге получаем лучшее от двух миров:
1. Никакого UB -> программы, которые делают то, что написано, а не то, что хочется оптимизатору
2. Вся инфраструктра, существующая вокруг С, вклюая библиотки, компиляторы, оптимизаторы и т.п.
3. .....
4. PROFIT!!!???

haxe.org возможно.
А вообще возможно и в С и С++, если бы стандартизаторы занялись рефакторингом стандарта, решением UdB, а не скоростным добавлением фич, которые толком еще не используются. А многие так вообще ограничиваются С98, причем жестко.

язык, который не имеет undefined behavior

Потеряем скорость

Во что это выражается в абсолютных числах?

Если код не переусложнен, не запутан, не пессимизирован, то обычно до 5-10% обычно.
Чаще выгоднее алгоритм изменить, или ручками с кешами повозиться (Intel VTune и вперед в атаку). Тормоза обычно не в проце, а работе с памятью, она сильно медленнее проца. Компиляторы и оптимизаторы автоматически с этим не справятся.

Чаше нужна оптимизация по размеру в ембедед. Но там обычно С, а не С++.

5-10% это не пшик, так что tradeoff между ебанутостью языка и производительностью вполне может быть оправдан.

Тем более, если я правильно понимаю, все эти плюшки можно поотключать в компиляторе?

Да и да. Более того, можно поотключать конкретные, но для этого тебе постоянно нужно будет следить за 100500 ключиков различных компиляторов. Есть icc, gcc, clang, cl и еще пачка локальных для конкретного железа.
Но часто оказывается, что изменить алгоритм или расковырять код через vtune оказывается быстрее, чем ловить баги из-за UB и оптимизатора. Но тебе нужно угадать заранее, чтобы быстрее будет и выгоднее.
Ну и моё видение локально — оно строится на тех задачах, что я обычно решаю. Мне 5-10% погоды не делают. Обычно мне нужен выйгрыш от 300-1000%. А это только сменой алгоритма или его сильной векторизацией (максимально юзать матрицы и MKL) и распараллеливанием.
Например, везде где возможно удалять циклы и работать с матрицами.

Это можно сравнить по результатам, например, -O1 и -O2 в GCC. Только начиная с -O2 он включает -fstrict-aliasing, -fstrict-overflow, -fdelete-null-pointer-checks и прочие основные источники реакции на UdB.

Вот пример статистики, но надо учитывать, что в -O2 входят сильно больше оптимизаций, чем эта группа. В основном разницы нет, есть один случай деградации, есть один +40% и есть один +5%. Я бы сказал, софт слишком разный, чтобы давать общую цифру, но в целом я большой пользы не вижу.
-O3 даёт больше, но он имеет (кроме векторизации) мало смысла, если не опирается на результаты -O2.

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

Не всё так просто. Если выключаем оптимизацию получаем предсказуемость, а если включаем по максиму, то получаем скорость до +40%, но лишаемся предсказуемости.
Хорошая ловушка.
Выход по сути один — максимум тестов (здесь их мало быть не может) и прогон на них после применения ключика оптимизации и надеяться, что тестами покрыли всё.
В итоге каждый процент быстродействие — это дорого и долго. Отсюда и решение, если железом масштабируется и дешевле, чем код ковырять долго и нудно, то ну ее нахер оптимизацию. Если же этот вариант не работает, то тогда можно с ней играться с оптимизацией.
Причем сильно желательно включать ее не ключиком -Ox, а отдельными ключами и последовательно.

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

так как надо проверять любой переход по указателю

С чего вывод, что сразу надо проверять? Речь-то не о ситуации «в указателе ХЗ что => в рантайме ХЗ что произойдёт», а о выводах компилятора типа «в указателе ХЗ что => у меня полная свобода перекорёжить по-своему».

что убьет локальность работы с кешем

А даже если так — ничего не убьёт. Если железная платформа имеет явные подсказки для переходов типа likely/unlikely — все нештатные варианты рассматривать как unlikely, а если сама предсказывает (современные x86 или AA64) — после 1-2 первых проходов будет основной путь.

Получим недоджаву.

Java JIT, к сведению, умеет исключать такие проверки, если считает, что null маловероятен.

С чего вывод, что сразу надо проверять? Речь-то не о ситуации «в указателе ХЗ что => в рантайме ХЗ что произойдёт», а о выводах компилятора типа «в указателе ХЗ что => у меня полная свобода перекорёжить по-своему».

Нет, если говорить про UB, то переход по указателю или запись в массив вызывают UB при невалидных входных данных. Давайте уже определимся с терминологией.

Нет, если говорить про UB,

1. На всякий случай — я пишу UdB, потому что есть ещё UsB (unspecified behavior)
2. Надо определяться не с терминологией, а с темой обсуждения. А она тут — именно сверх-ожидаемый интеллект оптимизаторов, а не просто факт UdB от доступа по мусору.

А даже если так — ничего не убьёт. Если железная платформа имеет явные подсказки для переходов типа likely/unlikely — все нештатные варианты рассматривать как unlikely, а если сама предсказывает (современные x86 или AA64) — после 1-2 первых проходов будет основной путь.

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

Это не в разы, а на единицы процентов.

Это в разы, так как надо проверять все указатели и массивы, а их данные лежат неизвестно где.

Java JIT, к сведению, умеет исключать такие проверки, если считает, что null маловероятен.

UB в Java?

Нет, собственный обработчик всяких SIGSEGV, который конвертит ситуацию в генерацию NullPointerException (а если была явная проверка на null — в переход на соответствующую ветку).

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

Шо то, шо это. Лучше прога, работающая и быстро, и правильно.

Если дашь много времени и заплатишь, как Скотту Майерсу.

Обычно надо новые фичи быстрее чем у конкурентов, а скорость и правильность не так важны (в разумных пределах)

це де так? невже в автомотів? чи хвінанасах?
чи петпроектах?

усюди, де є конкуренція. В пет проектах, певне, її нема.

Це так на певній ступені проекту. Коли кажуть щось накшталт: або ми виходимо в prod на %дата% або усі розходимося.

Ну дык в том и суть UB, что при нём оптимизатор может натворить любую фигню и будет прав. Это приколы C++ в целом.

Я ещё в разговорах об UB люблю приводить вот этот пример:
ideone.com/pcXaQk
Оптимизатор увидел, что мы выводим в stdout значение mul*i, имеющее тип int. И поэтому решил выбросить проверку i<10, поскольку i, по его логике, ну никак не может быть больше двух. Ибо при i == 3 уже произошло бы переполнение типа int, а переполнение знаковых целых — UB.

Угу, я его уже тут упоминал с подробным обсуждением на RSDN. Там ещё есть противоположный результат — сокращения цикла до 3-х итераций — всё на основе того же «знания».

А теперь смотрим на многие либы и видим, что там часто для размеров массивов используют знаковые целые и ждем, когда бабахнет.

В 1990-х годах разработчики языка Си и ОС Unix (Томпсон, Керниган, Ритчи) сделали длинное заявление со множеством акцентов, в котором они раскрыли, что эти разработки были первоапрельской шуткой, пародией на язык Pascal и ОС Multics в форме доведения сложности до абсурда, что сами они пишут на Лиспе под Apple Macintosh, и что теперь они крайне поражены популярностью Си и Unix, и даже планировали продать их Советскому Союзу для отбрасывания его уровня компьютерных технологий на 20 лет назад; ведущие фирмы-разработчики компиляторов Си и С++ и дистрибутивов Unix отказались комментировать это заявление[256]. Вскоре после этого авторы Си и Unix предложили язык Limbo под слоганом «что бы мы сделали, если бы начали с начала» и написанную на нём ОС Inferno.
--- Это со ссылочки

Ну так, как видим, всё это оказалось правдой. Go — наследник Limbo. У которого куча паскалевских черт.

Вот я за ним и смотрю внимательно, как Гугл его дальше развивать будет.

Виктор, остановись, мы не хотим еще и тебя потерять

Я бы уже к фортрану вернулся, если бы он кому-то из заказчиков нужен был. Мода же на С и С++.

И я бы в Go уже сбежал, если бы там было то, что мне нужно. В первую очередь матрицы и легкое подключение других библиотек с математикой.
А Питон мне сильно не нравиться, как язык и тормознут он безумно, если его не как клей для либ на С использовать.

и даже планировали продать их Советскому Союзу для отбрасывания его уровня компьютерных технологий на 20 лет назад

По моему, это новый слоган для ватника — совок развалился не из за рукожопости руководителей и дибилизации системы а из за злых буржуев и ЯП С !!!

Здається з С++ в усі часи і на багатьох компіляторах були різноманітні трабли при використанні неініціалізованих змінних. В чому проблема запаритись інціалізувати якимся дефолтними значеннями або нульом самому для більшої надійності?

Пусть сын Порошенка ининициализирует! %)

Просто люди часто не задумываются о соглашениях между ЯП и ОС. Например С++ может не инициализирвоать данные, но динамический линкер ОС проинициализирует данные хотя бы из-за соображений секьюрности, чтобы память с чужим контентом не была дана новому процессу и счастливый программер получает там NULL. А компилятор максимум знает назначение ОС для осуществления calling convention, но ни версию ОС ни тому подобных ньюансов, их знает линкер. Вот и получается что компилятор может делать с неинициализорованными данными что ему угодно.

динамический линкер ОС проинициализирует данные хотя бы из-за соображений секьюрности, чтобы память с чужим контентом не была дана новому процессу и счастливый программер получает там NULL

Для статических и глобальных данных инициализация нулевыми значениями (и конструктором, где есть) прописана в стандарте (C11 — 6.7.9). Если компилятор не отклоняется от этого, то остаётся проблема инициализации только тех, что с automatic storage duration. Но, как правило, в результате работы всяких ld.so в стеке уже была какая-то возня и там никак не нули. Нули будут в новоаллоцированных страницах (если их специально не заливают всяким мусором).

юзай −00 и оптимизируй вручную.

Многие вещи вручную оптимизировать невозможно или нерационально.

оптимизировать надо боттлнеки

Во большинстве случаев оптимизировать надо всё. Все современные компиляторы С++ при отключённых оптимизациях генерируют очень избыточный и медленный код.

На компиляторах, основанных на SSA (как нынешние GCC или Clang), -O0 приводит к чудовищному количеству лишних операций, типа пять копирований одного и того же между регистрами, часто по кругу. Оптимизировать вручную это невозможно.
GCC стал давать -Og, которое даёт некоторый весьма малый уровень вмешательства, но уже устраняет эти безобразия. Поэтому начинать надо минимум с него.

GCC стал давать -Og, которое даёт некоторый весьма малый уровень вмешательства, но уже устраняет эти безобразия.

О, не знал, обращу внимание.

Появилось в GCC 4.8. Так что до некоторых видов embedded могло не доползти.

Clang не ругается на неё с 3.9 или 4.0, но приравнивает к -O1, обещая в туманном будущем реализовать меньше странных оптимизаций.

Ещё мне понравился такой кусок в Linux Makefile:

ifdef CONFIG_READABLE_ASM
# Disable optimizations that make assembler listings hard to read.
# reorder blocks reorders the control in the function
# ipa clone creates specialized cloned functions
# partial inlining inlines only parts of functions
KBUILD_CFLAGS += $(call cc-option,-fno-reorder-blocks,) \
                 $(call cc-option,-fno-ipa-cp-clone,) \
                 $(call cc-option,-fno-partial-inlining)
endif

но это уже когда отладчиком лезешь аж в машинный код.

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

Все примеры в статье — отходы от стандартов написания правильного кода. Зачем тогда удивлятся что код написанный чёрть и как стреляет в ногу?!

Ну то лише приклади для наглядності, ідея в тому, що навіть в найбільш чисто написаному коді можуть вилізти приколи при оптимізації компілятором.

Да не совсем. Мне лень открывать С++ стандарт, но кто-то может посмотреть правила инициализации глобальных данных? На сколько я помню там указаны скалярные типы, массивы, юнионы, структуры, но типа указатель на функцию там нет.

typedef int (*Function)();
static Function Do;

Если это так, то это настоящий UB. Почему бы не сложиться звёздам так, что Do будет указывать на EraseAll() и так? Оптимизатор ИМХО сделал всё правильно, если память будет заполнена рандомом, то почему бы рандому не указывать на EraseAll()?

imgs.xkcd.com/comics/random_number.png

Вероятности. У EraseAll один адрес, а того, что может оказаться в том указателе 2^n (n размер указателя).
Но да, в стандарте явно не прописано для указателей на функцию. Но извини ни в одном стандарте и законе не возможно прописать всё в мире. А такое трактование стандарта как в этом примере — это называется в народе «заставь дурака богу молиться — он лоб себе расшибет».

Вероятности. У EraseAll один адрес, а того, что может оказаться в том указателе 2^n (n размер указателя).

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

А такое трактование стандарта как в этом примере — это называется в народе «заставь дурака богу молиться — он лоб себе расшибет».

Какой указатель лучше 0xDEADBEEF или 0xEBADEBAD ? %)

Еще раз. С очень большой вероятность твоя прога просто упадет, а не перейдет к функции, заботливо подставленной оптимизатором.
И повторю:

А такое трактование стандарта как в этом примере — это называется в народе «заставь дурака богу молиться — он лоб себе расшибет».
Еще раз. С очень большой вероятность твоя прога просто упадет, а не перейдет к функции, заботливо подставленной оптимизатором.

Я вообще с трудом понимаю, как программеры могут рассуждать о вероятностях факапа в коде и с этим жить. Я предпочитаю неизбежность, любое событие неизбежно, иначе оно бы не случилось.

Ты из тех, кто программирует без единой ошибки?
В стиле «Ни единого разрыва».

Причём тут ошибки? Я пишу код в детерминированном стиле и не надеюсь, что с большой вероятностью он будет работать как надо или с большой вероятностью он упадёт, если что-то пойдёт не так. Если ты хочешь, чтобы он упал, присвой ему NULL явно.

Я вообще с трудом понимаю, как программеры могут рассуждать о вероятностях факапа в коде и с этим жить. Я предпочитаю неизбежность, любое событие неизбежно, иначе оно бы не случилось.

© Mike Gorchak

Расставляешь 100500 ассертов и не мучаешься

Так вы уж определитесь «умные или красивые» (из анекдота).
Сами же себе противоречите в двух последовательных постах.

Я пишу код в детерминированном стиле и не надеюсь, что с большой вероятностью он будет работать как надо или с большой вероятностью он упадёт, если что-то пойдёт не так.

Ассерты как раз для этого

Это одна из причин, почему математики не должны лезть в программирование %) В детерминированной системе нет места вероятностям.

Это ты из какой вселенной пишешь? В нашей физика другая, вообще-то.

Ты программы пишешь не для вселенского процессора, а для полностью детерминированной системы. И вообще:

God does not play dice with the universe. © Einstein

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

Вообщем-то из определения самого понятия алгоритма следует, что он должен выполняться единственным детерминированным путём. То есть по сути дела, в записанном алгоритме undefined behavior не должно возникать в принципе, никогда. Поскольку стандарт C++ такие ситуации допускает — это косяк дизайна языка как такогого. Обсуждаемая в топике проблема возникла вследствие ошибки в дизайне/идеологии языка.

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

И кстати это не самая плохая идея. На больших массивах твои потери в быстродействии будут ну очень малы.

Во-первых, не каждая. Если компилятор уверен, что значение не выйдет за пределы размерностей массива, он просто уберёт проверку. И ему можно помогать в этом, например, одной предварительной проверкой перед циклом.
Во-вторых, проверка границ массива не имеет отношения к описанной проблематике, которая даёт значительно более тонкие эффекты.

Во-первых, не каждая. Если компилятор уверен, что значение не выйдет за пределы размерностей массива, он просто уберёт проверку. И ему можно помогать в этом, например, одной предварительной проверкой перед циклом.

Вот это я бы назвал вредительством. Выйграть 0.001% быстродействия за счет внесения ошибки в логику программы.
Вот у меня итерационный алгоритм, а оптимизатор решил его пройти в обратном порядке или выкинуть проверки выхода — в результате, что будет?

Вот это я бы назвал вредительством. Выйграть 0.001% быстродействия за счет внесения ошибки в логику программы.

Никакой ошибки. Например, компилятор знает, что индексы массива от 0 до 99, а тут индексация будет значением от 3 до 20.

Вот у меня итерационный алгоритм, а оптимизатор решил его пройти в обратном порядке или выкинуть проверки выхода — в результате, что будет?

Это ты о другом случае говоришь. Там я согласен — он не имеет права делать изменения, которые ломают логику входного кода.

Никакой ошибки. Например, компилятор знает, что индексы массива от 0 до 99, а тут индексация будет значением от 3 до 20.

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

Это ты о другом случае говоришь. Там я согласен — он не имеет права делать изменения, которые ломают логику входного кода.

Но дело в том, что машина не способна понять логику. Там нет ИИ и еще долго не будет. Ее задача выполнять то, что написано и оптимизировать только там, где это явно возможно и прозрачно без всяких UB.
По сути это криворукость разработчиков компиляторов. Если компилятор увидел UB, он обязан об этом предупредить и отменить оптимизацию этого кода оптимизатору. А дальше программист перепишет код без UB.
Но для этого нужно не множить сущности в стандарте, а отрефакторить его и четко указать все места UB или ввести правила для разруливания этих ситуаций.

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

Да, возвращаемся. Потому что на практике оказывается, что такого кода, который надо бы удалить просто из понимания, что он никогда не будет выполняться — может быть в разных местах, грубо говоря, от 30 до 90 процентов.

К этому относится даже простейшая ситуация, когда ты пишешь b = f(a, a+1); — решение прочитать `a` только один раз это уже оптимизация (которая не сработает, например, если `a` атрибута volatile, или в чём-то чуть более сложном типа b = f(g(a), a+1);, а `a` может быть модифицировано в g() (она глобальная, её адрес куда-то передавали, и т.п.)

«Во всём нужны мера, норма и предел» ©. Некоторый уровень таких оптимизаций разумен и полезен. Все споры в этой теме и ещё 100500 аналогичных в других местах — в том, где этот уровень превзойдён, и почему программист не может контролировать происходящее.

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

Так в том и заковыка, что это условие чётко определить невозможно: граница между теми оптимизациями, что приводят к переэксплуатации понятия UdB, и теми, что не приводят — слишком размыта. Но это я быстро пояснить не могу — нужен именно опыт разработки оптимизатора.

Пока же мы имеем или тотальные запреты на оптимизацию сверх минимума (Java), или возможность компилятору творить вообще что угодно в рамках своего понимания (C, C++)...

ты пишешь b = f(a, a+1); — решение прочитать `a` только один раз это уже оптимизация (которая не сработает, например, если `a` атрибута volatile, или в чём-то чуть более сложном типа b = f(g(a), a+1);, а `a` может быть модифицировано в g()

Небольшое уточнение: вы перепутали «не сработает» и «не хотелось бы, чтобы сработало».

Может сработать даже с f( g(&a), a+1 ) , т.к. изменение ’a’ и использование его в пределах одной точки следования — UB, результат которого вполне может совпасть с чтением переменной до изменения. Кстати, порядок вычисления ’a+1′ и ’g(a)’ не определен. В целях оптимизации, естественно.

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

Теперь новый термин

«sequenced-before»

Согласен по сути с уточнениями. Но не «перепутал», а переупростил. Надо было дать вариант с промежуточными присвоениями.

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

Как раз запись за пределы массива — типичнейший UB, отличающий С от остального. Можно о нем поговорить.

Где тут UB — это просто ошибка в логике программы?

Можно, но не в связи с темой целочисленного переполнения или пустого указателя.

В детерминированной системе нет места вероятностям.

Непонятно как у вас там и у прочих ембедерщиков.
Но на большом и толстом сервер сайде вероятность того что код будет всегда работать так как задуманно — никогда не 100%

Весь смех в том, что даже в советских ГОСТАх оное было отражено.

вероятность того что код будет всегда работать так как задуманно — никогда не 100%

Вот это было отражено? В каком именно ГОСТе и в какой форме?

ГОСТы по разработке ПО и его приемки. Там есть и критерии надежности и как их определять. И там везде вероятности.
Их и самому можно логически вывести. Там просто записано было то, что умные люди вывели логически для тех, кто не может сам.
Уверен, что подобное есть и в мэках и исо. Но мне лень ковырять их и искать, а по памяти не помню номеров уже.

Весь наш мир не детерминирован и вероятностен. С какой радости программа и тем более выполнение ее будет детерминировано? Но, нам очень сложно работать с недетерминироваными системами, посему мы накладываем кучу ограничений, чтобы приблизиться к детерминированности.
В этих же UB c C и C++ сознательно увеличивается степень недетерминированности системы.

Вот я и пишу, что стандартизаторы пошли с развитием языка не туда. Вместо того, чтобы уменьшать количество UB и вводить явные и четкие ограничения, они делают все с точностью до наоборот.
Их действия мне напоминает программистов, что с нарастающей скорость увеличивают технический долг в своей программе, что разрабатывают и не собираются его уменьшать, или хотя бы не увеличивать. Отрицают рефакторинг полностью.

Вот объясни с какой радости разименование NULL это UВ. Просто сделайте то, что написано. В тех системах, где оное допустимо всё пройдет нормлально, а тех где недопустимо сама ось обрубит прогу.
А если я раpименовываю rand — это UB?
То же и с signed int — это должно быть системно зависимо, а не UB.
Все UB, что стандартопейсатели наворотили я и не знаю.

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

Вот объясни с какой радости разименование NULL это UВ. Просто сделайте то, что написано. В тех системах, где оное допустимо всё пройдет нормлально, а тех где недопустимо сама ось обрубит прогу.

Объясняю. Там, где железо поддерживает hardware breakpoint или подобное поведение, ставится бряка на адрес памяти 0. Когда происходит разыменование 0, бряка срабатывает и рантайм кидает SIGSEGV (кстати, SIGSEGV — это чисто линукс, а Вы что предлагаете делать, стандартно для любой ОС или ее отсутствия?). А вот если нет хардварной бряки — то чтобы убедиться, что разыменовывается ненулевой указатель, надо в каждом месте разыменования вставлять код сравнения указателя с 0. Это будет основательно тормозить исполнение и увеличивать размер программы. И все скажут «плохой язык, надо писать новый».

Ничего не делать. Просто идти по указанному адресу. А дальше ОС или железо отреагирует каким-то образом.
Почему при разыменование rand, я не получу UB, а проц просто попробует сунутся туда.

А когда проц суется в чужую памать — разве это не UB?

Нет — это ошибка логики программы и ее разруливать должен или уровень ОС или железа. Но никак это не забота компилятора — если может он должен выдать ошибку или ворнинг в этом месте.
И второе, если речь про ОС, то rand может быть и а разрешенной области памяти.
Дальше программа или упадет или продолжить выполнять некий код.
В чем разница с NULL?

If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined .
www.open-std.org/...​2/wg14/www/docs/n1124.pdf page 95 clause 8.

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

Не пользуйся эзотерикой.
Разыменование указателя и запись в массив приводят к UB. Не надо его бояться. Если обкладывать ifами эти операции — просядет скорость.
Кстати, вот по линку с той самой википедии список UB www.open-std.org/...​w/docs/n1548.pdf#page=571 мне лень читать

В тех системах, где оное допустимо всё пройдет нормлально, а тех где недопустимо сама ось обрубит прогу.

А там, где ОСи нет (фирмварь), или вместо нее микрокернел (uC-OSII)?

И что? Проц попробует сунутся туда и дальше всё зависит от него.
Чем отличается сунуться по NULL от rand?

Так везде все от него зависит. Это и есть UB — когда код не говорит, что получится, а оставляет это процу, ОСи или расположению страниц в адресном пространстве. Надо найти определение UB.

en.wikipedia.org/wiki/Undefined_behavior

В чем разница в разименовании рандомного указателя и NULL?

Даже вот здесь i = i++ + 1; // undefined behavior бред от стандартизаторов. Что им мешало жестко задать исключительно слева-направо или справа-налево.
А знаю, кайф от «преждевременной оптимизации» — классические благие намерения, которые ведут всегда в ад.

Что им мешало жестко задать исключительно слева-направо или справа-налево.

Во всяких Java и Go именно так и сделано.

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

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

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

А потомучто нехер баловаться неинициализированными переменными. За такое, «плюсовые» кодерки должны депортироваться на джава-формошлёпство.

А потомучто нехер баловаться неинициализированными переменными.

Код тут и тут оба со всеми инициализированными переменными, зато результат оптимизации — противоположный.

За такое, «плюсовые» кодерки должны депортироваться на джава-формошлёпство.

За такое надменное верхоглядство, как Вы тут демонстрируете — надо депортировать даже не на Java, а куда-нибудь на Visual Basic под Excel.

Некоторым лучше жевать, чем говорить.

жава рулез, хрести сакс

Именно потому рулезные чуваки зашли через kotlin и groovy и наверняка через что-нибудь там ещё аля scala ))

То же и с signed int — это должно быть системно зависимо, а не UB.

Ну в пределах конкретных систем это вполне реализуется. Для GCC и Clang: есть общий -fwrapv, а есть -fstrict-overflow (который сам включается при -O2 (GCC), но можно его явно выключить). Кода, который «ниасилил» эти проблемы и применил -fwrapv, достаточно много (например, Python ещё во времена 2.6 был таким), а -fno-strict-overflow «стандарт» для ядра Linux и ещё кучи мест.
В Linux, кроме того, также включают -fno-delete-null-pointer-checks (я согласен, что это дурь, но, видимо, слишком много мест, где его хвосты...)

GUID’ы тоже не используете?)

GUID уникален, а не рандомен.

У UUID высокая предсказуемость. А вообще марш все читать дискретку!

Так все же

высокая предсказуемость.

или

уникален

?

Вот честно, не интересно заниматься словоблудием. Эти понятия вообще ортогональны. Разбей поток рандома на 0 и 1 и у тебя будет хреновая уникальность и хорошая непредсказуемость.

Понял косяк.
Так все же что на счет уникальности GUID он уникален или нет?)
Потому что wiki говорит о какой-то вероятности коллизий.

Так все же что на счет уникальности GUID он уникален или нет?)

Уникален для чего? Для одного процесса, для операционки, для домена и т.д.?

До сих пор, насколько я слышал, ни на типе 1, ни на типе 4 коллизий не зафиксировано (для типа 1 — при условии правильной генерации timestampʼов).
Хотя вероятность — да, уже вроде бы высокая.

Предсказуемость — у типа 1. А сейчас в основном используют тип 4 (случайная генерация).

Почитай заодно, почему UUID не используют в качестве криптографических ключей.

Он не уникален. Но можно код писать и не пользуясь гуидами. В конце-концов, как-то индустрия работала без гуидов 30 лет до того, как мелкомягкие придумали ком.

В том, что оно было задизайнено как уникальность у него заложено в имени.

Просто люди не понимают понятия детерминированных систем. Никто не ожидает, что при чтения байта будет значение 257, так и не стоит ожидать чтения нулей там где они не были записаны, только и всего. Рандом никак не влияет на детерминированность, это всегда 0 или 1 при декомпозиции. Всё.

Так думали во времена дос. Потом поняли что все бажное, поделили на процессы нити, изолировали память, придумали разные штатные методики типа дог вотч и тд. И вообще, самое надёжное ПО сейчас на рынке это то, которое хорошо умеет себя реанимировать и максимально рассчитано на разного рода сбои. Включая сбои оборудования сети итд. И такой код пишут зачастую что да, а действительно мы прочитали значение 256 или нет. Ну или хотя бы контрольные суммы перепроверить прочитанных блоков.

И вообще, самое надёжное ПО сейчас на рынке это то, которое хорошо умеет себя реанимировать

Как всё запущено. В надёжных системах ПО не имеет права себя реанимировать, в случае сбоя оно должно максимально корректно завершиться и уйти в мир сегфолта, на его место должно стать новая созданная копия упавшего ПО.

Какая игра слов, по сути поддерживающая пост выше.

Я подозреваю, что это и имеется в виду — просто собственно исполняющий код и контролирующий его код формально находятся в пределах одного продукта. Так, например, в Erlang/OTP с системой супервизоров. Но если кто-то хочет явно проверять условие падения — это тоже можно реализовать (грубо говоря, через try-catch).

Методов много и разных. По сути Мике просто перефразировал игрой слов то, что написал Бубен, причем ограничившись один вариантом.

«Как-то» это, да, ключевое. Например, есть монтирование разделов по uuid. Жутко вкусная штука, а без неё надо было искать вручную, где на каком пути какой раздел оказался. И таких примеров много.

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

На сколько я помню там указаны скалярные типы, массивы, юнионы, структуры, но типа указатель на функцию там нет.

C++11 last draft:

3.6.2/2:
Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) before any other initialization takes place.

8.5/5:
— if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T;

3.9/9:
Arithmetic types (3.9.1), enumeration types, pointer types, pointer to member types (3.9.2), std::nullptr_t, and cv-qualified versions of these types (3.9.3) are collectively called scalar types.

Так что — требует инициализации пустым (NULL, nullptr) указателем. Разницы между указателем на функцию и указателем на что-то другое тут нет.

Стандарт C требует то же самое, но без промежуточной отсылки на скалярные типы.

Может дело в typedef ? У меня нет clang этой версии, чтобы проверить.

Писали, что такое clang творил чуть ли не с самых первых версий. С 2.9 так точно.
Нет, typedef ни при чём. Работает логика по принципу
1. Вызывают по указателю не проверив его => считаем, что он гарантированно инициализирован непустым значением. Ибо иначе UdB.
2. Есть только одно место, где ему что-то присваивают => согласно п.1 оно вызывается. Ибо иначе UdB.
3. Присваивается согласно п.2 одно-единственное значение => можем не заглядывать в указатель, а вызвать безусловно.
По крайней мере пункт 3 это та же самая логика, что если компилятор видит, что по p типа A* вызывается виртуальная A::foo(), а он знает, что p инициализирован B() (потомком A) — он вызовет B::foo, не делая «лишнего» заглядывания по указателю.
Достаточно чуть-чуть разрушить эти гарантии — например, сняв слово static у указателя — и он резко становится «белым и пушистым», не делая таких предположений.
А людей смущает именно полная последовательность выводов — что компилятор сделал неожиданный вывод непонятно из чего.

Вот и получается, что не инициализированная переменная безопаснее, чем инициализированная.

А подобные оптимизации предполагают, что ты не только должен досконально знать С и С++, что невозможно, но также досконально знать, что и как будет делать конкретная версия оптимизатора.

Да, с вышеприведенными ситуациями всё в деталях разобрано в инете. Но как много программистов С и С++ это изучили. И кроме того, где гарантия, что не возможна еще куча других таких же странных ситуаций?
В итоге ты имеешь язык, который не гарантирует выполнения того, что ты написал в коде и требует написания кода исключительно людьми типа Майерса.
Не, ну допустим такой лох как я ругается на эти языки. Это мелочь, но я привел ссылку, где его ругают много более умные и опытные люди, чем я.

Неожиданный вывод :) примерно того же уровня ожидаемости, что обсуждаемое решение компилятора :)

На один случай проблемы от инициализированности (если вообще считать, что проблема в этом) — 1000 от мусора в неинициализированных переменных.

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

Твоя программа, что не управляет стержнями АЭС может падать, глючить и это мелочи. На RECIF когда писали и поддерживали прогу для настройки роботов. Она очень любила падать. Но менеджмент решил, что дешевле ее перезапускать инженерам, чем пофиксить падучесть и менеджмент был прав. Но есть места, где цена подобных ошибок очень высока.
Компилятор же инструмент, что используется очень большим количеством людей для разнообразнейшего софта, поэтому цена подобных ошибок в нем высока и очень. Лучше меньше оптимизации, но надежнее его работа.

В данной же конкретной ситуации вообще оказалось

что не инициализированная переменная безопаснее, чем инициализированная.

.

Да, с одной стороны кажется, что я улетел в очень высокие материи, но оное оказывается очень приземленным, когда сам вваливаешься в нее, если пришлось писать что-то важное.

А если еще учесть, что на С++ и С пишут не только люди уровня тебя, а много менее опытные и грамотные, то с С и С++ нынче всё очень плохо.
Я видел как новички парсили RIFF WAVE шапку битовыми сдвигами, как обнуляли весь объект класса мемсетом (скастовав указатель) и тп. Получается, что рядом с каждым новичком нужно садить кого-то уровня тебя и ты будешь контролировать каждую его строчку. Это бессмысленно.

Когда ты 20 лет в С и С++ ты уже на уровня подсознания ощущаешь где код будет опасным, где нет, но большинство программистов не с таким опытом. Да, я уже давно не могу сказать, почему конкретный код будет опасен, я просто чувствую это. Но ситуация с этим статиком меня всё одно сильно удивила. За 20 лет столько раз менялось поведение компиляторов, что и не сосчитать. Баги появлялись и фиксились, потом возвращались и опять фиксилис и так с десятки раз, причем у всех компиляторов свои. Я уже не упомню с каким количеством компиляторов С и С++ и каких осей я имел дело.

У тебя смесь нескольких заметно разных вещей.

Для управления стержнями — вообще ни C, ни C++ не применимы, если серьёзно подходить к вопросу. Минимум — Ada с её правилами и гарантиями. А, возможно, вообще что-то самописное.

Для C/C++ уровень проблем от обсуждаемых случаев UdB, как (не)возможность доступа по nullptr, примерно сравним с уровнем проблем от доступа по испорченному указателю, который тоже UdB. Что одно — ошибка программиста, что другое. Но разница в том, что некоторые вещи компилятор может уловить, а некоторые (как use-after-free) — обычно нет. И ещё — в том, что курсы и книги C/C++, как правило, замалчивают проблемы UdB, и их выпускники предупреждены о доступе по кривому указателю, но не о переполнении знаковой арифметики и тому подобному.

А если еще учесть, что на С++ и С пишут не только люди уровня тебя, а много менее опытные и грамотные, то с С и С++ нынче всё очень плохо.

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

что не инициализированная переменная безопаснее, чем инициализированная.

И вот с этим я по-прежнему не согласен. Проблема не в инициализации. Если бы указатель был на стеке, оно имело бы право решить точно так же, потому что использование неинициализированного значения это такое же UdB.

За 20 лет столько раз менялось поведение компиляторов, что и не сосчитать.

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

Проблема не в инициализации.

Проблема в оптимизаторе. В эти оптимизации заложили ложные предпосылки.

И ещё — в том, что курсы и книги C/C++, как правило, замалчивают проблемы UdB, и их выпускники предупреждены о доступе по кривому указателю, но не о переполнении знаковой арифметики и тому подобному.

Потому что преподаватели просто о них не знают. Если еще с точками следования как-то запомнили и то их уже отменили и там еще то шаманство, но по крайней мере i+++i не пишут уже.

Для управления стержнями — вообще ни C, ни C++ не применимы

А на чем там работа с железом написана, а прошивки? Всё тот же С. Знаешь какой уродливый код в черных ящиках российских военных вертолетов на С?

Но оно менялось приблизительно в одну сторону — больше интеллекта (какой бы он ни был в деталях)

И вот это очень плохо. Интеллектом пока только высшие животные обладают. Вот когда появиться ИИ в широком смысле тогда и добавляйте интеллект в компилятор. А иначе с каждым днем будет все хуже и худе.
Вот UB со знаковыми целыми меня просто выбешивает.
А сколько UB с плавающими, я не знаю. Одна работа с ними уже требует знаний больше, чем есть у среднего программиста.

Но самое главное, язык программирования — это инструмент для решения задач, нужных людям. И если язык программирования требует изучения в течении 20 лет — это уродство, а не инструмент (мы не живем 500 лет, а всего 70 в среднем).
Вон знакомые для одного сервера даже не стали заморачиваться с С или плюсами, а просто попросили матлабовский код матлабом в С преобразовать и заюзали. Да, не оптимально с точки зрения быстродействия, но сильно дешевле, чем портировать ручками в С или С++ (внутрях там RNN и еще пачка алгоритмов).
И мне понравились очень проекты haxe и go, но они пока очень сырые.

Порылся на en.cppreference.com по вопросу дефолтной инициализации

static Function Do;

.

Получается, что указатель на ф-цию попадает в -
«pointer types».
Все эти указатели группируются в — «scalar types».

А вот тут (en.cppreference.com/...​guage/zero_initialization) уже написано, что нулевая инициализация выполняется, если —
«If T is a scalar type, the object’s initial value is the integral constant zero explicitly converted to T.»

Так что вряд ли глобальная переменная — указатель на ф-цию, вряд ли будет чем то инициализироваться, кроме 0.

вряд ли будет чем то инициализироваться, кроме 0.

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

Вообще эта оптимизация — результат огромной, титанической работы над компилятором, в других языках об этом даже речи нет. Что касается undefined behaviour, достаточно выставить уровень предупреждений W3, или W4, и читать сообщения компилятора. Я лично обращаю внимание на все варнинги, и делаю по ним фиксы.

Что касается undefined behaviour, достаточно выставить уровень предупреждений W3, или W4, и читать сообщения компилятора.

False. Множество случаев компилятор не ловит. Там же на хабре были примеры.

Для мест, которые опознаны как подозрительные, специально придумали UB sanitizers с проверкой в рантайме.

Я лично обращаю внимание на все варнинги, и делаю по ним фиксы.

Это хорошо, так и следует делать.

И, упс

OS 	   x86 	x86_64 	ARM 	ARM64 	MIPS 	MIPS64 	PowerPC64
Linux  yes 	yes 			                         yes  	yes 	        yes
ARM ARM64 пролетели.

Расшифруй, непонятно.

Я по ссылке сходил посмотрел. Для линуха

ARM ARM64

 не поддерживаются.
Они только для андроеда поддерживаются.
Это от гугла, санитайзер который.

А если серьезно, то ключик это у GCC полезный, вот только, как я понял при включении всех ворнингов он не включается, его нужно самому добавлять. Но я мог и неправильно понять. 100500 ключиков у компилятора — уже не нормально.
Конечно, можно сказать, что инструмент нужно знать, но когда сложность инструмента начинает требовать ИИ в широком смысле (поумнее человека) — это уже очень кривой инструмент.

Это от гугла, санитайзер который.

В GCC и Clang есть в обоих, но с разными возможностями. Ты который смотрел?

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

Да, потому что это не просто warning, это специальный код рантайма во всех подозрительных местах, и библиотека поддержки.

но когда сложность инструмента начинает требовать ИИ в широком смысле (поумнее человека) — это уже очень кривой инструмент.

Или просто отражающий реальную сложность мира и задачи.

В GCC и Clang есть в обоих, но с разными возможностями. Ты который смотрел?

Я не особо внимательно смотрел, но табличка бросилась в глаза.

Да, потому что это не просто warning, это специальный код рантайма во всех подозрительных местах, и библиотека поддержки.

С компиляторе и так уже куча всего, это там не помешает уже.

Или просто отражающий реальную сложность мира и задачи.

А вот это не так. Сложность мира не изменилось, просто возможности железа увеличились и мы стали способны решать чуть более сложные задачи.
Но вот сам язык не должен усложнятся с превосходяшей железо скоростью.
Вон у Майка жесткое ограничение С98 и не более. С++17, 20 даже не предполагается в будущем. Это и есть ответ бизнеса и людей на безумие в С и С++.
В ядре Линуха ты сам увидел кучу ручных ограничения для безумия компилятора С. Сейчас там 5-10 ключей, завтра будет 20, после завтра 100. В итоге выключат нафиг все оптимизации. Да, рисую негативную картинку, но мы люди имеем свойство выбирать не оптимистичные пути движения в будущее, а чаще негативные (чего стоит засерание планеты — это не по теме, но по сути). Реагировать начинаем, пока гром не грянет.

А вот это не так. Сложность мира не изменилось, просто возможности железа увеличились и мы стали способны решать чуть более сложные задачи.

Ну, я именно этот «проекционный» мир и имел в виду.

Вон у Майка жесткое ограничение С98 и не более. С++17, 20 даже не предполагается в будущем. Это и есть ответ бизнеса и людей на безумие в С и С++.

А это уже тупо. Я понимаю, C++17 не принимать, но C++11 это уровень просто доведения того же 98 до минимально логически замкнутого вида. Если они не могут сменить компилятор из-за внешних причин, вплоть до тупости процессов заказчика — ok, но если это внутреннее решение — то кого-то надо выкидывать в Днепр.

В ядре Линуха ты сам увидел кучу ручных ограничения для безумия компилятора С.

Это вообще-то режим для спецнужд, а никак не умолчание.

Да, рисую негативную картинку, но мы люди имеем свойство выбирать не оптимистичные пути движения в будущее, а чаще негативные (чего стоит засерание планеты — это не по теме, но по сути). Реагировать начинаем, пока гром не грянет.

Так на это давно есть Java/C#/Go/etc.
хотя в embedded они достаточно медленно проникают.

А это уже тупо.

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

Это вообще-то режим для спецнужд

если пишут на С или С++, то там почти всегда такие нужды. Если подобных нужд нет, управляемые языки выгоднее.

Так на это давно есть Java/C#/Go/etc.
хотя в embedded они достаточно медленно проникают.

Вот у меня есть определенная надежда на Go. Но пока он очень сырой.

А это уже тупо. Я понимаю, C++17 не принимать, но C++11 это уровень просто доведения того же 98 до минимально логически замкнутого вида. Если они не могут сменить компилятор из-за внешних причин, вплоть до тупости процессов заказчика — ok, но если это внутреннее решение — то кого-то надо выкидывать в Днепр.

С98 замість СХХ — правильне бізнес рішення в лонграні

C++11 — правильное решение. C++98 — уже давно нет.

я про старий добрий С замість підрихтованого С++

C98 не существует. Если вы про C99, то правильно называйте, чтобы не сбивать людей с толку.

Расскажите это Гуглу, который Хромиум делает, к примеру

Гугл закоснел уже в очень многих аспектах. Это — один из них.

Может быть. Но я думаю, что там просто прикинули проблемы с переходом и решили, что оно того не стоит.

Это и зовётся — закоснели. Потому что проблемы перехода с 98 на 11 ничтожны, касаются только очень специфических мест, методы решения отработаны и описаны на каждом углу. Надо просто взять и сделать. А польза — колоссальна.
(Кстати, я не проверял, так ли они запретили C++11. Тут временно доверюсь. Но был шумный запрет, например, исключений.)

А польза — колоссальна.

Где???

В продукте, где же ещё.

В чем конкретно польза для продукта?

Ну вот простой пример — emplace* для контейнеров. Ранее надо было или страдать от цены копирования, или идти через промежуточное «пустое» состояние, которое недопустимо с точки зрения логики программы. Сейчас имеем конструирование сразу на нужном месте, без затрат на ерунду.
Move-семантика, аналогично, убрала по сравнению с C++98 кучу лишних копирований.
constexpr — устранение пачки вычислений из рантайма.
Повторюсь, C++11 — это то, чем должен был изначально стать C++98, если бы его не заморозили.

Это не для продукта, а для программиста. Да и движок уже давно написан. А для продукта — все равно, на чем писали.

А для продукта — все равно, на чем писали.

Не всё равно, потому что заметное сокращение рантайм-затрат.

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

Последовательность из пустого бессмысленного конструктора и уже реального «конструктора» целевого содержания присвоением или чем-то ещё — соптимизировать было нельзя, потому что пропуск конструктора запрещён.
Точно то же для создания промежуточной копии, которая нужна только затем, чтобы формально вернуть значение.

Для высоконагруженных частей системы используются структуры с тривиальными конструкторами и с оглядкой на расположение данных в памяти. Все эти классы и копирование — в тех 95% кода, которые выполняются 5% времени.

Все эти классы и копирование — в тех 95% кода, которые выполняются 5% времени.

В вашей специфике — может быть.

Move-семантика, аналогично, убрала по сравнению с C++98 кучу лишних копирований.

И сделала возможным создание unique_ptr, а также других некопируемых типов, которые теперь — о чудо! — таки можно куда-то передавать и хранить в контейнерах.
Да, можно было юзать shared_ptr, существовали реализации и до C++11. Но выделять лишнюю память на счётчик ссылок и прочую служебную ерунду для каждого объекта и платить за лишние атомарные инкременты/декременты по каждому чиху — ну, такое.
Ещё был auto_ptr. Он пытался имитировать «перемещение» через конструктор «копирования», принимающий не-const ссылку. Что часто приводило к неожиданным последствиям в рантайме. Неудачный эксперимент, который вышел неудачным именно из-за отсутствия move-семантик в языке.

Но если говорить о самой полезной на мой взгляд фиче C++11, то это override. Писать иерархии классов с виртуальными функциями без него — это полнейший мазохизм, как по мне.

Но если говорить о самой полезной на мой взгляд фиче C++11, то это override. Писать иерархии классов с виртуальными функциями без него — это полнейший мазохизм, как по мне.

Вообще, такие вещи неплохо ловят профильные IDE. У netbeans специальный знак возле функции, которая переопределяет базовую, без необходимости писать override.
(Кроме варианта, когда базовый класс меняют, конечно, и надо отследить все производные. Вот тут, да, лучше опереться на компилятор.)

Что именно им рассказать?
chromium-cpp.appspot.com
Что они неправы что используют множество фич C++11/14, потому что на доу кто-то считает эти фичи злом или C++ «мёртвым»?

Прикольно, они обновились)

Прост уже не первый раз вижу на ДОУ утверждение, что гугл якобы не юзают современный C++. А потом нахожу пруфы обратного :)

.

Так на это давно есть Java/C#/Go/etc.
хотя в embedded они достаточно медленно проникают.

Го -може повільно,
жаба давно в ембедеді, точніше вона з ембдеда вилізла,
Сдієз — теж має життя в ембдедеді
Хрести не нужни,
чистий С — лов\систем левел,
мови високого рівня для аплікейшенів

Баги в компиляторах и оптимизаторах всегда были и будут, но в целом нельзя не согласиться что они делают куда больше пользы, чем вреда.

Ложка дёгтя делает куда больше чем бочка мёда.

На практике такие проблемы если и встречаются, то крайне редко.
Обратите внимание, как народ извращается, чтобы написать, какой компилер плохой.

Это просто для краткости, чтобы быстро отловить баг. А в боевом коде такую дрянь можно ловить ой, долго. Мало кто додумается написать тесты на проверку НЕвызываемости функции.

В данном конкретном примере пишут, что при -О0 оно крешится.
Вы много знаете программ, боевой код которых ни разу не дебажили?
А в дебаге как раз -О0 ставят, чтобы оптимизации не мешали.

А в дебаге как раз -О0 ставят, чтобы оптимизации не мешали.

Не всегда

Ну да, если в фирмварь не влазит, то ставят -Os. Или когда профайлят. Других причин пока не знаю. Если есть — расскажите, интересно.

Я много знаю программ которые на этапе компиляции undefined behavior, а в рантайме всё пучком.

при -О0 оно крешится

Олексей прав. Вот у тебя бочка меда, а я туда ложку дегтя вкину. Мед можешь выкидывать, целую бочку.

Ну вкинь. В любой открытый большой проект. Пускай из-за тебя ядро линукса выкинут, или хромиум с его 10М строк на плюсах.

Речь о С, С++ и компиляторах. Ты думаешь с какой такой радости народ массово бежал с этих языков и пользуются ими сейчас только там, где другого варианта просто нет.
Но свято место пусто не бывает. Мир уже созрел к приходу нового языка на замену С и С++.
В ближайшие 10 лет мы точно увидим смерть этих языков, если они продолжат двигаться в текущем направлении.

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

Не ты первый, не ты последний

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

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

Оттеснили его еще 10-15 лет назад. А вот сейчас ренессансом я это не назвал, это больше похоже на путь в могилу.

Возможно, время покажет. Пока я не вижу нормального конкурента для С++ в его нише. Rust, возможно, не очень с ним знаком, но слышал, что он не так уж хорош, как его рисуют.

Пока я тоже не вижу. Но уже возникновение Rust и Go говорит о многом.
Когда-то возник D, но он возник слишком рано. Если бы сейчас он возник, у него были бы все шансы вытеснить С и С++ в современном варианте.
Go уже у С и С++ забирает прикладную сетевую нишу. Остаются за С только дрова и прошивки в этой части.

Go вряд ли дальше этого зайдёт в замещении С++, ибо сборщик мусора.
Я бы ещё смотрел на Swift как на возможную замену С++ в местах, где не нужен тотальный контроль над памятью и ресурсами.

Go вряд ли дальше этого зайдёт в замещении С++, ибо сборщик мусора.

Мне пока не понятна стратегия Гугла с Go. Посему всё еще может измениться.

Go вряд ли дальше этого зайдёт в замещении С++, ибо сборщик мусора.

Go пойдет настолько, насколько у гугла хватит денег.

Нет.
Если речь про то, что оно иногда подвисает на stop the world, то есть методы и без такого, и неплохие в остальном.

Это всегда медленнее, чем отсутствие GC.

Нет. Доказано на практике, что если допускать достаточно времени перед собственно сборкой, вариант с GC может быть быстрее.
Выигрыш получается за счёт отсутствия затрат на структуру динамической кучи (объекты просто последовательно в RAM), на учёт free/delete для каждого возвращаемого куска, на гонки по кэшу (при типичной организации памяти в managed сборка идёт соседними областями).

Что мешает использовать последовательную память без GC?

без GC процесс уборки памяти детерминирован. Убить этот кусок памяти нужно прямо сейчас. И никого не интересует что этот кусок far jump для кешей. В гарбадж процесс недерминирован, он может убирать только то что считает выгодным сейчас (в том числе по contiguous логике). Поэтому GC может работать быстрее, это нормально.
Но есть и третий путь. Это функциональщина и вот там сборка будет даже поэффективней чем у этих двоих подходов. Гарбадж в Лиспе 50х годов задвигает по эффективности все выстраданные десятилетиями алгоритмы менегеров памяти в императивных языках. Такие дела.

Если под убить кусок вы подразумеваете удалить объект, то ручное управление памятью позволяет делать это когда хочется, другое дело, что логику этого надо будет написать руками, а это не так просто.

Если вы имели в виду вернуть память системе, то и тут можно это делать только когда это нужно.

Когда GC выделяет память, грубо говоря, это стоит какой-нибудь count++ по области памяти. Тоесть экстремально дешево. После этого GC может заняться shrink holes — перемещениями уплотнениями в background. От того и в менеджед языках вы и не видите реальный адрес обьекта. А если увидите то немедленно окантуйте его в fixed{} чтобы GC случайно его не переместил. Обычный алокатор при запросе куска памяти должен сразу быстренько проанализировать где есть дырка для нужного куска памяти, и после переместить уже ничего нельзя (С++). Вот хз как оно дальше работает на уровне TLB процессора и виртуализирования памяти, но в целом логика на прикладном уровне именно такая. Тоесть GC не только при удалении памяти выигрывает, но и прежде всего при ее выделении.
А делать то можно в ручную все что угодно, обычно это называется flyweight и стратегии пулов. Но это другая история.

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

Я когда-то давно заморочился умножением матриц и вот скорость умножение менялась скачком при переходе размеров матриц, соответствующих размерам кешей (скачки были в раз 10 по времени, при переходе границы очередного кэша). Причем Интел и АМД вели себя в корне по разному, причем конкретные версии процессоров.
Но учесть все это для всех целевых процессоров — это гигансткий объем работы. И на новый процессор опять нужно будет переделывать алгоритм.

Это прикол с совпадением локаций строк одновременно обрабатываемых матриц? Если да, то он хрестоматиен. Но и вляпаться в него достаточно легко.
Кстати, вот тут, например, с Java легче — потому что каждая строка аллоцируется отдельно :) и вероятность такого конфликта по всей матрице — падает практически до нуля.

И на новый процессор опять нужно будет переделывать алгоритм.

Уже меньше вероятности — по крайней мере для стандартных алгоритмов — и кэши больше, и 2-way реже... а вообще против такого можно и явный контроль в рантайме поставить.

Нет. Это только о том, в какой кеш (Lx) )кладутся матрицы. Стоит только не влезть в него и скорость резко упадет.
Причем разница в работе с кешами у разных процессоров сильно отличалась.
После этого я понял, что кроме самих АМД или Intel, лучше них написать свой велосипед не получится.
И обычно там реализованы блочные алгоритмы умножения, а не построчные.
Я надеюсь. что MKL это учитывает и не только это.

Что-то мне подсказывает, что ты уперся в конвееры на SSE. И таки да, для Интеля-АМД они весьма по разному себя ведут.
Возился для ARM NEON для своих видеодекодеровых нужд — в конце концов психанул из-за его жуткой капризности и голландца, который доделал все что мне нужно в libav )

Нет, ты не угадал. Именно с кешами нюансы были. И да это был P3 и того же времени АМД, уже не помню какой.

Я мож не совсем в теме, но по моему все,
что связанно с матрицами уже давно на GPU считается.

Нет. Только иногда юзают GPU. Слишком много с ним танцев с бубном. Код под GPU это отдельная и большая работа, даже если ты заюзаешь либы от AMD и NVIDIA. А удобных оберток для них (юзать BLAS, LAPACK влоб — это еще тот мазохизм) пока нет.

Пока рулят Eigen и armadillo для матриц, а они знают про MKL, но не в курсе про реализации для GPU. Может через несколько лет добавят поддержку. Даже в OpenCV поддержка GPU совсем недавно появилась и она очень сырая.

говорят, PyTorch умеет

Here we introduce the most fundamental PyTorch concept: the Tensor. A PyTorch Tensor is conceptually identical to a numpy array: a Tensor is an n-dimensional array, and PyTorch provides many functions for operating on these Tensors. Like numpy arrays, PyTorch Tensors do not know anything about deep learning or computational graphs or gradients; they are a generic tool for scientific computing.

However unlike numpy, PyTorch Tensors can utilize GPUs to accelerate their numeric computations. To run a PyTorch Tensor on GPU, you simply need to cast it to a new datatype.

pytorch.org/...​amples.html#warm-up-numpy

Но речь про С++.

И да, это интересно, но как он с OpenCL и не новыми картами. Например, стандартные тесты с известного сайта для OpenCL просто завешивают всё.

А зачем С++? На питоне писать в разы быстрее, а математика все равно на видяшке будет

Мне нет. Пробовал питон, не пошел он мне. Слишком много на питоне нужно ручками писать для прототипа.
Я на матлабе обычно прототипирую. А затем уже на С++, если прототип отработал ожидаемо.
Возможно мне уже лень еще и питон глубоко изучить, как дубль матлабу.

А можно примеры того что можно сделать с матрицами на бумаге (оно же так проектируется, да?) и для чего вот, например этого youtu.be/...​qf8PaR8VB6gWU1nJ2IQ&t=924 будет не достаточно?

Непроизводительность затрат и сложность механизма там, где нет возможности её полноценно поддержать.

Я знаю софт, который работает на последовательной памяти. Типа выделил кусок в 30GB и пишешь в неё, пока не придёт всем глобальный flush. Но на то, чтобы переточить под него структуры данных, ушло столько времени, что ой.

А если ты пользуешься стандартными алгоритмами на традиционных структурах данных (а в большинстве кода таки вынужден так делать) — «последовательная память» не при делах.

А в чем проблема подложить линейный аллокатор под глобальный оператор new?

В нелинейном освобождении.

У линейного аллокатора delete() пустой, но есть reset().

Я знаю софт, который работает на последовательной памяти. Типа выделил кусок в 30GB и пишешь в неё, пока не придёт всем глобальный flush.

Вроде это оно и есть. Только операторы подменить надо было.

Это уже другой метод — может зваться, например, «временные арены». Главное — именно деструкция всей арены вместо каждого объекта по отдельности.
Против долгоживущих объектов — не поможет.

Это уже другой метод — может зваться, например, «временные арены». Главное — именно деструкция всей арены вместо каждого объекта по отдельности.

blog.molecular-matters.com/...​egies-a-linear-allocator

Против долгоживущих объектов — не поможет.

Долгоживущие выделяются до запуска системы при помощи обычного аллокатора.

Долгоживущие выделяются до запуска системы при помощи обычного аллокатора.

Мне бы такие простые системы, где так можно выделить заранее...

Линейный аллокатор подходит для циклических систем, вроде бекенда.

«для циклических систем, вроде бекенда.»

Эту фразу однозначно в цитатник. :crash:

Вопрос в ее реализации. А реализовать, чтобы не тормозила очень и очень сложно.

Но уже возникновение Rust и Go говорит о многом

Говорит о том, что каждая контора хочет ограничить кодеров в своей тюрячке?

Говорим Rust — подразумеваем Mozilla
Говорим Go — подразумеваем Google
Говорим C# - подразумеваем Microsoft
Говорим Swift — подразумеваем Apple
Говорим Java — подразумеваем Oracle/Android

Чего ради? Такая же тюрьма как и все остальные.

У таких мов як раст проблема що він занадто молодий. Багато змін вносять в нього.

Ты забавный. Половина мира ещё использует С89, которому 28 лет, другая половина на острие хайпа использует супер-новинку С99, которому 18 лет. C11 был всеми дружно послан в жопу, т.к. gcc уже лет 20 имеет некоторые его фичи, остальные фичи С11 писал какие-то отбитые на всю голову чуваки-теоретики со своими нативными поддержками мнимых чисел и прочим трешем, которым никто не пользуется кроме лабораторных работ. Плюс отсутствие обратной совместимости. Вот тебе и приход замены -> сразу в утиль.

По сути сейчас у gcc есть три опции -std=c89, -std=c99, -std=gnu99. Всё остальное будет мёртворожденным по дефолту. Всё, что полезное будет, как расширение gnu99.

По сути сейчас у gcc есть три опции -std=c89, -std=c99, -std=gnu99

И как долго эта картина будет продолжаться, ещё 28 лет?

А почему бы и нет. Если всех всё утстраивает. Мне в gnu99 нравится очень многое, что я бы сделал стандартом в С, толко надо ссаными тряпками разогнать ту кодлу что там сейчас сидит и выстрадала С11. Мне реально в жизни не хватало трёх вещей:

1) case по зонам:

case 1 ... 20:
break;
case 21 ... 30:
break;

2) инициализация массива по енумам

static const GLuint gl_prim_to_hw_prim[GL_TRIANGLE_STRIP_ADJACENCY+1] = {
[GL_POINTS] =_3DPRIM_POINTLIST,
[GL_LINES] = _3DPRIM_LINELIST,
[GL_LINE_LOOP] = _3DPRIM_LINELOOP,
[GL_LINE_STRIP] = _3DPRIM_LINESTRIP,
[GL_TRIANGLES] = _3DPRIM_TRILIST,
[GL_TRIANGLE_STRIP] = _3DPRIM_TRISTRIP,
[GL_TRIANGLE_FAN] = _3DPRIM_TRIFAN,
[GL_QUADS] = _3DPRIM_QUADLIST,
[GL_QUAD_STRIP] = _3DPRIM_QUADSTRIP,
[GL_POLYGON] = _3DPRIM_POLYGON,
[GL_LINES_ADJACENCY] = _3DPRIM_LINELIST_ADJ,
[GL_LINE_STRIP_ADJACENCY] = _3DPRIM_LINESTRIP_ADJ,
[GL_TRIANGLES_ADJACENCY] = _3DPRIM_TRILIST_ADJ,
[GL_TRIANGLE_STRIP_ADJACENCY] = _3DPRIM_TRISTRIP_ADJ,
};

3) анонимные структуры и юнионы.

Теперь у меня это всё есть в gnu99, а всё остальное пусть догорает.

1) case по зонам:
case 1 ... 20:
break;
case 21 ... 30:
break;

Лучше тогда case по булевому выражению:
switch {
case i < 0:
...
case i > 0;
...
}
как реализовано в Go. Ещё там через запятую можно перечислять условия в одном case.

С предлагает это делать через if. Но switch в С — уникально кривая конструкция.

Оригинальная конструкция switch — прямое отображение процессорных комманд. Что там может быть кривого?

Область видимости чего? Кода? Данных?

В С? К чему применяется это термин в С?

Так что тебя не устраивает, покажи пример?

Мне лень искать с одним классическим примером жуткого switch.
Там была помесь цикла со свичом.

en.wikipedia.org/wiki/Duff’s_device ? Кстати хороший тест на собеседовании — объяснить что делает этот код.

Особенно хорошо потом взять на работу такого, кто подобный код пишет.

Тебя пугает код, потому что ты не понимаешь как он работает и никогда не задумывался о машинном коде, который генерирует компилятор. Если хочешь могу объяснить доходчиво на пальцах.

никогда не задумывался о машинном коде

Я с этого начинал в программировании. Но это извращение для больных на голову.

Оно не было никогда «прямым отображением», это такой себе оптимизированный if с возможностью лукапа по таблицам. Для компиляторов тех времён это надо было писать отдельным конструктом.

Оно не было никогда «прямым отображением»,

Jump по таблице из регистра старее проституции.

Вот я и говорю — возможность лукапа по таблицам. Там, где она реализована в самом наборе рассматриваемых констант.
Если у тебя в switch() будут значения 1, 2, 99 и 286, то таблицы как-то не получится.
А с другой стороны — если записать пачку ifʼов, то компилятор тоже может свернуть их до таблицы.
Разницы между if и switch для современных средств — никакой, кроме синтаксиса. Отсюда и идут варианты как в Go, где switch это сжатая цепочка if-elif.

Когда свитч в С появился — это была эра 8 битовых компов и различных микроконтроллеров. Свитч был незаменим при обработке последовательных протоколов а ля BSC для имплементации машины состояний, где 0-31 ASCII таблицы были управляющими символами. Просто открой программы того времени и посмотри для чего оно применялось.

А с другой стороны — если записать пачку ifʼов, то компилятор тоже может свернуть их до таблицы.

Компилятор времен создания? %) В те времена любой высоуровневый язык был прямым указанием для компилятора как делать код и если ты хочешь switch — значит ты хочешь табличный джамп. Только и всего.

Свитч был незаменим при обработке последовательных протоколов а ля BSC для имплементации машины состояний, где 0-31 ASCII таблицы были управляющими символами. Просто открой программы того времени и посмотри для чего оно применялось.

Спасибо, кэп.

Компилятор времен создания?

Нет, современный. Тут я уже говорил о современных.

Спасибо, кэп.

You’re most welcome %)

Путь развития С++ меня уже давно не волнует, мой ответ касался только С. Тем более после переезда тут неожиданно выяснилось, что, например, сейчас есть аж три вакансии среди десятков тысяч: C++/BigData, C++/CAD, C++/Security. И ни в одном из трёх направлений мне не интересно. Во всех остальных вакансиях рекрутеры ошибочно называют С С++’ом. Это болячка интернациональная. Знания последних стандартов требует только одна позиция в C++/BigData. Вторая позиция — это суппорт, третья — работа на правительство, со всеми вытекающими.

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

Народец не показатель. 99% кодерков и на басике с удовольствием программировали бы, если бы под это были проекты.

Менеджмент решил, что индустрии нужны толпы недо-кодерков — и начали применять джавы с шарпами, где ни попадя. Закончили кучей зафэйленных проектов, а в тех что не зафэйлены — отделы сидят в профайлерах и выискивают, где бы чего оптимизировать и заставить «коллектор» чего освободить (в том же «шарповом» коде).
Да и то, что получается на выходе — тормознутое г..., преполненное багами (даже разработческий софт от мелкомягких).

Вы так говорите, как будто если бы те кодерки вместо Шарпа или джавы на Си писали, багов было бы меньше и код читаемый. Лол

Вы так говорите, как будто если бы те кодерки вместо Шарпа или джавы на Си писали, багов было бы меньше и код читаемый

Те кодерки, вместо «шарпов» — шли бы не в «плюсы», а в торговцы на базарах и дворники.
К несчастью, порог входа в «джавы» с прочими «шарпами» оказался настолько низок — что отличные потенциальные дворники оказались говно-кодерками.

П.С. Хотя, почему «к несчастью»? Говно-кодерки ежедневно колбасят столько говнокода, что владельцам этого говнокода не остаётся ничего другого, как приглашать мэтров это всё разгребать. За хороший рейт. :)

Щось я подібне чую весь час як вчив і користувався С++. Але все ніяк він нікуди не дівається. Да, в певних областях під певний спектр задач з’явилась купа більш зручних мов програмування, але С++ забити до смерті буде дуже тяжко.

С наврядчи замінять,
а С++ немає майбутнього крім того щоб обновлювати коден рік стандарт , поки не набридне реанімувати труп

а С++ немає майбутнього крім того щоб обновлювати коден рік стандарт , поки не набридне реанімувати труп

Как знать. Быстродействие процессоров уже лет 10 не увеличивается, новых технологий на замену старому-доброму кремнию пока не видать, написание распаралелленных приложений слишком дорого — а сложность приложений растёт... :)

ага, запусти доу.уа на 10 річному ПК, нічого ж не помінялося

По быстродействию ядра, ничего за 10 лет особо не изменилось. Ядер стало больше — но это приложениям не поможет.

По быстродействию ядра, ничего за 10 лет особо не изменилось

Вот именно что и по одному ядру есть рост (рядом писал подробнее)

Ядер стало больше — но это приложениям не поможет.

Поможет, хоть и не сразу. Медленнее, чем хотелось, но процесс идёт.

так ти загрузув доу.уа на старий комп?

так ти загрузув доу.уа на старий комп?

Ты всерьёз полагаешь, что до 2005 интернет-сайты не открывались? :)

ти дістав свій покритий пилью ПК 10 річної давності і написав пост оттуда?

dou как раз достаточно лёгкий сайт. Тяжёлый — например, FB. А вообще, реплика явно не по адресу.

чувак заявляє, що нікуя не міняється в світі процесорів.
Я ж бачу, що в ембедед уже коробки з процами овер 1ГГЦ та пам"ять овер 4ГБ.
А ти що хоч сказати, що кроме С та С++ в ембед нікуя нема і не буде??

А ти що хоч сказати, що кроме С та С++ в ембед нікуя нема і не буде??

Я хочу сказать, что при останавливающемся росте количественных показателей (для не-читателей: ещё не остановившемся, но останавливающемся) — ценность средств, которые максимально эффективно используют «железо», уже растёт. Когда/если он совсем остановится — будет расти насколько, что все прочие средства будут вымываться к лешим.
А будет то, у чего подскочит популярность, C++, Rust или кем-то ещё — вопрос ортогональный этому.
Но для начала «под нож» пойдут те средства, в которых неустранимо динамическая типизация. Как минимум после этого Python, Ruby, Perl, NodeJS и много других — в лучшем случае станут управляющим клеем над реально исполняющим кодом. Чуть-чуть позже аналогично с Javascript в браузере — и не как Webassembly нынче, а посерьёзнее по нему проедутся.

Вот именно что и по одному ядру есть рост (рядом писал подробнее)

Он микроскопический в сравнении с ростом более 10 лет назад.

Поможет, хоть и не сразу. Медленнее, чем хотелось, но процесс идёт.

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

Он микроскопический в сравнении с ростом более 10 лет назад.

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

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

Шаманством оно было 10-20 лет назад. Сейчас это просто ремесло. Ну а что большинство людей морально остались в старом, и половина фреймворков не поддерживает — будет постепенно лечиться.

о внутренней параллельности через векторизацию.

На одном ядре или 8?

Шаманством оно было 10-20 лет назад.

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

На одном ядре или 8?

На одном.

На блокировках он большую часть времени висеть будет.

Уточни, о каких задачах речь — конкретные области и алгоритмы.
А то примеров на хорошую параллельность я могу накидать сходу чуть ли не десяток, но они наверняка не те, что ты думаешь.

На тебе на плохую — градиентный спуск. Да и почти любой итерационный алгоритм. Это практически все алгоритмы оптимизации. Даже положить simulated annealing на параллельные вычисления уже сильно нетривиальная задача (есть пару статей, что сумели положить на GPGPU, но реализаций я не нашел).
Более того, если алгоритм параллелится, но я не могу сделать тяжелые потоки, они по алгоритму легкие, то я застреваю на блокировках. Или надо глубоко лезть в lock-free алгоритмы. Но не всегда есть то время, чтобы так глубоко лезть.

1. Думаю, даже градиентный спуск можно распараллелить :), точнее, не сам спуск, а задачу над ним. Это ведь обычно поиск экстремума на множестве аргументов? Если разрезать область поиска, можно несколько поисков запустить впараллель. И блокировок тут будет самый минимум.

2. Задачу ты нашёл показательную, не спорю. Но какая её доля от всех решаемых на всём множестве хотя бы рабочих станций? Я боюсь ошибиться в количестве нулей после точки...

Остальные примеры где-то в том же духе. Я всё-таки ожидал, что ты попробуешь предсказать именно типичные «по больнице» задачи...

1.

Да. Так можно. Но еще никто не сделал такой имплементации. Причем в R^n все сильно сложнее становится. И нужно самому под конкретное железо имплементацию делать. Здесь и учет размера кеша и учет количества доступных ядер.

типичные «по больнице» задачи

В мой больнице такие.

2. Задачу ты нашёл показательную, не спорю.

Да, именно показательную вспомнил.

А да при разделении области поиска ты теряешь основную фичу градиентного спуска на границах разделения.
И да подобное разделение это стандартный трюк, что я юзал. Более того, можно заюзать GPGPU и выкинуть градиентный спуск, а реализовать вариант Монте-Карло но не на случайных точках, а на сетке.
Но вот готовой такой реализации я пока не видел. Нужно самому писать. У меня пока знаний GPGPU не хватает для правильной и красивой реализации, с учетом особенностей памяти GPGPU.

И задачи оптимизации — это не узкая область. Они постоянны в расчете дифур, ML и DS областях, моделировании физических процессов.
Даже в визуальных образах при определении преобразовании в 2D, 3D без задачи оптимизации никуда. У массово применяемого ранзака, как метода оптимизации куча ограничений и очень часто он даст в корне неверное решение. Он основан на сильно урезанном Монте-Карло и в первую очередь по причине «голосования». Да, это относительно быстро, но выход только на локальный максимум. И в реальных, не синтетических задачах, на это нарываешься очень часто.

В мой больнице такие.

Да, но твоя ужасно редка.

А в остальном мире таки возможностей больше. Например, тормозящий браузер можно разделить на несколько профилей :)
да и собственно однонитевость JS всяких Firefox это legacy от общего состояния, зафиксированное в API. Скоро всем надоест и его запретят к лешим.

Ну и второй момент без отношения к алгоритмам. Это архитектура процессоров. Современная архитектура универсальных CPU сильно неудобна для многопоточности.
На это ты нарsваешься уже на 8 и более ядрах в CPU.
Да, есть и другие архитектуры, но они доступны в единичных очень дорогих суперкомпьютерах только, о которых мы только читаем иногда в новостях которые именно заточены на многопоточность.
Массовые же GPU и CPU это 2 крайних случая. Один на массовую многопоточность одинакового кода на разных данных. Другой на 1 поток разных команд на разных данных ориентирован.

Современная архитектура универсальных CPU сильно неудобна для многопоточности.

Не «современная», а её удобной вообще не бывает.
GPU не счёт, оно не MIMD, а SIMD.
И надо выкручиваться в этих условиях. Прогресс есть — вон промисы с awaitʼами изобретают.

Современная в смысле, что сейчас массово используется.
И вопрос даже не в SIMD или MIMD, а в собственно железной организации взаимодействия ядер.
Да, универсальной и оптимальной быть не может. Но мне жаль, но не развились массово другие архитектуры для большого количества ядер, кроме варианта в GPU.
Отсюда и такая сложность многопоточного программирования.

И вопрос даже не в SIMD или MIMD, а в собственно железной организации взаимодействия ядер.

Так другого пока не придумали, и не видно, чтобы могли придумать что-то реально новое.
А раз нет идей по другой архитектуре — нет причины предполагать, что она вообще возможна.

Отсюда и такая сложность многопоточного программирования.

Она совершенно естественна, а не железоспецифична. Чтобы это понять, достаточно представить себе функционирование самого себя. Вот у тебя задачи по ремонту, вот — по кормлению семьи, вот — планы выбраться в парк, а теперь вспомни, как все планы ломаются, когда что-то меняется в обстановке.
И это ты неявно в состоянии понять, что какие-то планы надо отменить, а программе надо всё это явно описать.
Ну да, такое надо писать в стиле промисов, а не в стиле мьютексов. Так его уже сейчас освоил любой неленивый.

Так другого пока не придумали, и не видно, чтобы могли придумать что-то реально новое.
А раз нет идей по другой архитектуре — нет причины предполагать, что она вообще возможна.

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

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

Я нет, надо почитать, что это такое и в чем отличие от мютексов.
Но с разделением данных всё та же лажа осталась.

То что в массовом железе не реализовали, это не говорит о том, нет идей и ничего не придумали.

Ну например хоть одну?
Я всё-таки хоть краем глаза, но слежу за этой темой. Ну нет там ничего.

Я нет, надо почитать, что это такое и в чем отличие от мютексов.

Там всё банально.
Делай раз: объект, которому передаёшь функцию действия и функцию реакции. Действие себе где-то исполняется в фоне. Реакция получает ответ этого действия или признак, что то сломалось (по исключению).
Обычно обе оформляются замыканиями.
Делай два: есть стандартный метод навесить в качестве реакции порождение нового промиса (со своим действием).
В результате построение становится 1:1 рисованием блок-схем с возможностью дорисовки или заранее, или на ходу. С последовательными действиями, развилками и т.п., но всё это исполняется где-то асинхронно, а тебе дёргают коллбэки, в которых ты можешь эту блок-схему достраивать.
Обычно где-то снаружи есть то, что вызывает вход в эту конструкцию, и получает из неё выходной результат. Например, веб-сервер: по получению запроса он порождает промис с обработкой запроса, а когда приходит ответ — отдаёт его.

и в чем отличие от мютексов.

Отличие в режиме работы с данными.
С тредами и мьютексами ты, например, лочишь данные, работаешь с ними, снимаешь лок.
Тут тебе залочить ничего нельзя, ты работаешь порциями. Если несколько таких одновременно в несколько порций — рисуешь изоляцию транзакций.
Зато можно в любой момент посмотреть, сколько таких и что они изолируют.

Но с разделением данных всё та же лажа осталась.

И её тоже принципиально устранить невозможно.

pipes and filters как раз для этого. Конечно не любой код туда ложится, но много высоконагруженного как раз таки да.

docs.microsoft.com/...​atterns/pipes-and-filters
www.dossier-andreas.net/...​ture/pipe_and_filter.html
Вообще — базовый метод распараллеливания не влазя в алгоритм. Используется в обработке видео, компьютерном зрении и других многошаговых задачах.

А где либы, реализующие оное под линухом?

кажется, нигде. самому можно написать. gstreamer типа юзает.
впрочем, значек «|» в шелле — тоже оно.

И почему я на 90% был уверен в подобном ответе от тебя?

Шаманством оно было 10-20 лет назад. Сейчас это просто ремесло.

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

Не все такие умные, как ты.

ага, запусти доу.уа на 10 річному ПК, нічого ж не помінялося

Запустил на тачке 2004 года, и?

Быстродействие процессоров уже лет 10 не увеличивается

Увеличивается. Если для Nehalem считалось нормальным, что оптимальный код работает на 1 IPC (instructions per cycle), то для Haswell уже нормально целиться на 3-5 в плотных циклах. И это ещё до векторизации. Всё ранее SandyBridge уже вредный отстой, Haswell где-то средненормально. Для AMD картина похоже, но чуть задержалась. ARMʼы тоже неплохо развиваются. IBM показывает огромные успехи с z14. А ещё DDR4, которая ускоряет типичные нагрузки.

Хотя по сравнению с темпами до 2005 и для слабооптимизированного софта это всё, конечно, мышкины слёзы.

И разве это баг? Это фича в случае UB, которое ты не заметишь, пока не вступишь.

Что поделать. В крайнем случае можно отключить оптимизации и включать только те, в которых уверен, вручную.

А в ключиках нескольких компиляторов не утонешь?
Более того, как можно быть уверенным в конкретной оптимизации. Вот включил ты ее и программа по веселому стечению обстоятельств год работает, а потом внезапно херит кучу данных. Представь, что эта прога на самолете.
Получается, что мы вернулись к тому времени, когда оптимизатор все выключали. Но в этом случае ты получишь просадку быстродействия и объема проги. Ручками на асме под современные процессоры особо не на оптимизируешь — учесть кеши, конвейеры, многопоточность. Это в молодости я мог посчитать время асмовских команд и знать сколько точно мой код будет выполняться.

Вот я и возмущен тем путем, куда ломанулся С и С++. Одна надежда, что еще какое-то время эти языки продержатся и на оставшийся мне век хватит и на урезаном подмножестве этих языков и максимально аккуратно писая мой код будет работать так, как я ожидаю.

Более того, как можно быть уверенным в конкретной оптимизации.

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

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

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

Вот я и возмущен тем путем, куда ломанулся С и С++.

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

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

Уже есть пример с Виндой. Я на ней сидел еще с доса, но Win 8 и дальше таки стимулировали меня перейти в Линух.
Да можно рассказывать, что у мелкомягких еще куча рынка за ними, но, как говорил Остап «лёд тронулся, господа присяжные». Пока мелкомягких сильно поддерживают те, кто пишет игры. Остальное — это откаты.
А до мелкомягких все сидели на IBM и где теперь те оси от IBM.

Ну, если С++ вытеснят с рынка, будем писать на чём-то другом. Пока что я не вижу какого-то заметного оттока людей из С++.

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

Я имел в виду оттока с ~2012 по сейчас.

Вот сейчас мы как раз в том времени, когда определится или отток будет, или С и С++ начнут возвращаться к своей основной идее. Если не начнут, то в ближайшие года С и С++ начнут опять вытесняться в еще более узкие ниши и пойдет отток. Точнее застрянут здесь остатки старперов, как с коболом.

Если в С++20 таки доставят все вкусные фичи, которые нам так долго обещали (концепты, сеть, рефлексию), то С++ никуда не денется ещё очень долго.

Метаклассы, вон, если сделают, можно будет выкинуть все moc’и, что очень хорошо.

Нужна возможность стандартизованного, кроссплатформенного применения этих самых правил, которые по умолчанию ведут к UdB.
Например, переполнение в операциях чисел со знаком. C# дал хороший пример, но слишком общий. Представим себе пометку куска кода знаком вида [[arith(signed, truncating)]]. Нужно кому-то в этом куске работать, как в Java или Go — остаются только младшие биты результата? OK, распишитесь. Умолчание — оставить прежним.
Главное — практически всё для этого уже есть. GCC уже давно научился, например, такой штуке, как #pragma GCC optimize(ключ). А на общем уровне есть -fwrapv или -ftrapv. Вот просто совместить их. Ну и протащить через стандарт (самое сложное, займёт лет 5 и кучу седых волос).

Да, наверняка индусы будут ставить такие прагмы на весь свой код. Ну это их проблема. Зато не потребуется смена языка, чтобы расчистить какой-то кусок кода до приличного вида и снять с него подобные ограничения.

Пока же это всё на 90% тупая историческая нелепость (я как-то вытащил из khim@habr признание, что UdB для операций знаковых целых вызван именно тем, что нет гарантии, что отрицательные будут в дополнительном коде — а ведь железа с другой реализацией сейчас тупо не встретить от слова «совсем»).

Кому это все на практике надо? Либо берется размер с запасом, чтобы не влететь в оверфлов, либо ты четко представляешь, что делаешь, и что должно получиться.

Либо берется размер с запасом

Ага-ага. Вот брали такой размер с запасом, умножая на некоторую константу. Теперь вдруг в соседнем отделе эту константу увеличили, например, в 10 раз, и весь запас съелся. ИЧСХ — человек не успевает заметить такое изменение обстановки, а компилятор — замечает, но оборачивает в свою сторону — решает, что x никогда не будет больше 100, и поэтому цикл по x до 200 превращает в вечный (x<=200 заменяет на true).

либо ты четко представляешь, что делаешь, и что должно получиться.

«Ты» (программист без компьютера в голове) не может охватить сознанием весь код, если он больше нескольких страниц. Отдельные гении только подтверждают существование статистически подавляющего большинства.

Ну чувак, если у вас код шарится между отделами — пишите юнит-тесты. Так делают все, например, Гугл.

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

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

Вот и «серебряную пулю» нашли. Вот только не действует она на нежить, твоя пуля.

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

Даже параноидальное тестирование не гарантирует тебе отсутсвие багов.

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

Так таки будет глючить.

Но хоть глючить не будет.

Ты уж определись.

Да, а твое распознавание речи может не распознать речь из-за сбоя в оперативке, вызванного космическим излучением.
Все глючит, часто или редко или когда железо разваливается. Ассерты смещают в сторону «редко».

Конечно и не только по этому. А еще потому, что могут быть баги в распознавалке как программные, так и алгоритмические и потому что речь — это случайный процесс (называется квазистационарный случайный процесс).

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

То, что тесты не проверяют все случаи — по сравнению с этим уже второстепенное.

Собирай все в либу, и подключай одну и ту же версию к основному модулю либо к юнит-тестам.

И потеряю в производительности. А если речь идёт о решении на шаблонах C++ — то или вообще о библиотеке речи нет (всё инлайнится), или потеря производительности даже не в разы, а на порядки.

Ну чувак, если у вас код шарится между отделами — пишите юнит-тесты.

Одно из первых, что делают люди, которые разъясняют особенности UdB — что никакими юнит-тестами это не ловится.

Не надо охватывать весь код. Весь код должен быть сносно написан, и если в нем влазишь в оверфлов — это проблема, которой не должно быть.

Мышки, станьте ёжиками ©

Боишься — опять-таки обложи юнит-тестом.

Не проверяется случай UdB тестом. И не надейтесь.

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

На это стандартный ответ обычно бывает — с такой скоростью результата, как получается при assertʼах на любой чих, лучше писать на Go. Там хоть до порчи памяти не доводит.

Одно из первых, что делают люди, которые разъясняют особенности UdB — что никакими юнит-тестами это не ловится.

Запись за пределы массива ловится включением отладки в менеджере памяти (если массив динамический) или добавлением байта за пределами массива и проверкой, что он не перетерся. Cловили юнит-тестами или ассертами UB)

Не всегда и не везде. Уж ембедеру это следует знать.
Т.е. проверяй сам и пусть компилятор не умничает с UB.

Видимо, кое-кто ни разу не сталкивался со случаем, когда сбой в индексации не на 1-2, а на несколько тысяч :)

Он с WinCE похоже дела не имел. Там бы он это прочувствовал полностью.
С какой скоростью я убегал от этого уродца. Аж пятки сверкали.

Но странно, что с железом он этого не попробовал, где оси и нет.

А под Valgrind запустить?

В мире не только линух есть с валгриндом и он кучу всего пропускает.
Давным давно отказался от подобной мути. Толку от таких инструментов мало.

А вот это зря. Наиболее типичные ошибки им таки ловятся отлично.

Так типичные я давно и так вижу и не делаю уж лет 15 минимум. А вот не типичные — это уже другой вопрос.
По крайней мере с типичными столько лет в своем коде не сталкивался.

Вот что меня поражает у большинства приплюснутых — это гигантское ЧСВ. Им кажется, что они подобны Иисусу. А да, я этим лет 15 назад тоже переболел.

Нет, если течет — оно как раз для того. Там еще Helgrind есть и cppcheck

Ну, 90% случаев таки отловит. Но это никак не гарантия.

А мне даже понравилось. На редкость правильная оптимизация, просто недоработана идеологически. Идеология для языка — не мелочь. Это его основная задача, поскольку языки пишутся не для машины, а для человека.

Смотри что происходит: функция считается инициализатором значения, то бишь конструктором. Потому она и подлежит оптимизации. Но тут мы сталкиваемся с другим нюансом: почему инициализация не ленивая? Ведь с точки зрения оптимизации, конструировать всё одновременно — не есть гуд, если код позволяет, инициализации надо откладывать, очень часто «навсегда» — львиная доля кода используется ну очень редко, а уж данные тем более.

И что ещё более мерзостно, так это то что код перестаёт отражать то, что происходит на самом деле. Более невозможно дебажить код на C++, его приходится дебажить уже на куда более низком уровне, читай очень дорого.

Правильная оптимизация — это ЕСЛИ результатом её будет код на C++. То есть можно свободно читать чего он там наоптимайзил, и даже копипастить это в исходник. Для Java баги подобного толка заманухи тоже имеются (хотя разумеется куда реже), но там ты можешь честно декомпилировать байткод. А здесь как — ассемблер читать предлагают?

// Хотя декомпилить Stream API с лямбдами — то ещё удовольствие, узреть что же на самом деле оно творит. Потому и стараюсь сразу писать нормальный код, чтобы читатель сразу видел что происходит.

Правильная оптимизация — это ЕСЛИ результатом её будет код на C++. То есть можно свободно читать чего он там наоптимайзил, и даже копипастить это в исходник.

en.wikipedia.org/...​ki/Instruction_pipelining уже ни на какой С++ не ложится. А без него — никак.
Это не значит, что компилер в примере не кривой.
Это не значит, что такие примеры встречаются в жизни.

Это значит что тупо поставили в продакшен откровенно сырую версию, назвав баги фичами, да ещё и поставив по дефолту в пакет оптимизации.

Мне нравится сама идея. Но уж никак не реализация.

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

Вообще, pipelining — это самая основа. Для гипертрединга он не годится, там нужна полноценная суперскалярность, причём с Tomasulo в основе.

Без этого вообще ничего не наоптимизируешь. Причина тормозов на if`ах — как раз сброс пайплайна.
www.agner.org/optimize/optimizing_cpp.pdf

А здесь как — ассемблер читать предлагают?

Таки да. Вперед в прошлое.
С++ с компиляторами уже вплотную подобрался к грани, когда ни один компилятор в мире не сможет ничего гарантировать при компиляции и ты будешь получать хер поймешь что. Ибо тебя поджидают тучи UB.
Сложность разработки на С и С++ уже зашкаливающая. 50 лет от асма убегали и к нему вернулись сделав круг (и да на асмах для разных процев я попрограмировал в молодости — больше не хочу).
Так что не удивлюсь, если в ближайшие 5 лет Гугл продвинет Go на замену С и С++. Им стоит только плотнее им заняться для разного железа.

т.е. го, руст и иже с ними автоматом рашат вопросы с оптимизацией? можно спросить — как?

Есть надежда, что там минимизуруют количество UB, а работать будет не сильно медленнее С.
Чем более жесток язык в возможностях, тем легче его оптимизировать.

если принять во внимание, что магии не существует, я не виже путей как добиться и того и того одновременно

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

Чем более стандартный код, тем меньше возможности кодописателю показать, как он хочет руками это оптимизировать.
Так как джава все еще медленнее С++, а узкие места пытаются переписать на ассемблере, то в ближайшие годы не светит.

Потому что там прослойка в виде JVM, как минимум. Второе — горбатая коллекция.

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

Ось вам ще трохи С-кріпоти від мого колишнього одногрупника.
habrahabr.ru/post/136283

Ну, на захист плюсів можна сказати, що приклади там на чистом С, а плюси, особливо нові стандарти де є std::function(), дозволяють піти від вказівників на функції і скоротити кількість UB.

Подписаться на комментарии