Как стать автором
Обновить

Комментарии 361

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

Но вот 6 лет назад (скоро уже 7 лет) занесло на узконишевую платформу IBM i и специфические задачи - работа с БД и коммерческие расчеты. И обнаружил что если есть язык, специально предназначенный для решения какого-то класса задач, то решать эти задачи на нем и проще и быстрее и эффективнее чем на любом языке "для всего". Потому что он специально под эти задачи сделан. И код на нем получается простоя и понятный. И работает он быстро и эффективно. И вот прям зашло... Хотя если что-то низкоуровневое-системное, то тут, конечно, С удобнее...

Numba

что если есть язык, специально предназначенный для решения какого-то класса задач, то решать эти задачи на нем и проще

Поздравляю парни, да вы открыли понятие DSL!

А любой DSL можно на любом другом языке реализовать. Хоть на Python, хоть на C++, да хоть на JSON.

И это не проблема "базового" языка, это проблема продуманности (или, что чаще встречается - непродуманности и/или излишней абстрактности-заумности) предлагаемых API и прочих специфичных задаче сниппетов и структур.

Применительно к С++ это и вовсе означает очевидное - пора выбросить многовековое нагромождение шаблонных несуразиц по имени STL как "стандарт", которое задает тон всем остальным библиотекам, и заменить чем-то более простым, понятным, адекватным и современным.
Взяв лучшее от "C++ вытеснителей".

И дело пойдет. Но никто пока не решается.

Применительно к С++ это и вовсе означает очевидное - пора выбросить многовековое нагромождение шаблонных несуразиц по имени STL как "стандарт", которое задает тон всем остальным библиотекам, и заменить чем-то более простым, понятным, адекватным и современным.Взяв лучшее от "C++ вытеснителей".

Ок. Выкинули все из С++. Теперь вопрос - вы работаете с БД. В больших объемах - лопатите десятки и сотни миллионов записей по многим таблицам.

И вот простенькая задачка - прочитать запись из одной таблицы по ключу, как-то поработать с ее полями (а там могут быть date, time, decimal, numeric) и положить запись в другую таблицу. Сколько зависимостей вам придется подтянуть в С++ и сколько кода написать. А на советующем DSL это выглядит так:

dcl-ds dsMyFileRec likerec(MyFile.MyFileRecF: *all);

chain (KeyValue) MyFile.MyFileRecF dsMyFileRec;

dsMyFileRec.numericFeild += 1;
dsMyFileRec.dateField += %days(3);

update MyFile.MyFileRecF dsMyFileRec;

5 (пять) строк кода.

1 - объявили структуру "такую же как формат записи MyFileRecF в таблице MyFile

3 - прочитали запись для значения ключа KeyValue в эту структуру

5 - увеличили в записи поле numericFeild (допустим, оно имеет тип NUMERIC - это тип данных с фиксированной точкой) на 1

6 - увеличили дату в поле dateField на три дня

8 - сохранили запись обратно в таблицу

Заметьте - все это на 100% средствами языка и в рантайме не создано ни одного дополнительного объекта (чтобы работать с типами numeric и date) - все типы данных, что есть в БД, нативно поддерживаются языком.

Это можно написать на С++. Но так написать не получится. Будет длиннее и много лишних телодвижений.

Хитрые сипипишники скажут - просто надо воспользоваться каким-нибудь фреймворком или библиотекой на С++ для БД!

Хитрые сипипишники скажут -  просто надо воспользоваться каким-нибудь фреймворком

Как хитрый C++ ник я могу сказать - ничего лучше INSERT INTO table2(...) SELECT ... FROM table1 до сих пор не придумано.

Если не хватает базового SQL (и каких cross-database links), то есть понятие EXTERNAL TABLES, когда вывод некого скрипта можно представить для сервера баз данных как виртуальную таблицу (к примеру в .CSV формате) и далее опять куда там нужно тебе INSERT INTO SELECT FROM

И то - это если у тебя какого PL/SQL нет под рукой (который вполне покрывает 99% задач по трансформации данных).

Это если говорить о реалиях реальной жизни.

А если говорить о "взагали по взагалях" и как оно неплохо было бы - то возможности C/C++ тебя не ограничивают, упарываться в DSL можно как угодно - к примеру из таблиц/вьюшек нагенерировать классов в стиле OORM. И взять любой из десятка доступных API - от преснокислого ODBC до прям специализированных, OCILIB какой к примеру очень и очень неплох.

Вопрос то в чем собственно? Если не удалось найти достаточно выразительный и удобный DSL (а SQL и PL/SQL это как раз примеры очень удачных DSL), то напиши свой. И документацию к нему не забудь. Для себя-же, который будет это все потом через пару лет читать и материться, в духе "какой же баран это все так криво понаписал".

Если не хватает базового SQL (и каких cross-database links), то есть понятие EXTERNAL TABLES, когда вывод некого скрипта можно представить для сервера баз данных как виртуальную таблицу (к примеру в .CSV формате) и далее опять куда там нужно тебе INSERT INTO SELECT FROM

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

Ну или вот задачка. Есть клиенты. Их 50млн. У каждого 5 типов адресов. Это с одной стороны. С другой стороны есть "субъекты списков росфина" (террористы/экстремисты всякие). Их... ну тысяч 300, положим. И у каждого по 2-3 адреса.

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

И как решать будете? Прикиньте количество комбинаций (пар для сравнения).

С учетов способа хранения всего этого в БД (в виде витрин, где все адреса уже разложены по элементам) можно сделать все это скулем. Запрос получается на три страницы. И работает очень долго (на тестовом юните, где данных кот наплакал - субъектов там реальные объемы, но клиентов всего-то тысяч 20) оно работает где-то 60-70сек. На бою это растянется на несколько дней, что неприемлемо.

А на DSL, без скуля вообще все это работает в разы быстрее (на тех же данных на тесте 4.5сек). Хотя бы за счет того, что там большая часть поверок идет без чтения записи в БД. Просто по наличию соотв. ключа в индексе (да, DSL такое позволяет - просто посмотреть есть оно в индексе или нет, не лазя в саму таблицу за записью).

Ну а если нужен скуль -

       dcl-proc GetCustInf;
         dcl-pi *n extpgm('CUS001');
           inCusNo like(arCNum) const;
           outName like(arName);
           outAddr1 like(arAdd1);
           outAddr2 like(arAdd2);
           outCity like(arCity);
           outState like(arStte);
           outZip like(arZip);
         end-pi;
         exec sql select arName, arAdd1, arAdd2, arCity, arStte, arZip
                  into  :outName, :outAddr1, :outAddr2, :outCity, :outState,
                        :outZip
                  from   ARMSTF1
                  where  arCNum = :inCusNo
                  fetch first 1 row only
                  with CS
                  use currently committed;
         return;

Просто вставляем SQL запрос в код на DSL. А всю сложную логику пишем на DSL. Никто не запрещает. Embedded SQL называется. Запросы любой сложности. С подзапросами и прочим. Можем из программы на DSL вернуть SQL ResultSet если надо. Можем принять ResultSet из другой программы.

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

Все эти "виртуальные таблицы в CSV" на таких объемах просто встанут колом.

Почему они встанут колом? Эти виртуальные таблицы (в формате CSV) можно чем угодно делать - от bash до С/С++, да хоть на ассемблере. В базу данных подается просто готовый результат уже, который нужно сохранить (если нужно). Возможно сейчас не совсем было понятно, что такое EXTERNAL TABLES и как они работают, но... можно погуглить наверное. В простейшем случае это SELECT FROM stdout - а кто в этот stdout пишет и что именно - это вопрос десятый.

А так - для современной базы данных сотни миллионов записей в день это не проблема. Там практический лимит был для типового не очень свежего 16 ядерного x64 сервера около 5 млрд. записей, по 100 байт каждая, в сутки. С парой индексов сверху. Без индексов сильно больше. И то оно все упиралось просто в хранилище. А если direct path insert какой взять INSERT /*+ APPEND */ в простонародье, и без логов - то там и сильно больше 5 млрд можно осилить. Не в один поток естественно это все.

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

Просто вставляем SQL запрос в код на DSL. А всю сложную логику пишем на DSL. Никто не запрещает. Embedded SQL называется.

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

Другой вопрос - где вы такие объемы берете-то.

Да объемы не вопрос. Банк. 50млн клиентов. И у каждого куча данных. Счета (у крупного корпората их может быть сотня, у обычного физика и то десяток счетов, считая кредитные и депозитные, не редкость), карты (плюс держатели карт), доверенные лица всякие, клиентские данные - документы, адреса, доверенности, контакты... А еще всякие риски, платежные документы, проводки...

Сколько там всего этого трудно сказать. По самым грубым оценкам - несколько десятков тысяч объектов БД.

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

писать свой DSL для подобного

А кто говорил про самописный? Это не самописный язык. Речь про RPG. Он с 1959-го года существует. В целом, все, что можно написать на С, можно написать на RPG и наоборот. Но сравнивать их бессмысленно т.к. есть вещи, которые удобнее и быстрее писать на одном, а есть - на другом. Бизнес-приложения - это про интенсивную работу с БД и всякие расчеты, связанные с датами, временем и деньгами (а деньги считаются только в формате с фиксированной точкой). Поэтому вполне логично, что все это реализовано на уровне языка.

И да, вопрос производительности стоит крайне остро. Потому что в нормальных условиях нагрузка на сервер 50-60%, а в пиковых (например, предновогодний шоппинг) может резко подскочить до 90... Сервер - 120 8-поточных ядер Power9.

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

Насчет DSL-подхода (и, что характерно, на примере все той же задачи взаимодействия с БД) весьма неплохо написано вот в этой статье: https://habr.com/ru/articles/422667/

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

Откуда уверенность, что DSL это обязательно какой-то самописный велосипед?

Вот есть у IBM платформа IBM i - middleware коммерческие серверы. Для бизнеса в первую очередь. История там длинная, если закапываться совсем вглубь, то можно дойти до System/3. "Новая история" - System/38 -> AS/400. Дальше уже больше ребрендинга. Сейчас оно называется IBM i (есть еще мейнфреймы IBM z, но они от System/390 идут, это другое).

И есть на этой платформе DSL - RPG Корнями уходит в 1959-й год (первая реализация эмулятора табуляторов для IBM 1401).

Так вот, на нем тут пишется более 80% кода. Де-факто на этой платформе это стандарт. Появился SQL - добавили в RPG возможность встраивать SQL в RPG код (exec sql ...).

И да, для решения задач в тех областях, где эта платформа используется, это самый эффективный путь. Можно пробовать (и пробовали) делать все то же на С. Или на С++. Но ни по скорости разработки, ни по качеству результата никакого выигрыша нет. Скорее даже наоборот. Будет прикручивать к тому же С++ кучу зависимостей, чтобы получить то, что в RPG уже есть на уровне языка. То, что в RPG реализуется compile time типами, в C++ будет реализовано создаваемыми в runtime объектами (а это лишнее время и лишние ресурсы).

Но если мне, например, нужно удобное API для работы с какой-нибудь User Queue (есть такой тип системного объекта), я сделаю это на С. А потом буду использовать из RPG приложений уже под бизнес-задачи. Ну и до кучи напишу на RPG UDF/UDTF чтобы можно было со всем этим из SQL работать.

Синтаксис выглядит забавно. Мы в oracle apex подсмотрели такой подход, чтобы не объявлять десятки переменных l_out_name ARMSTF1.arName%type;:

procedure get_custInf(
    p_arcnum in ARMSTF1.arcnum%type
) as
    cursor l_c is
    select
        arName,
        arAdd1,
        arAdd2,
        arCity,
        arStte,
        arZip
    from
        ARMSTF1
    where
        arCNum = p_arcnum
    fetch first 1 row only;
begin
    for i in l_c loop
        -- i.arName etc
        exit;
    end loop;
end get_custInf;

Минус фреймворка в том, что там слишком много чего делается в рантайме. В приведенном выше пример структура - статическая. Она "раскручивается" на этапе компиляции. Ровно так, как если ее просто руками наколотить в структуре записи в БД. И никаких маппингов, никаких дополнительных объектов в рантами не создается. И типами с фиксированной точкой, датами, временем работает точно также как в С работаем с int или double... Ну есть специфические операции типа операций с округлением - просто нужно специальный модификатор поставить который указывает что делать при уменьшении количества знаков после запятой - откидывать или округлять. Но это уже тонкие тонкости всякие. А если пишем a = b + c, то все три переменных могут быть любого числового типа - int, float, decimal, numeric...

>Минус фреймворка в том, что там слишком много чего делается в рантайме

Сделайте фреймворк, который будет делать "слишком много" не в рантайме, в чём проблема?

Дело в том, что у меня нет нужды делать какие-то фреймворки.

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

Так речь не о вас, а о вашем ошибочном суждении. Конечно ошибочно оно не только у вас, если посмотреть на весь зоопарк DSL.

>И никаких маппингов, никаких дополнительных объектов в рантами не создается. 

Сделайте ещё специализированную БД и прикрутите к ней этот фреймворк и там тоже не потребуется создавать маппингов и прочих рантаймов. А потом посмотрим, что будет эффективнее - ваш DSL или специализированная система.

dcl-ds dsMyFileRec likerec(MyFile.MyFileRecF: *all);

chain (KeyValue) MyFile.MyFileRecF dsMyFileRec;

dsMyFileRec.numericFeild += 1;
dsMyFileRec.dateField += %days(3);

update MyFile.MyFileRecF dsMyFileRec;

5 (пять) строк кода.

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

UPDATE myFile rec
   SET rec.numericField = rec.numericField + 1,
       rec.dateField = rec.dateField + 3 -- ook, Oracle specific
WHERE  1 = 1; --  likerec(MyFile.MyFileRecF: *all)

Ну да, += в SQL так и не завезли, печаль печаль огорчение :) Но по сути, чем вот то что выше лучше? Возможности вставить произвольное процедурное в процессе - это тоже есть, можно на каждое UPDATE хоть JSON запросы на внешние вебсервисы дергать, и даже результаты в другие вебсервисы засылать, есть и такие средства.

Ну я привел пример из серии Hello Word. И это не псевдокод. Это реально скомпилируется и реально сработает. Оно действительно вот так просто все.

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

Начать с того, что эта структура может вам не из БД прийти, а как параметр вашей процедуры (допустим, это некий актор). И вам нужно будет произвести несколько десятков манипуляций с разными ее полями, подтягивая дополнительные данные откуда-то. А потом отдать ее обратно уже в модифицированном виде. И так 100млн раз в день (примерно с такими объемами работает комплекс комплаенс-проверок в системе контроля платежей).

Про возможность вставлять SQL запросы непосредственно в код написал выше. Иногда быстрее и эффективнее действительно тянуть данные из БД SQL запросом. А иногда наоборот - прямой доступ и по времени и по ресурсам будет выгоднее. Это уже на основе накопленного опыта понимаешь где что лучше.

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

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

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

В современном мире использовать SQL для highload runtime, да еще и с гарантией по времени отклика - да, это так себе идея. Тем не менее, о чем вообще спор был изначально?

С++ вполне может быть адекватен даже для бизнес задач. Для тех самых типовых задачек вида "конвертируем что-то из одного условно-табличного вида в другой" (к которым сводится 99.9% всех айтишных задач).

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

В современной разработке что C++, что Java подходы из коробок/книжек - они, изначально, мягко говоря, не про бизнес задачи. Сильно много слов заставляют писать не по "теме".

LINQ вот еще подавал надежды, но тоже сдулся. lsFusion еще тут мелькал, как альтернатива 1С, но... не будем больше о грустном.

По хорошему нужно что-то, что может в одном адресном пространстве (без всяких IPC/TCP/etc) иметь сразу и ORM, и SQL, и сразу HTTP(s)/JSON, и чтоб быстро и чтоб надежно, и еще column-storage. И без лишних конвертаций туда-сюда. И чтоб язык был хороший для "скриптов".

Tarantool еще, ах да. Интересно, так и пытаются LUA продавать?

LINQ вот еще подавал надежды, но тоже сдулся

нельзя ли поподробней? пропустил похороны поциента.

Тем не менее, о чем вообще спор был изначально?

В целом - про то, что бессмысленно притягивать за уши С/С++ "до уровня DSL" чтобы получить то, что уже есть в DSL, но с потерей времени. Т.е. оно ни работать быстрее не будет и писать быстрее не получится.

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

Вот вам еще про скуль.

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

По этой таблице есть индекс по ID клиента и DT - дате его актуализации.

Очень много ситуаций когда в задаче требуется получить текущую (последнюю) ДА по клиенту. Так вот, "скулевое" решение

select ID, max(DT)
  into :id, :dt
  from DATbl
 where ID = :id
 group by ID;

работает ощутимо медленнее, чем решение с прямым доступом

dcl-ds dsDATblRec likerec(DAIdx.DATblRecF: *all);

setgt (id) DAIdx;
readp DAIdx.DATblRecF dsDATblRec;

Это подтверждается PEX (Performance EXplorer) статистиками. Скуль пытается что-то там агрегировать в группе, в прямом доступе SetGT тупо по индексу ставит указатель после последней записи с заданным значением ключа, а потом ReadP читает предыдущую запись. Которая и есть в данном индексе запись с максимальным DT для заданного ID.

в прямом доступе SetGT тупо по индексу ставит указатель после последней записи с заданным значением ключа

Это вам повезло, что ставит указатель именно после последней записи. А если бы ставил перед первой? А если бы после последней, но задача была бы `select id, min(dt)` ?

Конечно, легко найти один частный случай...

А если в SQL варианте для max() что-то "агрегируется" с заметными тормозами, это просто недоработка оптимизатора конкретного движка. Вы будете давать зуб, что все движки тут недорабатывают, или кроме DB2 ничего не проверяли?

Это вам повезло, что ставит указатель именно после последней записи. А если бы ставил перед первой?

Если я правильно понимаю, что означает "SetGT", то "поставить перед первой" было бы "SetLT".

то "поставить перед первой" было бы "SetLT".

Может, да, может, нет... надо смотреть в детали. Тем более что там есть ещё над чем подумать:

  • Откуда взялась именно такая сортировка, кто-то создал курсор по составному индексу? Это не сказано.

  • Если этот id последний, SetGT станет на последнюю позицию или пустую за последней?

  • А для SetLT, если это первая, станет на первую или перед первой? Нужен readn (по аналогии с readp) или нет? Эти случаи кто-то отработал?

  • Зачем сначала становиться за последней позицией, а потом возвращаться назад?

Я писал когда-то под Clipper и FoxPro и помню эту беготню курсорами... да, оно просто на совсем простом. Но затем рост сложности, в отличие от SQL, практически экспоненциальный...

Это вам повезло, что ставит указатель именно после последней записи. А если бы ставил перед первой? А если бы после последней, но задача была бы `select id, min(dt)` ?

dcl-ds dsDATblRec likerec(DAIdx.DATblRecF: *all);

setll (id) DAIdx;
read DAIdx.DATblRecF dsDATblRec;

Вместо SetGT используем SetLL - он ставит указатель перед первой записью с указанным значением ключа.

Вместо ReadP (чтение предыдущей записи) используем Read - чтение следующей записи.

Если нужно проверить, есть ли в таблице запись с нужным значением ключа (но сама запись не интересует), пишем так

SetLL (KeyValue) MyIdx;
if %equal(MyIdx);
  // запись существет
else;
  // Такой записи нет
endif;

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

И там таких моментов очень много. Мелочь, но на скорость на больших объемах влияет ощутимо.

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

Есть две таблицы - "витрина элементов адресов субъектов" - таблица где содержится Id адреса, Id элемента и его порядковый номер в строке. И таблица где содержатся сами элементы - Id элемента и сам элемент (слово).

Аналогичная витрина есть для адресов клиентов.

Нужно найти соответствия - когда все уникальные элементы адреса субъекта входят в адрес клиента.

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

Длиннее по коду, но эффективнее:

  • Составляем список уникальных элементов адреса субъекта

  • Берем первый элемент списка, находим его Id в таблице элементов адресов клиентов

  • По этому Id находим Id всех адресов клиентов, содержащих этот элемент и заносим их в массив

  • Дальше цикл - берем очередной элемент адреса субъекта, находим его Id в элементах адресов клиентов и потом проходим по нашему массиву, проверяя наличие связки Id адреса (из массива) и Id элемента в индексе (без чтения самой записи, нам важно только есть оно или нет). Если есть - адрес "проходит в следующий тур", если нет - удаляется из массива (отсеивается).

  • С каждым элементом длина массива сокращается и после последнего там остаются только те Id, которые соответствуют адресам клиентов куда вошли все элементы проверяемого адреса субъекта.

Это работает в несколько раз быстрее, чем скулевый запрос "в лоб".

Итого

  • Чтение элементов адреса субъекта (3-5-7 записей)

  • На первом "обороте" цикла заполнение массива - чтение всех записей, содержащих первый элемент (там много - может быть несколько сотен тысяч)

  • На всех оборотах цикла чтение одной записи по уникальному ключу - получение Id очередного элемента адреса субъекта в таблице элементов адресов клиентов.

  • Все остальное - без чтения записей, только проверка наличия значений в индексе.

Никаких группировок и агрегирования скулем (это достаточно "дорогие" операции). Однократное заполнение массива с последующим его просеиванием.

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

Итого - на промсреде уже не 7-10, а 1-2 часа работает.

Вместо SetGT используем SetLL - он ставит указатель перед первой записью с указанным значением ключа.

Перед записью или на записи?

SetLL (KeyValue) MyIdx; if %equal(MyIdx);

И вылетаем на пустом ответе?

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

Если select только полей из индекса в SQL, можно тоже не тратить время на чтение записи. Есть такая оптимизация. DB2 её не умеет?

Длиннее по коду, но эффективнее:

Реализуемо в большинстве "взрослых" движков SQL как хранимая процедура. И с перебором курсора, и с промежуточными представлениями результатов выборки.

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

а если на С++ написать функцию которая это делает, то окажется можно сделать за одну строку:

make_what_we_need();

люди как будто не понимают, что этот дсл надо ещё реализовать

А если он уже реализован? И развивается постоянно?

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

Если работаете с базой - есть более эффективные и удобные инструменты.

А если например пишите свою базу - есть С++.

Для каждой части проекта - свой язык подходящий под задачу. Нет ни одной причины писать все на одном языке. Кроме пишу на чем знаю. Все равно, раз зашёл разговор про базу - уже 2 языка. Сделайте следующий логический шаг и пишите разные части проекта на подходящих языках. К тому же большинство языков схожи. Переход/изучение нового много времени не занимает. Кроме всякой экзотики (одна из весомых причин почему и экзотика кстати).

Язык я-ля DSL это хорошо, но с ним есть проблема. Кто на нем будет писать. Если он нишевый, но более-менее распространенный, типа того же RPG или ABAP/4, то в целом людей можно найти. Если же это совсем узкая штука конкретной конторы, то писать на нем прикладуху мало кому интересно, просто в силу того, что за пределами конторы это никому не нужно. То есть свой DSL лучше делать, чтобы могли на нем писать кто-то вроде аналитиков.

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

Совсем не обязательно писать свой именно отдельный язык. Есть же пример Lisp/Scheme, где и языка как такового нет и ты сразу пишешь просто в AST.
Ну так AST можно описать чем угодно - хоть в YAML, JSON или и вовсе на С/C++ макросах, которые будут убирать лишнее под капот.

К примеру вот пример C/C++ кода. Ниже FOR(), SELECT(), FROM(), WHERE(), IS_NOT_LIKE() - это макросы.

int main() {
  FOR(record, 
    SELECT(INDEX_DESC(persons_name)) 
    FROM(persons)
    WHERE(IS_NOT_LIKE(persons.name, "Сидоров%") && (persons.age > 40))
  ) {
    printf("%s %d\n", record.name, record.age);
  }  
  return 0;
}

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

Только не надо говорить что макросы это зло - вы просто не умеете их готовить (с)

P.S. А еще M4 существует :)

К примеру вот пример C/C++ кода. Ниже FOR(), SELECT(), FROM(), WHERE(), IS_NOT_LIKE() - это макросы.

Это eDSL. ;-)

Только не надо говорить что макросы это зло - вы просто не умеете их готовить (с)

  1. Насколько хороши здесь будут сообщения об ошибках, если аналитик случайно напишет `persons.age > "4O"`? Или, например, если аналитик случайно забудет в insert обязательные для заполнения поля?

  2. Насколько легко здесь будет реализовать «межмакросную» зависимость — ну там, про агрегатные функции и group by, скажем?

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

Ну наконец-то :-)

Да, речь именно об RPG. У нас в стране как минимум три банка (Альфа, Райф и Росбанк) его используют. Плюс как минимум три (которых я знаю) компании - BTC, Cinimex и РМС-Лаб предлагают услуги по разработке на нем. На SO есть теги #rpg (старые диалекты) и #rpgle (современный диалект). В LinkedIn есть группы. Есть форум на Code400, есть блоги - Nick Litten, Scott Klement, Simon Hutchinson (RPGPGM). Есть IT Jungle, MySampleCode, WisdomJobs... Т.е. вполне устойчивое сообщество где можно и поделиться чем-то и спросить.

Так а чем вас так STL не устраивает? Да и "стандарт"?

Вот было круто то, когда был borland C++, embarcadero C++, и очередной закрытый компилятор от странной конторы которая не понятно вообще будет ли жива через 2 года.

Ну и на самом деле никто не мешает вам не линковаться с -lstdc++ и жить себе прекрасно на рукописных примитивах с -fno-exceptions в счастливом мире си с классами.

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

да хоть на JSON

Это как?

Ну например вот функция сложения двух чисел

{"type": "function", "name": "adder", "args": [["x", "int"], ["y", "int]], "result_type": "int",
 "body": [
   {"st": "return", "expr": ["+", "x", "y"]
   }
]}

Такой себе AST в виде списков и деревьев, только на JSON.

В реальном, конечно, будет много отличий (например вместо "x" в выражении, скорее всего, будет что-то вроде {"local":"x"}), но внешне будет достаточно похоже.

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

Если вы про RPG, то это не язык, заточенный под задачи "работы с БД и коммерческие расчеты", а платформа мейнфреймов ΙΒΜ заточенна под глубокую интеграцию с этим прориетарным языком(вроде возможности прямого чтения данных из бд). Шаг в сторону от ΙΒΜ мейнфреймов и этот язык теряет всякий смысл в отличии языков общего назначения типа С/С++/Java и т.д.

И да и нет.

IBM i - это не мейнфреймы. Мейнфреймы - IBM z. i - это middleware. Изначально позиционировались как сервера для малого и среднего бизнеса. Но получилось настолько мощно и масштабируемо, что успешно используется и в крупном

Да, RPG интегрирован в систему. Но тут все интегрировано. БД (DB2) тоже часть системы. Компиляторы (CL, COBOL, C, C++, RPG) - часть системы. Все это получаешь сразу из коробки как единое целое.

При этом есть реализации и для других платформ. Тот же ASNA VisualRPG (в т.ч. и для .NET)

В С/С++ тоже есть поддержка decimal (правда, только ее, нет ни varchar, ни numeric, ни date, ни time, ни timestamp). И есть библиотека для прямой работы с БД - RECIO. Правда, в чисто "Сишном" стиле - куча опций, параметров, 90% которых в обычной работе нафиг не нужны.

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

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

Лично я до банка совсем другими задачами занимался. И 25+ лет писал на С/С++. И сейчас иногда пишу когда это оправданно задачей. Но вот столкнувши с RPG быстро понял что раз он тут есть, то использовать его для решения банковских задач эффективнее как с точки зрения разработки, так и с точки зрения конечного результата.

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

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

Замечательно. Осталось понять при чем здесь C++.

Ведь из ваших слов логичным образом вытекает, что в ваших условиях и на ваших задачах RPG будут сливать и Java (со Scala-ми и Kotlin-ами), и C# с F# впридачу, и другие высокоуровневые универсальные языки (ну, может быть, за исключением COBOL-а).

Но "на помоечку" (с) отправить нужно именно C++.

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

С/С++ тут только при том, что здесь он тоже есть и есть возможность сравнить решение одной и той же задачи на них или на RPG. По затратам времени на разработку и по эффективности полученного результата (объективно, через снятие PEX статистик на одних и тех же данных).

С COBOL вопрос открытый. С одной стороны, он также специализирован для этого же класса задач, с другой - он не развивается давно (а IBM его никогда и не развивала, им MicroFocus занимался). И в нет много того, что сейчас появилось и появляется в RPG. Чисто на уровне "синтаксического сахара".

Хотя, если стоит задача портирования, то COBOL будет лучшим решением т.к. его можно перенести на другие платформы (тот же GNU COBOL).

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

Тогда зачем продолжать полоскать C++?

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

Речь не о чувствах, а о том, чтобы картина была объективна.

Ваше восхваление RPG можно было бы понять, если бы вы пришли на платформу IBM i и начали решать свои специфические задачи на C++, долго от этого страдали, пока кто-то не раскрыл вам глаза на существование RPG. После чего мир заиграл новыми красками, а жизнь вновь стала прекрасна и удивительна.

Но, как я понимаю, было не совсем так. На этой платформе уже был инструмент, который заруливал и C++, и Java, и C#. Было бы странно отказываться от него. Но раз инструмент заточен и под платформу, и под задачу, то какой смысл все время противопоставлять его C++?

Вот кто-то бы начал сравнивать C++ с регулярными выражениями, уместно бы было такое сравнение?

Да, на C++ вы могли бы закатывать Солнце вручную выписывая что-то вроде:

auto exp = capture(repetition(2, 8, range('0', '9')));

вместо ([0-9]{2,8})

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

Ваше восхваление RPG можно было бы понять, если бы вы пришли на платформу IBM i и начали решать свои специфические задачи на C++, долго от этого страдали, пока кто-то не раскрыл вам глаза на существование RPG. После чего мир заиграл новыми красками, а жизнь вновь стала прекрасна и удивительна.

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

И да. Когда я пришел сюда 6+ лет назад, я имел неплохой опыт в С/С++ (25+ лет, значительная часть которых была связана с промавтоматизацией и той или иной степенью реалтайма - т.е. писать быстрый и эффективный код я умел и шишек на том понабил изрядно).

И да. Мне привычнее было бы делать все на С/С++. И я пробовал это делать на С/С++. Но быстро пришел к тому, что это не самый эффективный путь в данных конкретных условиях. И тут даже ООП не очень хорошо ложится т.к. работа всегда крутится не вокруг взаимодействия объектов, но вокруг потока данных, который в каждой задаче кардинально разный. Составить иерархию абстракций конечного размера не представляется возможным (хотя попытки были).

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

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

Если успокоит - могу сказать, что окажись в другом месте, где эффективно использование других языков (Golang, Rust. Haskel...) я бы делал точно такой же выбор по тем же критериям - сочетание трудозатрат на разработку с эффективностью и производительностью конечного результата. И если С/С++ даст лучшее сочетание - выбрал бы их. У меня нет предубеждений по поводу конкретного инструмента. В принципе нет.

На заре деятельности я точно также выбирал между Паскалем и С. Потом был момент выбора между Clipper и Clarion. В какой-то момент даже использовал dbVista (ныне RDM - Raima Data Manager) т.к. ее сетевая модель БД более подходила под конкретную задачу, нежели реляционная.

И да. Мне привычнее было бы делать все на С/С++. И я пробовал это делать на С/С++.

Ну понятно. Именно C++ плох потому, что вы пришли из C++. Пришли бы из Java, то плохой по сравнению с RPG была бы Java. Но т.к. в вашем багаже Java (или C#) не было, то плох именно C++.

Где я говорил что С++ плох? Вот прям чтобы вот так вот. Я говорил только то, что для задач, связанных с реализацией бизнес-логики на этой платформе есть более удобные инструменты.

Везде писал что RPG позволяет данные конкретные задачи решать проще и эффективнее. При этом, на нем можно сделать все, что можно сделать на С, но если нужно что-то системное, то это будет достаточно неудобно. Можно, но на С или С++ это будет проще и изящнее.

Скажем, я могу на RPG написать движок для распараллеливания обработки больших объемов данных или тот же USRQ API, но это займет больше времени и будет "неуклюже" по сравнению с С/С++.

С C# сравнивать не могу т.к. не сталкивался с ним во-первых, на этой платформе его нет (и нет возможности одну и ту же задачу решить разными инструментами и сравнить результат) во-вторых.

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

Да я вам уже устал повторять: на вашей платформе RPG для ваших задач рвет чуть ли не всех. Но вы упорно говорите про то, что именно C++ не лучший выбор. Хотя вместо C++ можно подставить любой современный мейнстримный язык.

Не подставляете же вы именно потому, что в бэкграунде у вас C++. Была бы Java, вы бы точно так же говорили про Java.

Если и сейчас до вас не дойдет...

RPG "рвет всех" только в конкретном классе задач. Это узкозаточенный инструмент.

С++ - более универсальный инструмент широкого профиля. И в тех задачах, которые не связаны с бизнес-логикой на этой же платформе (а такие тут тоже есть) он кратно лучше RPG.

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

И, честно говоря, не совсем понимаю вашу позицию - вы что пытаетесь доказать? И кому?

И, честно говоря, не совсем понимаю вашу позицию - вы что пытаетесь доказать?

Я пытаюсь доказать, что когда вы сравниваете RPG с C++ в своих задачах, то вы напрасно акцентируетесь на C++. Т.к. на место C++ в этом сравнении можно подставить хоть Java, хоть C#, хоть Kotlin, хоть Scala. Ничего не поменяется.

И кому?

Вам.

Я сравниваю сравнимое. То, что можно взять и запустить параллельно в двух терминальных сессиях и посмотреть разницу (по времени выполнения, PEX статистикам). Сравнить время на реализацию одной и той же задачи.

И неоднократно говорил, что ряд задач я делаю на С/С++ потому что это быстрее и удобнее. Но это системные задачи. Обычно требующие плотной работы с системными объектами (системные указатели, MI), работы в TERASPACE модели памяти с 64бит указателями и т.п. Для всего этого на этой платформе нет альтернативы С/С++.

Я сравниваю сравнимое.

Да я тоже могу на C++ сделать DSL для описания регулярного выражения, а потом сравнить его результат с выхлопом, скажем, re2c. Или сделать на C++ вручную bottom up LR(1) парсер и сравню его с bison-ом.

Не, ну а чё? Сравнимое же.

А потом еще и буду говорить, но вот когда нужно делать обработку 2D изображений, тогда, конечно же, С++ круче, чем re2c и bison вместе взятых.

Получится как у вас с вашим любимым IBM i.

Я могу понять, когда люди сравнивают C++ и, скажем, Java для реализации проектов типа NetBeans или Eclipse. Когда компонентная архитектура, плагины от разных разработчиков. И хочется, чтобы все это работало и быстро, и надежно.

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

Просто сейчас, так сложилось, у меня 80% задач требуют вполне определенного инструмента. Но это не значит что я буду натягивать сову на глобус и использовать тот же инструмент под оставшиеся 20%.

Ну так о чем и речь.

Тогда повторю свой вопрос: а при чем здесь C++?

Зачем вы именно C++ противопоставляете "наиболее эффективному инструменту под задачу"?

В переводе на русский, я так понимаю, "зачем именно под статьёй про C++, где обсуждается только C++ и всё, что с ним связано, влезать со своим инструментом", верно? Просто, ну, это можно было бы и прямо сказать, со стороны совсем не очевидно, что эта статья только про C++.

Как бы да, и я стараюсь об этом прямо сказать.

Но, позволю себе немного растечься мыслею. Сама статья весьма специфична и в ней речь идет о (кмк) сравнительно узкой сфере задач, решаемых на C++. Тем не менее, она актуальна в том плане, что автор статьи рассказывает о вещах, которые появились a) недавно (так мне показалось) и b) как способ преодолеть ограничения, достигнутые при применении универсальных языков (C++ в частности). Т.е. сперва задачи из предметной области автора статьи решались на C++ (и конкурентов практически не было), но времена меняются и сейчас вне C++ можно достичь результатов лучше, чем на C++.

Здесь все OK. С поправкой на то, что не нужно специфику узкой области автора статьи экстраполировать на вообще весь C++ и сферы его применения (что, кмк, происходит в комментариях, автор статьи такой попытки и не делал).

Но вот появляется персонаж, который в очередной раз (ну вот реально он не впервые прибегает с RPG в комментарии к статьям про C++) говорит: "А вот RPG в моих задачах рвет C++ как тузик грелку". И это не имеет отношения к поднятой в статье теме просто потому, что RPG специально затачивался под такие задачи, и рвет он не только C++, но и вообще все за исключением COBOL-а (с той же самой платформы).

Мало того, что данный персонаж со своим RPG уже поднадоел, так еще и непонятно какие выводы из его историй стоит сделать (и это принципиально отличает обсуждаемую статью). Ближайшая аналогия: если вам нужны регулярки, то используйте готовые regex-а, а не костыльте их самостоятельно. Ну как бы да, спасибо, Кэп.

Если внимательно почитать статью, то

 Я по-прежнему писал в основном на C++, но то и дело ко мне обращались с вопросом: «А хочешь вот эту задачу, она не на C++?» И я отвечал: «Давай!» А затем брался за задачу, какова бы она ни была.

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

Дальше идет перечисление инструментов, которые к каких-то узких областях лучше С++

И вывод:

Если мне придётся променять  C++ на «не C++», то 80% моей работы никак не изменится. Язык C++ уже просто нерелевантен для большинства моих задач. Могу ли я в таком случае утверждать, что для меня C++ уже на 80% мёртв?

Вот об этом мои комментарии. И самый первый вроде как особых возражений не вызвал. Потому что на 100% соответствует последнему абзацу статьи.

Да, но речь в посте же идет про задачи общего назначения, не привязанные к вендору и экосистеме(как RPG и IBM). К примеру, амазон может сделать специальную интеграцию Visual Basic для своего api, что позволит писать на нем под AWS быстрее и проще чем на любом другом языке, но это не сделает Visual Basic в общем случае удобнее и производительней чем С++

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

Вы читаете мои мысли!)

C++ умирает именно потому, что современное программирование - оно вообще не про написание кода, а про его чтение и компоновку из готовых кусков.

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

Соответственно, рулит не синтаксический сахар, синтаксис сейчас не значит вообще ничего.

Непременные признаки хорошего современного языка общего назначения:

  • Удобный тулчейн, чтобы сборка проекта, стянутого по git clone, осуществлялась одной командой и не падала хотя бы в 95% случаев, а при падении выдавала внятное сообщение об ошибке.

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

  • Богатая стандартная библиотека.

У C++ нет ни первого, ни второго, ни третьего. Какое будущее может быть у языка, в котором написать программу заново может быть проще, чем заставить скачанное с github'а просто собраться на вашей машине? Современный программист не будет читать пять страниц инструкции по сборке, которая подразумевает ручную установку зависимостей и ручную же синхронизацию пачки скриптов, использующих make, bash, perl, python и node.js одновременно.

Что за язык, у которого в стандартной библиотеке до сих пор нет нормальных средств работы с сетью? И это когда WiFi уже даже в микроконтроллеры встроен, а весь софт давно ушёл в облака.

C++ умирает именно потому, что современное программирование - оно вообще не про написание кода, а про его чтение и компоновку из готовых кусков.

Да С++ уже 30+ лет как умирает и умереть не может. В сферах где нужен перформанс (и часто компоновка из кусков не работает, потому что кусков то и нет, так как проприетарное железо/операционки), а С слишком голый, альтернатив по прежнему не особо.

На плюсах писал лет 8. Пришёл к ощущению, что что-то совсем низкоуровневое лучше на "голых" сях слабать, а если что посложнее, особенно с зависимостями - тулинг незаменим, так что какой-нибудь Rust или Python, да хоть Ruby.

Кажется, что на C++ пишут приложения, которые с одной стороны будут живы и через 15-20-30 лет, с другой - поддерживать их будут плюс-минус те же команды. В современном климате таких не настолько много: или делаем на века и для всех (как ядро Linux), или цикл разработки занимает несколько лет: зашли на рынок, привлекли денег, выросли, выкинули старый код и переписали заново.

Кажется, что на C++ пишут приложения, которые с одной стороны будут живы и через 15-20-30 лет, с другой - поддерживать их будут плюс-минус те же команды.

Весь серьезный геймдев, всякие про приложения типа фотошопа, 3д макса и т.д.

что-то совсем низкоуровневое лучше на "голых" сях слабать

Я вроде и согласен, но бывает что людям хочется чтобы С, но были классы, или темплейты, или что еще. Проще в таком случае взять С++ и кастрировать (а давайте напишем на плюсах, но без RTTI/исключений/смартпоинтеров/у_кого_на_что_фантазии_хватит), что собственно часто и происходит.

Весь серьезный геймдев

Геймдев пишет на том, начем написан лучший движок по сочетаюнию цена/качество. Когда был бум мобильных игр, геймдев писал на Unity/C#.

 всякие про приложения типа фотошопа, 3д макса и т.д.

Blender на чем написан?

Blender на чем написан?

80% на C++. Тулзы и аддоны на Python. Его рендер (cycles) на C и С++.

Если я ничего не путаю, в Maya расширения тоже пишутся на Python, когда она сама написана C++.

И Godot на C++ со своей скрипт машиной. Теперь все будут писать на gdscript, спускаясь в плюсы по желанию

Геймдев пишет на том, начем написан лучший движок по сочетаюнию цена/качество.

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

Сам юнити так же написан на С++.

Нет, Unity написана C#, в отличии от 99% движков, которые, да, написаны на C++.

Нет, "Unity itself is written in C++".

Там же: "The code that has to run super-fast like the physics and animation, all that  is C++" (David Helgason, the CEO and co-founder of Unity Technologies).

C# это скриптовый язык юнити и, если правильно помню, редактор написан на шарпе, сам кор движка/рендер написан на плюсах

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

Проще в таком случае взять С++ и кастрировать

То так, мне кажется, что многие конторы так и работают с C++. Но управлять процессом сложно; в плюсах за годы появилось много всего хорошего, конечно, но нет какого-то... Ясного видения, что ли. Стандарты выглядят как свалка. "Фичи? Их есть у нас!". То же самое начинает проникать и в код - или у тимлида светлая голова и готовность следить за внутренними стандартами, или "мы пишем без темплейтов кроме вон тех трёх мест, где лучше не придумали". Иногда лучше меньше, да лучше.

Мой аргумент, пожалуй, в том, что "пишем на кастрированном C++" - в некой мере следствие занятой плюсами доли рынка. То бишь уже есть люди, умеющие писать на C++, и дешевле занять их, чем переучивать или находить кого-то, умеющего в идеально подходящий с точки зрения технологии стек. От такого язык не умрёт быстро, но на спад, пожалуй, идёт. Так-то и фортран с коболом "живы".

Если нужен перформанс, то делается свой собственный язык и пишется компилятор для него на чём угодно. C++ в таком случае не подходит, потому что стандарт оставляет слишком много вещей как undefined behavior (лайфтаймы, алиасинг, всё такое), которые надо обкостыливать для соответствия стандарту и надеяться, что оптимизатор поймёт, что вы имели в виду.

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

Не более бред, чем использовать для топового перформанса язык, который топовый перформанс не даёт (и это я ещё интринсики не вспоминал) и на котором невозможно писать надёжный код (ведь C in CVE stands for C/C++), даже если обложиться санитайзерами, линтерами и программистами старой школы, пишущими ядро.

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

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

C++ во всех таких местах используется (если используется) исключительно из-за соображений легаси и доступного пула программистов. Ну, «программист», могущий выдать что-то криво-косо как-то работающее на плюсах, таки чуть более распространён, чем человек, способный написать DSL, а что там завтра будет или через 10 лет с поддержкой — да хоть трава не расти.

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

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

Но согласитесь, очень уж печальные у вас примеры: по-сути это просто высокообразованные высокотехнологичные спекулянты

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

живущие в городах-помойках.

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

Ну это вопрос философский, особенно считать ли спекулянтов чем-то плохим.

Всё упирается в философский вопрос - "нужны ли мы нам?" :-) Если вас интересует существование человечества, развитие индустриальной цивилизации, то спекулянты безусловно плохи.

Это особенно печально, но удалёнка нас спасёт.

Очень приятно осознавать, но удалёнки у спекулянтов нет. :-) Так что если хочется на них работать, придётся жить в помойке. Карма.

Да и можно заработать спекуляциями многоденег, переехать в город подешевле и поприятнее

Для этого надо быть спекулянтом, а не работать на них.

Всё упирается в философский вопрос - "нужны ли мы нам?" :-) Если вас интересует существование человечества, развитие индустриальной цивилизации, то спекулянты безусловно плохи.

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

Более того, есть разные интересные эффекты следующего порядка малости. Например, тот же HFT — это вообще-то локально игра с нулевой суммой, поэтому туда идёт весьма определённый тип людей с определённой толерантностью к риску. Ты уверен, что хочешь, чтобы эти люди иначе пошли бы, я не знаю, писать софт для кардиостимуляторов или ракет, особенно учитывая, что писать софт для кардиостимуляторов или ракет бесконечно скучно на фоне инфры для трейдинга или язычка для описания правил выдачи кредитов с Хиндли-Милнером и row polymorphism'ом по всем последним папирам с POPL?

А так и волки сыты, и овцы целы, и ликвидность генерируется без попыток ответа на не имеющие ответа вопросы вроде «в какой момент инвестирование перестаёт быть инвестированием и начинает быть спекуляциями в плохом смысле?»

Для этого надо быть спекулянтом, а не работать на них.

Чувак, пишущий инфру для трейдеров (или те вот всякие DSL) — он уже спекулянт или ещё нет?

Очень приятно осознавать, но удалёнки у спекулянтов нет.

В зависимости ответа на предыдущий вопрос она может быть :] Мне не так давно предлагали удалёнку у весьма известных спекулянтов, и там в офисе надо быть что-то вроде около одного дня в неделю, а в некоторых командах — раз в месяц. Можно жить в паре часов езды от ближайшего города и выбираться в офис раз в неделю по какому-нибудь там 183-му шоссе ;]

Чувак, пишущий инфру для трейдеров (или те вот всякие DSL) — он уже спекулянт или ещё нет?

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

и там в офисе надо быть что-то вроде около одного дня в неделю

Это и есть нет удалёнки.

Главное, что он бессмысленно прожигает свою жизнь.

Ну может быть исключение - если он разработает, например, новый lock-free wait-free gluten-free алгоритм или структуру данных :) чтобы сэкономить 5нс заказчику, и опубликует это. А иначе прогресс в таких областях не очень-то и движется.

На ICFP последней реально было крайне печально смотреть на применения «высокого программирования»: либо пузырь AI, либо спекуляции, либо геймдев (кмк, мэтры очень стеснялись участия в этом геймдеве, но может быть и зря — затея хотя бы отчасти благородная: дать передний край детям). Одна радость — у Tarides был стенд про космос (они запустили Mirage на одном из индийских спутников в качестве нагрузки).

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

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

Бред, но ещё это может быть HFT.

Как это согласуется с

В мире настолько много опытных программистов, как никогда ранее в истории.

Не особо-то они, значит, и опытные, а так, натасканные на несколько шаблонов.

Я правильно понял что языком общего назначения вы назвали Golang? :)

Не смотря на то, что эта критика абсолютно валидна - мы это решили при помощи vcpkg, где Cmake сначала скачивает все зависимости (залоченные), а потом собирает.

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

" У C++ нет ни первого, ни второго, ни третьего " - ололо. поколение nuevo гордится тем что не слышало про make ?

Make это отрыжка из 70ых.

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

я бы воспользовался кросс компилированием)

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

до сих пор нет нормальных средств работы с сетью

Работу с файлами дождались - и сети дождёмся. И зачем-то 2D графику...

" рулит не синтаксический сахар, синтаксис сейчас не значит вообще ничего " тот в C# каждый год новый сахар завозят - от ненужности )) там есть сомнительные вещи, но они больше для "когда очень нужно"

думаю у c++ сообщество все это есть.

Вопрос только в том, почему люди этим не пользуются?

  1. Есть тот же conan, vcpkg. Но хорошие либы и без них собираются одной командой - make или cmake.

  2. conan, vcpkg

  3. boost

Вообщем-то

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

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

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

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

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

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

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

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

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

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

В этом принципиальна разница.

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

Как же "быть не может", если новые модели самолётов выпускают в эксплуатацию только после экспериментальных полётов с лётчиками-испытателями и последующим анализом всей телеметрии.

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

Давайте остановимся на общем выводе что краш-тесты тоже имеют право на жизнь :) .

Крэш-тесты устраивают и самолётам. Разумеется, какому-нибудь 737MAX - вряд ли, хотя, при минимуме набивки только на голой обшивке - возможно, и делали (ну, с макетами набивки). А средствам подешевле и помассовее - тем более.

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

Давайте остановимся на общем выводе что краш-тесты тоже имеют право на жизнь :)

Безусловно :)

падение на землю самолета там вроде как не допустимый результат проверки

От отсутствия системы предупреждения самолёт сам по себе не упадёт. Это именно предупреждение для уставшего/отвлёкшегося пилота, что "кажется что-то пошло не так", и надо срочно что-то делать.

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

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

Бизнес часто попадает в ловушку, когда первые 10% решения действительно пишутся в 4 раза быстрее, а вот последние 10% из-за динамического типизирования встают в такую копеечку! См. тесты, объём которых больше программы. :-)

Так что "я сын С++ника, тут не всё так однозначно". :-)

" См. тесты, объём которых больше программы " а как размер тестов зависит от типизации?

Например, можно выразить часть гарантий в типах

Антипропорционально зависит.

И то, и другое — это средство проверки гарантий, если брать аналогии с интегрированием, то тесты — это Монте-Карло, а статическая типизация — автоматизированные аналитические вычисления (Mathcad/Mapple/Mathematica/Maxima).

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

Бизнес про это в курсе, поэтому действительно популярных языков с ДП сейчас осталось примерно три, причём во все типизацию тем или иным способом завезли.

причём во все типизацию тем или иным способом завезли.

«Органично совмещая недостатки обоих подходов» ;-)

Зачем вообще программировать на C++, если достаточно дать высокоуровневое описание алгоритма — и инструмент сделает вам код, в два раза обгоняющий код на C++?

тут есть один простой вопрос: на чем написан инструмент, который сделает вам код, в два раза обгоняющий код на C++, хотя бы частично?

Я боюсь что тут вариантов никогда не будет: С++, С, Ассемблер.

Они бессмертны, пока есть процессоры система команд которых это и есть ассемблер.

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

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

Если говорить про возможности чистого C:

Чистый C позволяет писать программы без динамической памяти. И в ряде случаев это хорошо. Однако чистому C требуемся стэк (и, возможно, немного локальной памяти). Без стэка C не может. С другой стороны: много ли языков могут работать в таких условиях?

И если пойти чуть дальше:

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

К чему это? А, да: сомнительно, что таких подобных языков (заменителей C, Asm) много. Вот честно: очень сомнительно. Приведёте парочку примеров?

FPC, например

Ну так Turbo Pascal, на систаксисе которого обосновался Free Pascal он весьма древний, как-бы не древнее ANSI C++....

С другой стороны: много ли языков могут работать в таких условиях?

Про Паскаль уже сказали. Естественно, его продолжения в виде Modula и Ada. Fortran.

Всем названным нужно отрубить стандартную библиотеку, ну так и для C это тоже надо (freestanding сборка с нужными свойствами).

Среди этого комплекта мы в основном помним C только потому, что в нём соответствующие действия выражаются максимально прямо.

правда, иногда подставляя кэш процессора как квази-память для создания ощущения стэка

Это ортогонально использованию стека. Если говорить про старт x86 в до-ME реализациях, то там псевдо-RAM на стеке использовалась и для данных (типа результатов итерирования устройств на шинах), а до получения такой псевдопамяти стек эмулировался адресами возврата прямо в коде.

А, да: сомнительно, что таких подобных языков (заменителей C, Asm) много.

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

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

Это вы рассматриваете только легальные (белые) вакансии. Тогда как для ассемблерщиков самые доходные вакансии связаны не с написанием кода, а с реверсинженирингом всего подряд - от потрошения прошивок микроконтроллеров до слома коммерческого софта.

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

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

Или вы про читоделов?

Читал, что не хватает с обратной стороны - античитеров в команде разработчиков онлайновых игр, которые реверсили бы читы.

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

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

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

за 25 лет я что-то не видел ни одной вакансии на Ассемблер. Хотя как раз 25 лет назад меня взяли на работу в очень известную фирму именно потому что я был единственный кто знал Ассемблер, как потом выяснилось, но даже в описании той вакансии Ассемблер не упоминался.

А как же программирование микроконтроллеров?

esp8266 гоняет с 80кб общей оперативки и 4мя мегабайтами места под программу (она вообще flash, можно барахло свое хранить). Ассемблер там не то чтобы нужен.

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

Компиляторы теперь частенько состоят из двух частей. Первая переводит из языка в универсальный код, а вторая компилирует универсальный код под конкретную архитектуру. Это позволяет отделить оптимизацию под архитектуру от языка в разные проекты. Первая часть у любого приличного языка написана на нём самом. Даже у Python есть такой вариант. Вторая часть - это обычно LLVM, написанный на С. Но есть и Cranelift написанный на Rust.

Так что как минимум Rust можно целиком компилировать на нём самом. Другое дело, что так никто не делает, потому что никто кроме вас и ещё пары человек, не видит в этом ценности. Когда Rust начнёт вытеснять C, просто LLVM перепишут на Rust и дальше двинутся. Да уверен, кто-нибудь уже начал.

никто кроме вас и ещё пары человек, не видит в этом ценности

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

</зануда>

Не, вы не въехали :) Речь о том, чтобы компилятор/среда языка был целиком написан на нём самом, без вставок на С. Я не думаю, что отсутствие такого софта говорит, что язык плох, или жить не может без С. Припрёт - перепишут, а сейчас зачем, если есть и С и кодыри на нём?

Про это как раз в статье написано:

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

Появляются новые оптимизации в LLVM - и сразу появляются во всех языках, которые используют его.

Не согласен. Оптимизации и решения до уровня LLVM гораздо более важны для производительности. Из банального: один язык при любом обращении к массиву проводит проверку выхода за пределы n < arr.length, второй тупо берёт *arr[0] + n*sizeof(arr[0]), а Rust вообще имеет интеллектуальную выбиралку, проверять или нет. LLVM такие штуки не решает, столь сложные вещи ему даны в ощущениях.

Проведите эксперимент - возьмите C++ код, скомпилируйте в LLVM IR опциями типа `-S -emit-llvm`, напустите ll-opt на результат и затем в бинарь. С разными уровнями оптимизации у clang и у ll-opt. Увидите, что если clang получил -O0, то результат исправить потом практически нереально, будет куча лишних операций.

Такой инструмент можно написать на чём угодно. Я, собственно, такой инструмент и писал на хаскеле, который компилировал некоторый предметноспецифичный язык сразу в ассемблер, с учётом специфики предметной области. Получалось быстрее, чем если записать тот же алгоритм на C++. И инструмент такой на хаскеле тоже сделать быстрее, чем на C++, потому что можно больше времени потратить на высокоуровневые оптимизации и распорядиться ограничениями (а они есть не только у железа, но и у вашего времени) эффективнее.

Тоже используем Haskell для этого, только генерим С99 =)

тут вариантов никогда не будет: С++, С, Ассемблер.

А как же Forth?

А как же Forth?

Среда Forth со старта уже требует RAM и двух стеков в нём (данных и возвратов) и стандартной библиотеки.

А как же Forth?

не знаю такого, забыл наверно. У него есть бесплатная среда разработки, компилятор?

Начните с GNU Forth. Да, есть. (Но условиям он не соответствует)

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

Компиляторы большинства компилируемых языков написаны на них самих.

Да инструмент можно сделать на любом языке, собственно. На той же Java или на Python — на чём-то кроссплатформенном. А потом можно написать инструмент-компилятор на новом языке, который будет компилироваться инструментом-компилятором предыдущей версии.

Мы же теперь живём в XXI веке. В мире настолько много опытных программистов, как никогда ранее в истории. И ещё нам сейчас более чем когда-либо ранее необходим эффективный софт.

В XX веке всё было проще. У вас есть идея, вы обёртываете её в некий пользовательский интерфейс и продаёте как продукт для ПК. Программа тормозит? Какие проблемы? Всего через полтора года компьютеры станут вдвое быстрее. Главное войти на рынок, начать продажу фич, предпочтительно — чтобы они работали без багов.

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

Но вот почитать про языки, предназначеные для повышения эффективности выполнения - это таки да, интересно. Так что если рассматривать статью исключительно с этой стороны, то она IMHO таки достойна перевода.

Тем не менее, считаю, что писать на C++ — плохая привычка. Этот язык небезопасен, не так эффективен, как считается, при работе с ним программисту приходится жутко ломать голову над вещами, которые никак не связаны с созданием ПО. А вы знаете, что в MSVC uint16_t(50000) * uin16_t(50000) == -1794967296? А знаете, почему? Да, вот такие мысли меня занимали.

Доктор, когда я так делаю. Мне больно.

Так не делайте так.

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

uint16_t(50000) * uin16_t(50000)

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

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

если ваш конпилятор Сам Без Спросу преобразует Unsigned в Signed то это уже диагноз к выбиральщику конпилятора.

Даже если делать так?

uint64_t a = uint16_t(50000) * uin16_t(50000);

Да.

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

Справедливости ради, в новых стандартах integer promotion никуда не девается. Когда с вычислениями на C или C++ имеешь дело регулярно, это все учитываешь автоматически, "жутко ломать голову" отнюдь не требуется. Впрочем, тут выше пишут, что "современный погромист", дескать, читать мануалы не привык, а привык собирать все из готовых кусочков (и то с переменным успехом, как в известной сцене из фильма "Идиократия", где главный герой наблюдает за поведением соседей по тесту на уровень интеллекта), зато вот получать хочет все больше. Верной дорогой, как говорится.

Безотносительно того, что пишут выше, современный программист может быть специалистом в своей предметной области (от бизнес-процессов в компании до какого-нибудь машинного обучения или финансовых стратегий в трейдинге), и требовать от него заодно знать все особенности integer promotion, лайфтаймов в юнионах, алиасинга для char* и работы со строками, и автоматически учитывать, что «вот тут мы пишем на C++17, поэтому mmap точно лайфтайм не начинает, и мне надо поприседать, а вот тут мы пишем на C++20, где некоторые функции лайфтайм начинают неявно, и… пажжи, а является ли это implementation-defined и что там моя implementation пишет про конкретный mmap?» — ну, вы, конечно, можете, но это сильно уменьшает пул доступных специалистов, либо требует иметь двоих разных человек на двух разных позициях. А это снова увеличивает стоимость разработки и увеличивает сроки (потому что между этими людьми должна быть коммуникация с её оверхедом, и если «совмещённому» специалисту что-то станет ясно на тему предметной области прямо в момент написания кода, то с двумя людьми нужно дожидаться точки их синхронизации).

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

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

Это разумно требовать от С++ сеньора.

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

Более того, это может быть хуже: спец по C++ может не заметить при отладке-экспериментировании, например, что у него там какая-нибудь матрица блочно-диагональная получается (потому что он не линейный алгебраист и у него глаз тупо не намётан), и не свести задачу её SVD-разложения (или разложения Холецкого) к разложению блоков, что снизило бы сложность с кубической по сумме всех блоков (Σnᵢ)³ к сумме кубов блоков Σ(nᵢ³), что может быть быстрее на порядки. Зато да, сэкономит проценты на перекладывании байтиков или GC или jit или ещё чём-нибудь таком.

Или вот на хабре есть один любитель поговорить про мейнфреймы, который гордился, как можно мейнфреймно и низкоуровнево решить задачу пересечения двух здоровенных множеств чисел (или что-то подобное), упаковывая там байты куда надо и утилизируя кэши. В его формулировке он делал два вложенных линейных цикла, имея сложность O(mn), хотя в его же формулировке множества не менялись, и можно было бы хотя бы предварительно отсортировать одно множество (там числа, это дёшево) и потом свести задачу к бинарному поиску с итоговой сложностью O(n logn + m logn), что при подстановке его чисел показало бы на порядки улучшенную производительность. А можно было бы сделать ещё лучше, чем предсортировка (ну там, вспомнить про блум-фильтры, или потратить выходные не на вымучивание ещё одного пропозала, а на чтение папиров и узнавание про binary fuse-фильтры, например). Но не, зачем асимптотики, зачем думать про всё это? Лучше потратить фокус внимания на упаковку байтов и утилизацию кэшей.

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

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

Ну и, к слову, интересно, сколько игр, где производительность важна (скажем, FPS с графонием), использует собственные движки, а сколько — используют какой-нибудь unreal/unity/etc?

Почему игра 2024-го года на Unreal при всех крышесносных возможностях этого движка у меня выглядит паршивее и тормозит больше, чем Crysis 3 2013-го года? Куда делись все эти C++-разрабы с их супероптимизированным кодом?

Почему, кстати, другая постоянно развиваемая игра с C++-движком с историей лет в 20-25 в 2024-м году никак не осилит многопоточность? Может, это как-то связано с тем, что писать многопоточку на C++ тяжело, и все эти замечательные хакерские оптимизации встают колом, когда вы живёте не в 2003-м году (хотя уже тогда был HT), а хотя бы в 2013-м?

Игра 2024 года на Unreal выглядит лучше Crysis 2013 года. А ещё лучше посмотреть на то что последние версии Unreal могут не в играх, там явно видно производительность.

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

привык собирать все из готовых кусочков

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

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

Да и вообще, пишите код без багов, а с багами не пишите. Оценивайте сроки правильно, а неправильно не оцениваете.

Разработка ПО — это очень просто!

Если "новые стандарты" и "хорошие практики" это ckd_mul() из C23 и аналогичные интринсики GCC+Clang, то я согласен. Но для их прихода ко всем ещё несколько лет. Кстати, куда пропала ckd_shl?

Похоже, вы единственный, кто сможет ответить на ваш же вопрос. ☺

Описанное весьма похоже на ситуацию, когда в очередном проекте с классической классической клиент-серверной архитектурой я решил выбрать одну из ORM: не нужно строить многоэтажные SQL запросы, система всё разрулит сама, в крайнем случае железа помощнее добавим и т.д. Ну, всё хорошо, пока не появятся узкие места: большие объемы, необходимость явного управления транзакциями, оптимизация, миграция между версиями структр... Ага, я справился, так как раньше делал "руками" и понимал, что должно происходить на нижнем уровне.

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

1. Многочленная модель в 3 раза быстрее стандартной синусной функции, если собрать её при помощи clang 11 и опциями -O2 -march=native, а затем выполнить на Intel Core i7-9700F. Но, если собрать её при помощи NVCC с опциями --use-fast-math и выполнить на GPU, а именно на GeForce GTX 1050 Ti Mobile, то стандартная синусная функция окажется в 10 раз быстрее многочленной модели.

Это мне напомнило детские приколы. Кто быстрее: черепаха или Усэйн Болт?
А если Усейну ноги в бетон залить?

при "use fast math" - у вас будут не синусы, а функции, отдалённо их напоминающие (там ошибка уж очень большая). Что по-хорошему должно отдельно указываться в ТЗ (для нейросеток с пониженной точностью пойдут, только если learning и inference - использовать на одинаковых устройствах).

Киллер C++ № 1. SPIRAL

Использует:

Shell 60.7% (написана на С)

GAP 21.0% (написана на C)

C 16.6%

Киллер C++ №2. Numba

Библиотека Python, который вроде как на C реализован.

Киллер C++ №3. ForwardCom

Итак, ForwardCom — это ассемблер .... С любой практической точки зрения, это C будущего.

Инструментарий написан на C и С++.

Как я понял, у всех этих киллеров под капотом C и C++. Это уже самоубийство какое-то :)

Ну, если так рассуждать, то развитие именно так и происходит:

  1. Убийца машинного кода был написан на машинном коде.

  2. Убийца ассемблера был написан на ассемблере.

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

Тогда вопрос, а вот с этими убийцами убийц ассемблера такой фокус получится? Или для их существования/поддержки всё же требуется C/C++/третий язык?

Как заметил автор статьи, сейчас ничего полностью не умирает, тот же ассемблер, например. Он просто перестал быть мейнстримом. И если говорить о мейнстриме, то ИМХО Python или JS несколько лет уже вроде как "убили" сами знаете кого. Только вот ОС, драйвера и прочее, где нужна перфа, на этих "убийцах" не пишется.

Наблюдая за тем как всё больше "десткопных" приложений не могут запуститься без гигабайта памяти для текстового чата, я очень сильно надеюсь, что не будут писаться и дальше. Иначе linux.js, куда нужно установить gcc.js и просто подождать ещё 43 часа пока скачивается весь npm

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

вот еще три новых имени появилось.

Если говорить серьёзно по теме статьи, то три киллера С++ (в порядке увеличения их значимости):
1. С++
2. Go
3. Rust.

Го подпирает С++ "сверху", как язык более высокоуровневый, Rust снизу, как более низкоуровневый, а сам С++ - стимулирует программистов переходить на другие, более удобные \ безопасные whatsoever языки.

Это смешно, потому что правда.

если можно писать на Python и иметь производительность как на C++, то зачем вообще писать на C++?

Из-за преимуществ Обязательной Статической Типизации.

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

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

То, что статическая типизация повышает безопасность это, извините, миф. Конечно, если:

  1. Использовать ЯП/среду со строгой типизацией, которая кидает исключения при малейшем несовпадении, а не JS.

  2. Использовать эксплиситные конвертеры. (Грубо говоря, var a = 5.0; var s = String.print("%f", a);, для переменной a тип не выводится, типов нет, но если подсунуть %i, вылетит птичка).

  3. Покрывать код тестами и прогонять их перед тем, как пускать недельные циклы.

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

И я бы сказал, вообще так ставить вопрос нельзя — про преимущества статической типизации. Есть сценарии, где без неё просто нельзя обойтись. Например, задачи, для которых надо явно задать i4 или i8. Да хотя бы вот, если надо написать динамическую типизацию (для юзеров), нужна статическая типизация для себя (или CaaS). А есть сценарии, когда обойтись можно и нужно.

Статическая типизация очень сильно различается по силе в разных языках. Если говорить о типизации в стиле C++ или там, не знаю, Java — да, конечно, вы правы, без тестов всё равно никуда. Если же говорить о типизации в стиле полноценных зависимых типов [или хотя бы хаскеля], то она устраняет необходимость в тестах и является более мощным инструментом [или, соответственно, существенно их снижает], с точностью до предположений о внешнем мире (то есть, тестировать остаётся ваши предположения о том, как работает условная ФС/БД/тому подобное).

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

Это следует из progress theorem и preservation theorem.

Контекст был именно плюсы )

дедфуд ты что ли? а с тем акком что случилось?

Стоит вспомнить про типы, и сразу вспоминают про этого! A great disservice to the community.

тестировать остаётся ваши предположения о том, как работает условная ФС/БД/тому подобное

Тот неловкий момент, когда прочитал "как работает условная ФСБ"...

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

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

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

Если в C++ есть два модуля, каждый из которых работает правильно и при их взаимодействии нет никаких ошибок то они будут работать правильно и вместе в >90% случаев.

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

Самое грустное, что на Python уже некоторое время можно накидывать типы, но в доке чёрным по серому написано

Note: The Python runtime does not enforce function and variable type annotations. They can be used by third party tools such as type checkers, IDEs, linters, etc.

Я не найду с ходу ссылки, но делались расширения рантайма, который это проверял именно на основании описанных типов (вставлял assertʼы в рекомендованных местах). Встроиться в систему импорта в Python достаточно легко.

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

Руки бы поотрывать тому, кто меняет сигнатуру публичной функции без сохранения обратной совместимости. И это безотносительно ЯП.

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

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

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

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

  1. Покрывать код тестами и прогонять их перед тем, как пускать недельные циклы.

Конкретный workflow вашего проекта имеет мало отношения к общей работе отрасли.

И я бы сказал, вообще так ставить вопрос нельзя — про преимущества
статической типизации. Есть сценарии, где без неё просто нельзя
обойтись. Например, задачи, для которых надо явно задать i4 или i8.

Или задачи, в которых должно быть именно целое число. Или задачи, в которых должно быть целое число от -90 до +90. Или ещё стопиццот вариантов, когда i4 или i8 это просто контейнер, слишком широкий для представимых значений, просто ближайший адекватный.

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

Если вы не в курсе, ваши «"полноценные тесты"» невозможны не за миллионы лет, они просто невозможны. Я когда-то специально интересовался, как обходят проблему остановки в системах, которые что-то гарантируют. И мне было отвечено, что введением фундаментальных ограничений, которыми в ЯП общего назначения и не пахнет.

А мои «полноценные тесты» это результат творческого процесса, потому что узкие места определяет программист, а не тупой автомат (компилятор). Именно поэтому они и лучше.

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

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

Э...

В статически-типизированном языке C++ можно определить шаблон класса bounded_value, что-то типа:

template<typename T, T Lower_Bound, T Upper_Bound>
class bounded_value {...};

конструктор которого будет проверять, что переданное ему значение попадает в [Lower_Bound, Upper_Bound]. Что легко протестировать один раз.

После чего при тестировании функций вида

void do_something(bounded_value<char, -90, 90> a, bounded_value<char, 0, 90> b) {...}

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

В отличии от языков с динамической типизацией.

Это называется: «Дай сиплюсплюснику гвоздь и микроскоп, и он оба эти предмета будет забивать шаблоном» ))))

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

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

Можно примеры?

А чем руками плохо?

from typing import NewType
LimitedInt = NewType('LimitedInt', int)

def check_limit(a: int, min_a: int, max_a: int) -> LimitedInt:
  if a < min_a or a > max_a:
    raise ValueError
  return LimitedInt(a)

def do_something( a: LimitedInt, b: LimitedInt ):
  pass

def main():
  do_something(check_limit(argv[1], 0, 10), check_limit(argv[2], 100, 500))

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

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

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

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

Почему же?

По факту.

Без декларации типов не заработает.

Может, это и хорошо, что закончилась. Потому что её ж тут намеренно ограничивают.

(Конечно, лучше было бы наоборот - динамическая только там, где явно разрешено.)

По-моему упущен важный момент, что ограничения выражены в типе. У вас LimitedInt просто чем-то ограничен, у комментатора выше он ограничен конкретными значениями

А, так это же легко сделать, просто в check_limit не передавать пределы, а жёстко вкодить. Он выступает, по сути, конструктором для LimitedInt. Я просто показал пример семантического использования типов.

from typing import NewType
Hours = NewType('Hours', int)
Minutes = NewType('Minutes', int)

def make_hours(a: int) -> Hours:
  if a > 24:
    raise ValueError
  return Hours(a)

def make_minutes(a: int) -> Minutes:
  if a > 60:
    raise ValueError
  return Minutes(a)

def timeout(hours: Hours, minutes: Minutes):
  seconds: int = hours * 60 *24 + minutes * 60

Ну в вашем примере вы упомянули конструктор. Значит проверки будут происходить в рантайме. А значить даже в каком-нибудь пайтоне можно описать класс BoundedNumber который точно так же будет проверять в конструкторе что там ему пришло на вход.

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

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

Можно вынести на этап прогона юнит-тестов, который во многом заменяет для Питона проверки при компиляции. Через assert-ы которые убираются в релизном запуске.

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

Посмотрите эту ветку обсуждения: https://habr.com/ru/articles/811151/comments/#comment_26776101
Я там основную претензию высказал -- когда начинается декларация типов в Python, исходная динамическая типизация прощается с нами.

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

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

при компиляции даже характер данных не проверяется!

Ну пока вы будете думать только про типы i4 и тому подобные - да. Но если вы ни разу не видели enumʼы, ranged типы с проверкой при присвоении, и т.п. - это говорит только о вашем кругозоре. Даже в стандартном C++ тривиально сделать диапазонный тип (или взять готовый из boost). А с помощью __builtin_assume при чтении значения такого типа можно ещё и указать компилятору, какой диапазон значений, чтобы он использовал это в своих оптимизациях.

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

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

А динамическая типизация в первую очередь даёт потерю этих самых гарантий контроля, ещё более "эффективно", чем undefined behavior и прочие неприятные особенности.

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

Если вы не в курсе, то такой результат получается без ограничения размера и сложности систем. А если вы проверяете, например, функцию деления чисел в процессоре без команды деления (как Itanium) - то перебрать полное множество сочетаний делимого и делителя возможно, даже если это 2^128 вариантов (2^64 * 2^64). И искусство тестирования состоит в том, чтобы определить, какие тестовые входные значения нужны, а какие - излишни, зная алгоритм (или, наоборот, не зная).

Что-то с вашим забегом в теорию тут откровенно "не то".

А мои «полноценные тесты» это результат творческого процесса, потому что
узкие места определяет программист, а не тупой автомат (компилятор).
Именно поэтому они и лучше.

А с чего вы решили, что у кого-то узкие места определяет компилятор? Что вы вообще тут назвали "узкими местами"?

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

Передёргиваете именно вы, выставляя такие требования. Статическая и сильная типизации помогают отлавливать большое количество проблем, которые при динамической и при слабой типизациях можно было бы ловить только тестами. Я пишу на Python с 2004 года и постоянно имею дело с проблемами динамики - то None вместо строки, то строка вместо числа, то двумерный массив вместо объекта матрицы и наоборот (привет numpy)... и это ещё Python, где типизация в основном неплохо сильная. Раньше ещё был Perl, где простейший аналог d['x'] выдаёт undef, и такая ерунда плодится ещё сильнее. Простейшая разметка типов и напускание чекера на код сокращает время на тестирование раза в 2-3 при первом написании, и несчётно - при последующем возврате к коду.

Если взять компиляцию, так от неё никто не ждёт чудес проверки безошибочности

Почему? Я жду.

И поэтому ушёл с C++, кстати.

И мне было отвечено, что введением фундаментальных ограничений, которыми в ЯП общего назначения и не пахнет.

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

Да и никто не мешает иметь два фрагмента — тотальный с ограничениями и гарантиями, и нетотальный с более классическим балансом.

А мои «полноценные тесты» это результат творческого процесса, потому что
узкие места определяет программист, а не тупой автомат (компилятор).
Именно поэтому они и лучше.

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

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

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

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

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

Автор тестов делает то же самое. Он просто выискивает потенциально проблемные ситуации и добавляет проверки. Иногда он основывается на репортах о пропущенных багах, добавляя тест, чтобы искать подобную проблему в будущем. Тоже ничего сверхъестественного. Проблема тут в том, что кто-то слишком буквально трактует слова «100%-ное покрытие тестами». Надо понимать, что это не абсолютные проценты, а чья-то персональная метрика. К тому же, она эмерджентна (несводима к составным частям). Если кто-то разбил проект на 10 частей и проверил каждую, другой может разбить проект на 11 частей в соответствии со своим вИдением, и окажется, что тестов не хватает.

Теперь, главное: в чём польза. Когда компилятор делает свои наивные проверочки, ему доступна, ещё раз, только грамматика языка. Она, конечно, фильтрует много откровенного шлака, но очень примитивного. А когда проверки делает программист тестов, ему доступны помимо грамматики: 0) знания о типичных сценариях, 1) общие знания о предметной области, 2) характерные данные, 3) реальное поведение среды (со всеми багами и трактовкой ошибочно неоднозначных мест из документации). Естественно, проверки, написанные программистом, будут лучше и полнее, чем результат компиляции.

Поэтому я зверею, когда слышу от нубов, которые не знают ничего кроме JS и TS или JS и C++ (никого из присутствующих в виду не имею), что статическая типизация + компиляция что-то добавляет к безопасности. Я бы не летал самолётами, не плавал пароходами, и вообще не маршировал с такими мальчишами, которые полагаются на проверки компилятора, вместо того, чтобы все их закладывать в тесты (вместе с кучей других проверок). Которые, разумеется, не потребуют никаких миллионов лет.

При всём при том, я противник TDD, и как раз по той же самой причине: TDD слишком всерьёз принимает всякие «покрытия тестами», а это идёт вразрез с фундаментальными ограничениями природы.

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

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

Автор тестов делает то же самое.

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

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

Он просто выискивает потенциально проблемные ситуации и добавляет проверки.

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

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

И я вынужден повториться, что вы крайне примитивно думаете про возможности компилятора, не говоря уже о том, что ошибаетесь в терминологии. Что выражает конкретный тип это уже не грамматика, это семантика, и эту семантику компилятор может проверить - и часто проверяет. Если у вас один тип имеет границы значений [0..100], а другой [200..300], то присвоение первого второму может вызвать ошибку компиляции. Если у вас должно быть число, а вы передали строку - типовая ошибка в JavaScript - лучше, если это будет отловлено на компиляции, а не в рантайме.

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

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

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

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

"Посмотрите, как при помощи усердия и прилежания в языке X можно сделать то же самое, что в Y реализуется при помощи тайпчекера."

И ведь у C++ система типов довольно слабая, но даже она лучше, чем ничего.

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

...а в середине недельного цикла компиляции. :)

xkcd.com/303 — это именно про плюсы.
xkcd.com/303 — это именно про плюсы.

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

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

" В этом движке было буквально всё, чем язык C++ мог похвастаться в 2005 году. Трёхзвёздочные указатели, восьмиуровневые зависимости, C-подобные макросы повсюду. "

азязя. тебя с самого детсва подло обманывали. это Не С++.

Для 2005 года нормально. С++03 еще был свеж и не мог быть везде в кодовой базе. Значит С++98.

C++03 — это несколько заплаток на C++98, новых фич там нет.

понимаете, все эти люди, которые хвалят прости госпади "Пытхон", они Реально Верят что 10тыщ строчек это БигДата. и что "создать зарплатную ведомость" это "очень трудоемкий процесс".

Пытхон за счёт С библиотек часто быстрее С# работает, а иногда быстрее С++. тоже касается вычислений на видеокарте

Пытхон очень быстрый

C++ в первую очередь угрожает сам C++

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

Не "сам C++", а комитет по его "развитию" и стандартизации.

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

То есть, хочется использовать новые возможности - придется смириться с тем, что контроль за результатом практически утрачен. Да, программа читабельна, она компилируется и правильно работает, но на то, каким образом это достигается "под капотом", лучше не смотреть, чтобы не расстраиваться. Это ставит C++ в ряд остальных непрозрачных языков, напрочь лишая его уникальности.

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

По мне так шаблоны в C++ выглядят странно, и я надеялся, что комитет их починит...

"Странно" - это крайне мягкая характеристика. :)

-- Сынок, что сказал папа, упав с лестницы?
-- Маты пересказывать можно?
-- Нет, конечно!
-- Тогда он промолчал...

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

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

Такое впечатление, что уход тех, кто "просекал фишку" состоялся во времена добавления в C++ механизма исключений. А то и еще раньше, когда перегрузку операторов сделали.

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

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

А вот когда ввели шаблоны, то понеслась массовая истерия в стиле "долой макросы, до здравствуют шаблоны!", "если ты до сих пор используешь макросы, то ты не понимаешь C++". И все бы ничего, если б хватило ума и настойчивости приделать к тем шаблонам вменяемые средства управления. Но из всего, что хоть как-то похоже на средства управления шаблонами, почти ничто не заслуживает эпитета "вменяемое". Сплошь кривые костыли, работающие только в рамках "официально одобренных" способов применения.

А уж когда обнаружилось, что шаблоны составляют пресловутую полноту, у народа вообще поехала крыша, и с тех пор всё, что технически возможно сделать на SFINAE, по определению считается кошерным и "в стиле C++". Типа, на хрена вводить в язык какие-то новые конструкции, если того же можно добиться с помощью трех, пяти или пятнадцати завязанных в уродливый клубок шаблонов, которые потом аккуратно замести под ковер вынести в заголовок?

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

А вот когда ввели шаблоны, то понеслась массовая истерия в стиле "долой
макросы, до здравствуют шаблоны!", "если ты до сих пор используешь
макросы, то ты не понимаешь C++". И все бы ничего, если б хватило ума и
настойчивости приделать к тем шаблонам вменяемые средства управления.

И всё равно в целом они вменяемее тех макросов, что были до того.

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

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

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

А большинство проблем с макросами растет от того, что в C/C++ не макропроцессор, а препроцессор, противоестественно отделенный от компилятора. В начале 70-х это еще можно было объяснить экономией ресурсов, но в конце 80-х такое объяснение уже не годилось. Могли бы в C++ объединить его с компилятором, превратив в нормальный макропроцессор, сделать минимально приличное управление и диагностику - и изрядная часть проблем ушла бы сразу.

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

Это есть, безусловно. Но самые простые случаи составляют большинство использования - это раз. После того, как написано и проверено, побочные эффекты (кроме раздувания кода;)) все отловлены - это два. Этого хватает, чтобы от макров избавляться по максимуму. Ну а на остаточный набор случаев, уже говорил, хочется что-то поумнее #define с прямой подстановкой цепочки лексем.

А большинство проблем с макросами растет от того, что в C/C++ не макропроцессор, а препроцессор, противоестественно отделенный от компилятора.

Да.

А вот когда ввели шаблоны, то понеслась массовая истерия в стиле "долой макросы, до здравствуют шаблоны!", "если ты до сих пор используешь макросы, то ты не понимаешь C++". И все бы ничего, если б хватило ума и настойчивости приделать к тем шаблонам вменяемые средства управления.

Осталось определиться на что вы жалуетесь:

а) на накладные расходы, которые связанны с шаблонами (какие, кстати говоря?);
b) на отсутствие "средств управления" (какого, кстати говоря?)

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

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

Использование же шаблонов в том виде, как оно принято в "современном C++" - многократно вложенных, со множеством рекурсий, неочевидных зависимостей и костылей из побочных эффектов - порождает чудовищные накладные расходы прежде всего на их сочинение, анализ, компиляцию и поиск ошибок. Мало кто толком понимает, как работает эта кухня, поэтому большинство лишь применяет то, что сделано "продвинутыми программистами". Эти "продвинутые", не имея адекватных языковых средств, реализуют нужную функциональность преимущественно на побочках, но таким образом трудно сделать оптимально, чаще будет "так уж получилось, но ведь работает".

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

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

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

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

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

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

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

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

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

Но сложные шаблоны в итоге всем надоели, и мы начали делать полноценный DSL. Правда, не на плюсах, плюсы там вообще мешались.

Но сложные шаблоны в итоге всем надоели

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

Я сам пытался найти смысл применения шаблонов, шаблонов со спецификациями еще в самом начале 2000-х и пришел к выводу что разные формы наследования и владения объектов работают гораздо эффективней шаблонов практически в любой задаче. Вы не найдете шаблонов в WPF например, в OpenGL, что еще привести для примера? Шаблоны хороши только для библиотек функций по коллекциям например.

в пользу более традиционных (что ли) решений

стали писать DSL

Традиционность DSL можно посмотреть по оценке соответствующего утверждения общественностью выше.

разные формы наследования [...] работают гораздо эффективней шаблонов

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

владения объектов

Как без шаблонов в одном случае владеть объектом «линейное преобразование входных данных», а в другом — «квадратичное»?

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

я вам с удовольствием поверю если вы продемонстрируете какой-то пример в котором это работает так как вы говорите.

Традиционность DSL можно посмотреть по оценке

под DSL вы имеете ввиду domain-specific language ?

Это вроде как язык который надо еще придумать. Я бы даже рассуждать не взялся на такую абстрактную тему.

я вам с удовольствием поверю если вы продемонстрируете какой-то пример в котором это работает так как вы говорите.

Что именно работает? Лишнее время на вызов виртуальной функции и последствия её неинлайнинга?

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

Это вроде как язык который надо еще придумать. Я бы даже рассуждать не взялся на такую абстрактную тему.

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

Что именно работает? Лишнее время на вызов виртуальной функции и последствия её неинлайнинга?

Лишнее время по сравнению с чем?

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

и последствия её неинлайнинга

это какое то новое слово в технике программирования, хотелось бы посмотреть пример функции которую вот прям нужно заинлайнить, опять же. Может и new тогда нужно заинлайнить?

Лишнее время по сравнению с чем?

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

Если вы посмотрите в ассемблер

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

Да и не только я — на том же cppcon люди относительно регулярно так или иначе стараются избегать виртуальных функций, пусть и в разных контекстах (см. доклад Луи Дионна на cppcon 17, или кучу докладов-вариаций на тему замены массива полиморфных объектов на набор массивов мономорфных объектов).

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

Серьёзно?

В одном случае у вас аллокации самих объектов, вызов виртуальной функции по указателю, создание и заполнение временного вектора внутри первой getVal, ну и вдобавок экзепшоны полезли. В другом случае у вас просто mov eax, edi; ret . Компилятор даже заинлайнил вырезал создание и заполнение вектора внутри второй, шаблонной getVal.

При этом я написал очень дружественный к оптимизатору код в случае наследования¹, тогда как в классических подходах к архитектуре на основе наследования точка создания полиморфного объекта и точка его использования разнесены куда больше, и компилятору ещё сложнее делать какие-либо оптимизации (вроде того, что он делает тут, заметив, что D1 и D2 имеют одинаковые размеры, выделяя память единожды, и делая сводя выбор объекта в рантайме к cmov'у нужного втбл). На практике всё будет сильно хуже.

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

Или вы считаете что дополнительное копирование из памяти в регистр занимает много времени?

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

это какое то новое слово в технике программирования, хотелось бы посмотреть пример функции которую вот прям нужно заинлайнить, опять же

Надеюсь, выше это тоже видно.

Может и new тогда нужно заинлайнить?

Когда компилятор осиливает убрать new (тоже как в примере выше с вектором) — это вообще праздник!

Серьёзно?

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

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

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

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

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

на приктике такие конструкции не то что никому не нужны , они будут мешать. Поэтому так писать никто не будет, поэтому вы и стесняетесь объяснять в чем смысл вашего кода, потому что объяснения вам самому покажут какая там ерунда написана

Жаль, что здесь есть только две оценки для комментария: +1 и -1.
Очень, очень не хватает оценки facepalm. Хотя здесь бы более уместной была бы double-facepalm.

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

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

У вас же логически ничего показать не получилось, кроме «мне не нравятся ваши примеры».

Тоже самое и с вашим примером если он что-то и доказывает то только потому что вы взяли за основу задачу

Ещё раз, вы сказали, что вызов виртуальной функции не особо отличается — я показал, что это не так. Всё.

Поэтому так писать никто не будет, поэтому вы и стесняетесь объяснять в чем смысл вашего кода

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

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

да фиг с ним можно без всяких умных слов, простой вопрос:

вы действительно считаете что масив констант запихать на стек в виде:

    std::vector<int> vec { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };

и делать это при каждом вызове это нормально?

по поводу

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

я честно говоря не ожидал что кто-то на уровне пикселей может рассуждать про применений виртуальных функций, пиксели это байты шорты или инты, если попытаться работать с ними как с объектами - это что-то странное для меня, видимо поэтому я вас и не понимаю, теперь я вижу откуда у нас разногласия идут. Но я на уровне обработки пикселей вообще на ассемблере писал тело функции которая вызывается из С++, мне и в голову не приходило применять там темплейты или виртуальные функции, там как раз надо MMX-ы и SSE применять, поэтому без ассемблера никуда.

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

вы действительно считаете что масив констант запихать на стек в виде: [.. std::vector ..] и делать это при каждом вызове это нормально?

Во-первых, не на стек, и это ключевой момент.

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

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

Пиксели с точки зрения алгоритма — это штука, у которой есть R, G и B-каналы, опционально есть A-канал, и которые занимают три или четыре байта в памяти соответственно (кстати, чему из байта шорта или инта соответствует трёхбайтовая структура?). Соответственно, у вас есть полиморфизм по части «считать следующий пиксель из raw-файла» (который кушает либо 3, либо 4 байта), и по части «получить R/G/B/A-компоненту пикселя».

Но я на уровне обработки пикселей вообще на ассемблере писал тело функции которая вызывается из С++, мне и в голову не приходило применять там темплейты или виртуальные функции, там как раз надо MMX-ы и SSE применять, поэтому без ассемблера никуда.

Слишком много слов «нет, всё равно пример мне не нравится».

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

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

А чё так? Вызов виртуальной функции же у вас несущественно отличается, а инлайнинг — пятый класс.

и последствия её неинлайнинга?

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

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

У меня функция на три строчки вызывается 10'000'000'000 раз потому, что у меня в день приходит 10'000'000'000 сообщений от биржи, и каждое из них надо, например, предварительно отфильтровать или иначе как-то обработать.

Помогите подумать о структуре кода-алгоритма, чтобы этого делать было не надо.

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

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

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

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

10 - 20 даже 100 ассемблерных инструкций всегда проще понять и даже переписать если надо, чем выгребать все сайд эффекты с темплейтами помноженными на оптимизацию в компиляторе которая зависит от десятка настроек, и еще и меняется с версией компилятора. Дело даже не в скорости, темплейты на таком уровне это просто не надежное решение даже.

Ну тогда вам точно куски на ассемблере надо писать

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

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

Дело даже не в скорости, темплейты на таком уровне это просто не надежное решение даже.

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

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

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

  1. Читать проще, чем писать.

  2. Пишу не только я, и не все пишущие смотрят в выхлоп компилятора.

  3. У алгоритма на плюсах легче проверять корректность (и заодно отчитываться перед каким-нибудь SEC, если что-то таки пойдёт не так), чем у выраженного на ассемблере. Когда я читаю ассемблер, я не слежу за глобальной семантикой того, что делает код, а лишь проверяю, нет ли локальных ненужных вещей (ну там вот new этих, указателей каких-то, смотрю, векторизовался ли поиск по строке, етц). Для глобальной семантики у меня есть исходный код на плюсах, и я предполагаю, что компилятор корректен. Ворочающий миллионами в день и потенциально могущий привести к очень неприятным судебным процессам код нельзя писать на асме.

Но как я писал где-то в соседней ветке, в итоге от темплейтов начали отказываться, но, опять же, не в пользу ассемблера (это вообще не заведётся), а в пользу собственного DSL'я.

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

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

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

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

Раз вы в состоянии проверить что ассемблер хороший, что вам мешает написать этот ассемблер напрямую без капризного посредника в виде С++ компилятора? Нафига вам темплейты сдались? У меня есть только одно объяснение: рассказы про темплейты ценятся гораздо больше чем рассказы про ассемблер. Ассемблер почему то воспринимается как что-то из прошлого века, но только без него ничего не работает в конечном итоге.

Как без шаблонов в одном случае владеть объектом «линейное преобразование входных данных», а в другом — «квадратичное»?

владение объектом с интерфейсом преобразование входных данных нет? не подходит? Почему?

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

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

владение объектом с интерфейсом преобразование входных данных нет? не подходит? Почему?

Потому что это лишний indirection в рантайме, и это медленнее, чем могло бы быть.

Представляете какой там трафик генерируется-обрабатывается?

Там нет цели минимизировать задержку, топовая производительность там не нужна. Да и от немаксимального throughput'а, в общем, тоже никто особо не пострадает. В трейдинге же, для которого я это делал, такая цель есть, потому что минимизация задержки напрямую конвертируется в деньги. Топовая производительность там важна.

Там нет цели минимизировать задержку, топовая производительность там не нужна.

я вам по секрету скажу что вы это сейчас рассказываете тому кто занимался, как раз, там, как раз, достижением Топовой Производительности со всеми MMX-ами, SSE-векторизациями, DirectX-ами, и фиг знает еще с чем. Ваш

лишний indirection в рантайме

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

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

А вот HFT-клоунада вся эта — там и деньги потеряют, и готовы платить дофига специалистам за каждые следующие несколько наносекунд.

Ну и да, если всё, что вы можете ответить — «я этим занимался» (очевидно, с намёком на то, что вы в этом специалист и вас надо слушать), то я могу возразить, что с вашим уровнем специализма мне всё понятно по дискуссии выше про виртуальные функции и аспекты их влияния на перф.

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

Не во всех задачах можно дожидаться накопления достаточного количества данных, чтобы обрабатывать их, например, применяя упомянутую вами «SSE-векторизацию». От опытного человека можно было бы ожидать понимания разницы между latency и throughput, и какие компромиссы есть в тех или иных случаях, но, видимо, не сегодня.

при этом нет никаких проблем с производительностью при работе через виртуальные функции

Потому что на 1 единицу затраты времени на вызов виртуальной функции приходится 1000 или 100000 затрат на энкодинг-декодинг, даже с учётом всего ускорения через вылизанный векторный код поверх последнего SSE, BMI2 и прочих вкусняшек.

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

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

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

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

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

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

Раз пошла такая пьянка про подавляющее большинство программистов, то это самое подавляющее большинство не в состоянии писать на C++ безбажный код.

Так они и пишут бажный. :) Потом фиксят те баги, что возникают [почти] всегда, а остальные, что возникают не слишком часто, списывают на глюки железа, ОС, кривые настройки, вирусы и происки инопланетян. :)

Ну да. А ещё вместо этого можно использовать более другие языки и не париться ни за перф (его потребность, как показывают обсуждения вот даже здесь, сильно переоценена), ни за минное поле из UB и неочевидных семантик. Машинлернеры вон на питон перешли (MLные команды в разных компаниях ещё лет 10-15 назад были куда более приплюснутые), в ядро поддержку раста завозят, а ваш покорный слуга перешёл на хаскель.

Примерно так. Если что-то написано на питоне и жрет ресурсы, как не в себя, это, по крайней мере, не вызывает такого недоумения, как если то же самое делает написанное на C/C++.

Раз пошла такая пьянка про подавляющее большинство программистов, то это самое подавляющее большинство не в состоянии писать на C++ безбажный код.

Исходя из моего (пусть и небольшого) опыта программирования на VisualBasic, Java и Ruby, это утверждение истинно не только для C++.

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

Мало кто толком понимает, как работает эта кухня, поэтому большинство лишь применяет то, что сделано "продвинутыми программистами"

А на какого размера выборке основываются ваши наблюдения о "мало кто толком понимает..."? Вы же вроде как чуть ли не в одиночку работаете.

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

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

Во-вторых, а на каком объеме исходного текста вы способны отслеживать качество результирующего машинного кода и для какой конкретно платформы?

Допустим, у вас 10KLOC и вы в состоянии оценить качество кода для семейства x86 процессоров. А если добавить сюда еще и пару-тройку ARM-ов? А если еще и e2k? А если еще что-нибудь?

А для проекта в 100KLOC вы в состоянии глобальное качество отслеживать?
А для проекта в 500KLOC?
А для проекта в 1MLOC?
И т.д.?

Может для проекта хотя бы в 100KLOC будет уже свой набор требований, в которых требования к производительности уже не будут приоритетом №1 (а если и будут, то не ко всему коду, а к отдельным его кускам)? Например, там будут иметь значения такие вещи, как обеспечение корректности, повторное использование и отсутствие копипасты с ее проблемами. Как раз те вещи, для обеспечения которых шаблоны (включая многоэтажные) хорошо себя зарекомендовали.

А на какого размера выборке основываются ваши наблюдения о "мало кто
толком понимает..."? Вы же вроде как чуть ли не в одиночку работаете.

Я на двух последних проектах работал в команде (считаем, по сотне человек в каждом). Телеком, полу-свои железки. Команда - люди стараются, конечно, но простые вызовы ASIO и замыкание на пару параметров для них предел. Уровень выше дают ну может до 5 из всех, скорее 3. Это пойдёт как уже значимая выборка? 3-5%.

(Как пример, дважды рассылался "циркуляр" про правильное использование erase-remove в итераторах, после того, как один кусок крэшился из-за erase() на map без него. Там вообще хватало простого clear(), но все пропустили.)

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

Это пойдёт как уже значимая выборка? 3-5%.

Да.

Но при крайне слабом контроле качества кода это всё превращается в невнятную кашу.

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

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

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

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

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

Раз пошла такая пьянка, то по моим личным ощущениям (моим личным, это важно) C++ники вообще никому не нужны.

итерация по строкам на порядок медленнее, чем могла бы быть

Это о чем именно?

Это о том, что первая функция на порядок медленнее, чем вторая:

void foo(const std::string& s, int& res)
{
    for (auto ch : s)
        res += ch;
}

struct meh
{
    char val;
};

void baz(const std::vector<meh>& s, int& res)
{
    for (auto ch : s)
        res += ch.val;
}
Мелочевка
int main(int, char **argv)
{
    constexpr auto Size = 128 * 1024;
    constexpr auto Reps = 100000;

    int res = 0;
    if (argv[1][0] == 'f')
    {
        std::string str;
        str.resize(Size);
        for (int i = 0; i < Reps; ++i)
            foo(str, res);
    }
    else
    {
        std::vector<meh> vec;
        vec.resize(Size, meh { 0 });
        for (int i = 0; i < Reps; ++i)
            baz(vec, res);
    }
    return res;
}
% g++ -O3 -march=native funcs.cpp main.cpp -o shit && time ./shit f
./shit f  3,47s user 0,00s system 99% cpu 3,472 total
% g++ -O3 -march=native funcs.cpp main.cpp -o shit && time ./shit b
./shit b  0,34s user 0,00s system 99% cpu 0,337 total
% g++ -v
[...]
gcc версия 13.2.1 20240210 (Gentoo 13.2.1_p20240210 p14)
% cat /proc/cpuinfo| grep "model name" | head -1
model name      : AMD Ryzen 9 7950X3D 16-Core Processor

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

Эта претензия - не к самому языку C++, а лишь к его стандартной библиотеке, которая написана на нем же. Многие настаивают на том, чтобы считать ее неотъемлемой частью языка, но это нелогично. :)

Нет, это претензия именно к самому языку C++, к самой ядерной его части и его семантике.

Многие настаивают на том, чтобы считать ее неотъемлемой частью языка, но это нелогично. :)

Так она описана в том же документе, что и язык, почему это не часть языка?

претензия именно к самому языку C++, к самой ядерной его части и его семантике

К ним тоже есть претензии, но там бороться с недостатками куда проще.

почему это не часть языка?

Потому, что "язык" (знаковая система) - это средство выражения, записи программы. А стандартная библиотека - просто набор утилит, на которые можно сослаться из программы на этом языке. Она является частью "системы программирования на языке", но не самого языка. Так же какой-нибуд "Евгений Онегин" является частью русскоязычной культуры, но никак не русского языка, как знаковой системы.

К ним тоже есть претензии, но там бороться с недостатками куда проще.

Окей, как побороть проблему выше? Собсна, в чём вообще там проблема, как вы думаете?

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

И при этом она тоже является средством выражения и записи программы, и смысл её существования в том, что она стандартная.

Я уж не говорю о том, что некоторые вещи в языке невыразимы без библиотеки. std::initializer_list, typeid, корутины, всё такое.

Так же какой-нибуд "Евгений Онегин" является частью русскоязычной культуры, но никак не русского языка, как знаковой системы.

Общепринятые слова и их семантика — вполне себе часть русского языка. Русский язык — это не только набор букв и немного синтаксических правил.

в чём вообще там проблема, как вы думаете?

В особенностях реализации std::string и std::vector, вестимо. Конкретно не вникал - сам не пользуюсь ни тем, ни другим. :) Побороть можно, скорее всего, введением в язык возможности более гибкого управления кодогенерацией/оптимизацией, и соответственно переписав реализацию string.

некоторые вещи в языке невыразимы без библиотеки.

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

Русский язык — это не только набор букв и немного синтаксических правил

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

В особенностях реализации std::string и std::vector, вестимо.

Нет, std::vector<char> будет тормозить примерно так же.

Ладно, раскрою карты. Проблема в том, что char* может алиаситься с любым другим типом, поэтому компилятор не знает, является ли объект по ссылке int& куском строки или нет, поэтому он вынужден перезагружать из памяти каждый следующий байт строки после каждой операции записи в этот int, что, например, убивает векторизацию. А вот структура, пусть даже состоящая из единственного char, уже алиаситься не может, поэтому там компилятор в состоянии всё сделать оптимально и векторизовать код, например.

Побороть можно, скорее всего, введением в язык возможности более гибкого управления кодогенерацией/оптимизацией,

Тут главное — не придумать случайно DSL, а то придет Kelbon и скажет, что так нельзя, бред и идиотизм.

Это означает, что или с языком, или с библиотекой что-то не так.

Это далеко не единственное и даже не главное, что с C++ не так.

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

Язык включает в себя семантику, библиотека — часть этой семантики.

char* может алиаситься с любым другим типом

При чем здесь типы? Совмещение/перекрытие происходит на уровне адресов - любой тип может оказаться [частично] совмещен с любым другим. Популярные компиляторы позволяют этим управлять, но везде по-разному. По-хорошему, это тоже должно управляться на уровне языка.

Язык включает в себя семантику, библиотека — часть этой семантики.

Исходный смысл термина "библиотека" - собрание литературы (то есть, продуктов, созданных при помощи естественных языков). То есть, библиотека вторична по отношению к языку.

Исходный смысл термина "библиотека программ" примерно такой же - коллекция программ, написанных на существующих языках, а не формирующих какие-то из этих языков.

То есть, утром - язык, вечером - библиотека, или вечером - язык, утром - библиотека, но язык вперед. :)

Совмещение/перекрытие происходит на уровне адресов - любой тип может оказаться [частично] совмещен с любым другим.

Нет, конечно. Указатель на meh не может на самом деле указывать на int, и наоборот (с некоторыми оговорками, которые в рамках данного примера некритичны).

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

Управляют они своим соответствием стандарту языка C++ (точно так же, как наличие флагов -fno-rtti и -fno-exceptions не делает соответствующие фичи опциональными), потому что слишком много старого кода написано в нарушение стандарта.

И да, без strict aliasing'а даже второй вариант (с std::vector<meh>) бы был тормозящим, да.

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

На уровне языка это вообще запрещено. Strict aliasing, все дела.

Но да, я согласен, C++ — слишком простой язык. Было бы очень здорово, если бы этим можно было бы управлять, особенно в рантайме, с динамическим скоупингом! Вызываете функцию в одном контексте — одно поведение, вызываете в другом — другое. А если ещё корутиной это поведение изменить, скажем… ух какой простор для полёта фантазии, мысли и ощущения собственного превосходства над машиной!

Исходный смысл термина "библиотека" - собрание литературы

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

А так-то вообще исходно «компьютер» — это такой чувак, считающий что-то на бумажке, а «C#» — это вообще нота. Непонятно, какой из этого сделать вывод, кроме того, что компьютер, исполняющий программу на C# — это на самом деле счетовод, в свободное от работы время монотонно теребонькающий одну ноту, и никак иначе.

Указатель на meh не может на самом деле указывать на int

Указывать-то он может, ибо никто ему не запретит. :)

Управляют они своим соответствием стандарту языка C++

На практике, VC++ всегда создает максимально безопасный код, если не указан __restrict, чего gcc не делает. На мой взгляд, поведение VC++ более разумно - хороший компилятор по умолчанию должен стараться обеспечить работоспособность программы, а не породить как можно больше глюков.

На уровне языка это вообще запрещено. Strict aliasing

Это не "запрещено", а "правильная работа не гарантируется".

если бы этим можно было бы управлять, особенно в рантайме

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

корутиной это поведение изменить

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

Эта претензия - не к самому языку C++, а лишь к его стандартной библиотеке, которая написана на нем же.

я бы даже дальше пошел в этом направлении:

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

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

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

если это претензия к производительности (скорости исполнения) сгенерированного кода

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

Вообще это довольно удобно. Язык у нас создан для производительности, но если к производительности есть вопросы, то это всё виноваты конкретные компиляторы и библиотеки, а не сам язык.

Так-то питон или жс тоже созданы для производительности, просто с компиляторами не вышло-с.

А где написано что язык создан для производительности, или это только ваше пожелание?

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

Заметьте договориться надо с машиной, а не скомпилятором!

Вы же вот пишите:

Дэниел Лемир, может, и справился бы, но мы (как и авторы всех реализаций, когда я последний раз проверял) — не он

Зачем же вы себя так ограничиваете в том чего вы могли бы достичь? Хотя я честно говоря не знаю кто такой Дэниел Лемир, но теперь посмотрю конечно.

А где написано что язык создан для производительности, или это только ваше пожелание?

Это лейтмотив любого keynote от Страуструпа на любом CppCon, а также лейтмотив половины хвалебных статей в адрес C++ и комментариев под такими (и прочими) статьями. Типа, нужен перф — берите C++ (или C), другого выхода нет.

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

Напишете эффективный кодек на жс без всяких васмов — вернёмся к этому вопросу.

Зачем же вы себя так ограничиваете в том чего вы могли бы достичь?

Понимание собственных ограничений — часть взросления и возмужания.

Хотя я честно говоря не знаю кто такой Дэниел Лемир, но теперь посмотрю конечно.

Можно начать с его блога lemire.me, а также статей вроде https://arxiv.org/pdf/1910.05109.pdf или https://arxiv.org/pdf/1902.08318.pdf или https://arxiv.org/pdf/2201.01174.pdf

на какого размера выборке основываются ваши наблюдения о "мало кто толком понимает..."? Вы же вроде как чуть ли не в одиночку работаете

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

хотелось бы пруфов

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

на каком объеме исходного текста вы способны отслеживать качество результирующего машинного кода и для какой конкретно платформы?

Не вижу связи. Одна из особенностей C++ в том, что одна операция в выражении или один оператор исходного текста на любой платформе дает, за редким исключением, небольшое количество (обычно не больше десятка) машинных команд. И только шаблоны (или макросы) дают возможность генерировать десятки-сотни и более команд на одну внешне простую конструкцию языка. Поэтому, если вдруг какая-то простая конструкция вдруг порождает (в этом же самом месте, или в других) неадекватно большое количество кода, это сразу же вызывает подозрение в неоптимальности. Объем кода в целом не имеет особого значения.

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

Может для проекта хотя бы в 100KLOC будет уже свой набор требований, в которых требования к производительности уже не будут приоритетом №1

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

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

Огромное количество разработчиков вообще не читает ресурсов типа Habr или reddit. А из тех, кто читает, очень небольшой процент ничего не пишет (ни статей, ни комментариев).

Не вижу связи.

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

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

Или о том, что был выбран неэффективный алгоритм.

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

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

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

Та же самая работа с JSON может выглядеть очень по разному. Можно взять шустрый RapidJSON (который весь на шаблонах) и написать 100500 строк кода вокруг него, т.к. RapidJSON дает ну очень низкоуровневый интерфейс. Можно взять более тормознутый nlohmann::json, с которым будет написано всего 1500 строк кода. Как раз за счет шаблонов. И разница по производительности в несколько раз не будет играть никакой роли, если работа с JSON-ом не лежит на критическом пути.

Огромное количество разработчиков вообще не читает ресурсов типа Habr или reddit

А что они читают, на каких материалах учатся? Если Вы полагаете, что для большинства из них кто-то одновременно и опытный, и заботливый, и обладающий педагогическими талантами, и требовательный, подбирает учебные материалы контролирует эффективность обучения, то как именно это реализовано, и насколько распространено?

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

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

разница по производительности в несколько раз не будет играть никакой роли, если работа с JSON-ом не лежит на критическом пути

И ради бога, если эта разница не ощущается пользователем непосредственно. Если же ему приходится регулярно ждать, пока загрузятся/обработаются те массивы - значит, уже непорядок. А если объем кода в разы и на порядки превосходит функциональность, то еще хуже - хранить и загружать этот объем приходится все время, а не только по требованию.

А что они читают, на каких материалах учатся?

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

Дались Вам эти платформы.

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

И ради бога, если эта разница не ощущается пользователем непосредственно.

Боюсь без конкретики все разговор опять зайдет про сферических коней в вакууме.

Я вам пример привел: работа с JSON. В которой шаблонов очень много. Но мало кого парят накладные расходы, связанные с использованием шаблонов в этой области.

Нельзя ориентироваться по тому, что пишут в Интернете

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

пишут в Интернете далеко не все, а только небольшой процент от причастных к профессии

Да - остальные это читают. :)

это же вы сетуете на высокие накладные расходы C++ного кода

Вы меня весьма превратно понимаете. Я сетую на высокие накладные расходы не "C++ного кода вообще", а кода, написанного на C++ без достаточного понимания работы вычислительной системы. У тех, кто этого не понимает, нет никаких оснований писать на C++, этот язык не для них. Он не для тех, кто осилил только умение излагать алгоритм языковыми конструкциями, пусть и весьма заковыристо. Для таких людей есть много других языков.

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

Мне-то как раз в подавляющем случае хватает. Но почти весь софт, который я делаю, работает в жестком реальном времени (звук), поэтому мне недостаточно, чтоб "работало вообще" - я стараюсь получить кратные запасы по производительности. А большинство этим не заморачивается, тестируя софт лишь в условиях, приближенных к идеальным, где их далеко не оптимальный код более-менее работает. Когда же их софт в связке с моим начинает хрюкать и трещать, пользователи выкатывают претензии прежде всего мне - "ведь не может быть, чтобы у Microsoft/Apple/Realtek и т.п. был плохой код!". :)

Отсюда и сомнение в том, что некий C++ный разработчик кроме своей прикладной области еще и будет сильным экспертом в нескольких аппаратных платформах

Сильным - не обязательно, и даже экспертом не обязательно. Но он должен уверенно ориентироваться в том, какой двоичный код порождает его текст, и какова реальная потребность в ресурсах у той задачи, которую он решает. Если же разработчик на C++ уверенно заявляет что-нибудь вроде "i5 на 2.5 ГГц это не потянет, надо минимум i9 на 3.5" или "8 Гб тут недостаточно, надо вдвое больше", а по факту это не так, то он выбрал себе не тот инструмент.

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

Я просто приведу здесь вашу же цитату:

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

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

Если "либо отказаться от многих новых возможностей" не относится к "C++ному коду вообще"

Это относится в основном к новым возможностям C++, которые в него вводились в ходе развития (шаблоны, исключения, лямбда-функции, замыкания и т.п.). По сути, это все то, что позволяет очень малым объемом исходного кода реализовать сложную и не слишком прозрачную обработку. У таких возможностей везде сложно с переносимостью и совместимостью.

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

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

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

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

А вот если мы хотим сделать на C++ шаблонный алгоритм, в который можно подставить энное количество данных и функций, которые он будет комбинировать для реализации алгоритма, то оказываемся примерно в той же ситуации, как если бы в подпрограмме на ассемблере можно было лишь тупо копировать параметры, переданные в регистрах, в другие регистры или память, а вместо команд сравнения/тестирования и условных переходов у процессора были только весьма странные конструкции, неочевидным образом меняющие порядок этого копирования, и на которых можно было бы, при известной сноровке, сгородить нечто похожее на привычные условные операции. Те, кто привык городить такое, воспринимали бы идею реализации в процессоре простых условных операций с удивлением и недоверием. :)

Шаблоны же просто подставляют на места своих аргументов выведенные типы - нет?

>3ий абзац

Очень интересно вы просто кучу сгенерированных функций, которые в целом не отличаются от простого написания всего этого барахла руками через ctrl+c/v, во что-то, что генерирует непонятное в процессоре.

На примере std::less, опишите генерируемые оверхеды и почему std::less<int> может быть хуже int_less_comparer?

Ну и как лучший (по моему мнению) класс std::vector, для разных типов может быть хуже рукописной IntResizableArray.

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

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

Еще скажите, что auto, это вообще игрушка дьявола которая убивает перформанс

Шаблоны же просто подставляют на места своих аргументов выведенные типы - нет?

Для протокола — ещё у шаблонов есть паттерн-матчинг специализации, что и делает их Тьюринг-полным языком и позволяет упарываться всеми этими enable_if и прочей страшной ерундой.

Ну и как лучший (по моему мнению) класс std::vector, для разных типов может быть хуже рукописной IntResizableArray.

Например, если вы в кишках вектора не учли, что int можно memcpy'ать при ресайзе, а std::string — нет, и поэтому поэлементно всё подряд копируете всегда несмотря ни на что.

А во всём остальном вы правы, конечно.

Диагональный гуглеж говорит, что вектора делают memcpy для тривиальных типов и потенциально тривиальных агрегатов (всякие стракты интов) [что логично], но красивых пруфов на руках у меня нет, пишут, что где-то в дебрях std::copy магия.

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

Каждый раз как в первый раз пишу std::hash с кучей гугла

ЕМНИП это вопрос quality of implementation. Ну и тут я примешал необходимость написать свой контейнер (например, мне как-то надо было сделать циркулярный буфер, и там всё правильно, эффективно и по стандарту сделать тоже та ещё весёлая задача).

Каждый раз как в первый раз пишу std::hash с кучей гугла

Перестал писать std::hash после того, как начал писать deriving (Generic, Hashable) , а дальше компилятор всё сам. Рекомендую!

А можно пожалуйста пример/ссылку/детальку про deriving (Generic, Hashable), как-то ничего не нашел толкового в гугле для c++

Это такая шутка-самосмейка на тему ухода от C++ в более хорошие языки, извините.

Шаблоны же просто подставляют

К "просто подставляют" у меня вообще никаких претензий нет - это основная, прямая функция шаблонов. Претензия к тому, что шаблоны с C++ реализованы лишь чуть менее убого, чем макросы в его препроцессоре, поэтому в человеческом виде годятся лишь для простых, типовых задач. Как только хочется чего-то чуть менее типового (того самого управления подстановкой), так не остается ничего другого, кроме "шаблонной магии".

На примере std::less, опишите генерируемые оверхеды

Почему именно на нем? Сама по себе std, если вокруг нее не наворачивать странного, вполне себе адекватна. Оверхэды возникают, например, когда в шаблонной функции используются временные объекты классов, переданных в параметрах (хоть явно, хоть неявно, что даже чаще), затем эта функция вставляется в конструктор другого класса, временные объекты которого, в свою очередь, используются еще в одной шаблонной функции. Когда такая вложенность обнаруживается в обычных функциях, это не слишком трудно размотать отладчиком/profiler'ом, а когда в шаблонных, то часто проще махнуть рукой и убедительно сказать руководителю/заказчику "иначе этого никак не сделать". :)

чем опытнее люди понимают в темплейтах и игрищах вокруг, тем меньше у них с ними политических проблем

Дык, именно так. Но где ж Вы нынче видите опытных людей в достаточном количестве? :) Большинство тех, кто называет себя "программистами на C++", просто умеет (неплохо или даже очень хорошо) писать на C++ программы, выполняющие заданные действия, но напрочь не в состоянии оценить, насколько адекватны те самые оверхэды. А при таком подходе в C++ нет никакого смысла - для них он просто один из множества языков, его уникальные особенности такими людьми не востребованы, но их продукция вносит свой вклад в оценку языка, как такового.

мне кажется вы путаете что-то

разве не С уникален этим?

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

Си в этом плане намного более предсказуем, как мне кажется

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

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

Про хобби уже много на Хабре написано: пельмени, когтеточки, тюльпаны, магазины на севере...

У всех свои задачи. Если все будут только упарываться по удовлетворению хотелок баб в обмен на отношения, то кому же двигать прогресс? Кто-то же должен не только строить шубохранилища и забивать их содержимым но и доходить до границ нашего познания и прокладывать путь дальше.

А это те, кому баб не хватило.

автор сидит в очень узкой нише и натягивает ее на C++ в целом

давайте возьмем кодовую базу Chrome

перепишем ее на новом универсальном ассемблере? перепишем на Python, чтобы выполнять на GPU?

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

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

Речь не о том, что "что-то убийца чего-то", а исключительно о выборе наиболее эффективного инструмента для решения конкретной задачи.

Через 10 лет выйдет статья на Хабре, что вот опять, снова С++ умирает. А С++ программисты, будут так же писать С++ код и заниматься, С++ проектами. Браузерами, высоконагруженныи сервисами, читать статьи о новых версиях стандарта и т. д Жизнь идёт, С++ уже лет 30 помирает, ещё чуть чуть осталось:)

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

Она не снижается, а размывается. Те проекты, которые имеют кодовую базу на плюса, от игровых движоков, до кад систем или баз данных, на плюсах и остаются. Но при этом растет доля проектов(в основном веб-сервисов), написаных на более простых языках - typescript, C#/java, go. Но это не умирание, это просто занятие своей ниши - ΙΤ переходит, вернее уже почти перешло, от состояния "один язык для всего" к "для каждой задачи свой инструмент".

Но при этом растет доля проектов(в основном веб-сервисов), написаных на более простых языках - typescript, C#/java, go.

Ну вот у вас или CAD с базами данных, или веб-сервисы. А промежуточного вы не видите. Когда-то такие задачи, как email сервер, VoIP-софтсвич, IDE (смотрим на пучок от JetBrains) - можно было писать только на полностью компилируемых языках с ручным управлением памятью, другое не работало. Сейчас IDE - на Java, C#. Свич - какие-нибудь кодекоконвертеры и передатчики RTP - ладно, C/C++, но сигнализация - нафига там C++ когда Java справляется не хуже (а я до сих пор парой пальцев ноги в проекте где Python). Email - аналогично, накой там C++, Java пробовалась уже с 2000 (если это не что-то масштаба Google), хотя Go дышит в затылок. И так далее.

А завтра и CAD будут забирать у C++, потому что нафиг там такое не нужно, кроме, может, пары критичных алгоритмов какой-нибудь сложной разводки. И то, хорошо оптимизированный язык типа C# вполне может за-JITʼиться в сравнимый код. Игровые движки - сколько игр не требуют 144fps и вполне бегают на Unity, а то и на чистом JS?

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

Я к этому думаю также, что если бы Go и Rust не старались паковать всё в один статический бинарник - то уже давно большинство содержимого всяких /usr/bin было бы на них. Сейчас они сами себе тормозят это продвижение (намеренно или нет - понять сложно).

А чего плохого в одном бинарнике? Место на диске ничего не стоит, а проблем с версионностью куда меньше.

Место на диске ничего не стоит

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

Жесткий диск в 1 терабайт стоит как два часа работы среднего программиста, о каких дорогих мегабайтах вы говорите?

Жесткий диск в 1 терабайт стоит как два часа работы среднего программиста

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

​1. Какой "жёсткий диск"? HDD? Замечательно. В типовом лаптопе сейчас SSD на M.2. За 1TB это будет 100$. "Средний программист" exUSSR получает где-то 2000$/месяц, то есть это уже день, а не два часа. Это пока что уровень разработки. Или вы про "среднего программиста" Кремниевой долины? Там, конечно, и час будет, но таких мало от общего числа по миру.

​2. Если мы говорим про ответственный сервер, то там цена быстрого надёжного хранилища будет равна минимум 4 таким дискам плюс контроллер для RAID 5/6/Z. Ладно, контроллер софтовый (не всегда адекватно, но упростим) - всё равно умножили на 4. Не буду считать это в 4 днях работы разработчика, хотя сколько тех серверов нужно для реального сервиса - может, и 1000... это уже 11 лет зарплаты одного такого программиста. Ладно, 3 года, если из Фремонта или Санта-Анны. И 20-30 лет, если из Бангалора:))

​3. Теперь, говорим про железяку на выносе (мой последний случай именно такой). Диск - флэшка на ~200GB, SD card. Уже занято ~70% на старте, ещё ~20% выделено на логи. И ещё требуется скорость обновления, каждые 10 секунд на счету, потому что это массовое падение сервиса - железяка обслуживает несколько тысяч клиентов. Лишний гиг несжимаемого (именно так) бинаря, по мегабайту на тысячу рабочих программ - реально, полминуты на скачивание (по совсем не свободным каналам, у юзеров звонки и трафик, а на сервере драка сотен скачивающих) плюс где-то столько же на заливку на флэшку.

Понимаете, почему расчёт на один HDD это, ничего личного, просто ламерство? И почему, например, убунтовцы в 24.04 говорили "очень-очень-очень извините, но наши стековые фреймы стоят пару процентов места и времени, но нам всем эта пара процентов много даёт непоправимой пользы, поэтому мы таки сделали"?

"Средний программист" exUSSR 

Ну вы бы еще с сомали сравнили. Я говорю про рейт тех стран, где находится большая часть IT компаний - европа, китай, индия и сша. Ставка 30 долларов в час это средний доход. А кремнивая долина это 60 в час и больше. Но даже при 2000 в месяц, диск обойдется вам в 4 часа работы.

Если мы говорим про ответственный сервер

AWS вам подойдет как ответственный сервер? S3 стоит $0.0125 per GB, стоимость мегабайта в рублях посчитайте сами.

Теперь, говорим про железяку на выносе

200GB диск, желязка, полминуты на скачивание - какие-то истории из 2004 года. Ваша "желязка" случайно не по модему в 28800 бод соединение держит? То же был бы неплохой аргумент. Но так да, понятно, вexUSSR своя атмосфера, согласен что каждый лиший килобайт может весь бюджет проекта опрокинуть.

убунтовцы в 24.04 говорили

Причему тут стековые фреймы, что за каша началась.

европа, китай, индия и сша. Ставка 30 долларов в час это средний доход.

Нет, вы слишком хорошо думаете о среднем доходе. Разделите минимум на два. Для Индии ещё больше.

AWS вам подойдет как ответственный сервер? S3 стоит $0.0125 per GB, стоимость мегабайта в рублях посчитайте сами.

S3? Конечно, не подойдёт, это потоковое хранение, а не устройство прямого доступа, и не обязательно настолько быстрое даже как HDD, часто - в разы медленнее (его могут держать на очень медленных дисках типа черепичной записи, через забитые каналы, и всё такое).

Как активный диск у Амазона (пусть мы смотрим таки на Амазон) надо смотреть, например, на EBS, и вот тут уже видно - provisioned IOPS (мы ж хотим гарантированную полосу?) это 125$/TB/месяц (открыло Ohio, пусть будет оно). Это соответствует расходу одного такого диска в месяц, или комплекта RAID за 4 месяца. Деля ещё на 2 за on-demand вариант (а не предоплаченный на 3 года, как аналог персонального железа), соответствует 8 месяцам. Ну, износить SSD за год активным общением это как пить дать. Кстати, там в цене ещё сами IOPS надо добавить, а не только хранилище для них:) Так что Амазон тоже на активном дисковом хранилище использует какой-то RAID, как я и описывал.

Собственно, упоминания S3 достаточно, чтобы понять, что вы упорно сравниваете паровозы с апельсинами, и, или 1) не понимаете вообще, что сравниваете несравнимое, или 2) активно троллите. Выбирайте сами.

200GB диск, желязка, полминуты на скачивание - какие-то истории из 2004
года. Ваша "желязка" случайно не по модему в 28800 бод соединение
держит?

​2024. Нет, это чистая реальность из этого года. Головная станция интернет-доступа. И тоже или даже не подумали калькулятор взять, или троллите. Полминуты на 28800 это ~86kB, а не гигабайт. Мне уже как-то надоедает вести беседу на таком уровне.

Причему тут стековые фреймы, что за каша началась.

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

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

Чтобы выполнить большой бинарник, его надо смаппить в раму. Потенциально весь, потому что мёртвый код обычно раскидан как попало, а не уложен в пределах страницы

Это больше касается Go, правда. В Rust для релизных сборок обычно включают LTO, и в бинарнике остаётся только необходимое

Ну для динамической линковки это будет работать лучше только для популярных либ, если никто до этого этот .so не загружал, то какая разница? И в расте ничего не мешает испольвать dylib, если нужна такая оптимизация. Или вообще на рантайме грузить через сишный ABI.

Чтобы выполнить большой бинарник, его надо смаппить в раму.

Но есть нюанс: образ программы надо отобразить в виртуальную память, а не в каую-то там раму. Даже на ЕС ЭВМ Ряд2 так было - хотя физическая память там действительно была размещена на рамах, порядочного такого размера, которые в шкаф центрального процессора вставлялись, но виртуальная память там таки уже была.

Так вот, отображение того же exe-шника формата PE в WIndows заключается всего лишь в выделении диапазона адресов страниц и указания, что содержимое эти страниц на диске надо читать из файла PE. Ну, а физическая память выделяется по потребности, и может быть возвращена, когда нужда в ней отпадет. Windows тут чисто для примера, поскольку про нее я знаю лучше, чем про *nix, а так AFAIK все современные операционные системы работают примерно так же.

И в Линуксе он так и не появится

Странно С++ сравнивать с dsl языками заточенными для работы с БД. Естественно, что у универсального языка будет менее удобные интерфейсы.

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

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

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

Универсальность - это как автомобиль который "одинаково хорош" и в трофи-рейде и в кольцевой гонке.

Заказчику неважно на чем оно там у вас написано. Ему важно чтобы это было сделано быстро и работало максимально эффективно. А где использовать sql, а где прямой доступ к БД - это вы сами должны решить. И не быть ограниченными в возможностях только потому что "универсальный язык" чего-то там не позволяет. Не позволяет - ищите тот инструмент, который позволяет. Или ищите другого заказчика и другие задачи.

Вы меня не слышите. Зачем тянуть dsl в проект. Если в итоге все использование сведется к методу GetMyData()?

Сравнение языков с машинами всегда некорректно.

Сам sql это уже абстракция и язык запросов. Накатить dsl + orm + ещё кака какая-то.

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

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

И вот эта самая функция получения данных каждый раз будет новая. А потом эти данные надо обработать. С учетом того, что все числовые данные всегда только в формате с фиксированной точкой и никак иначе (скажем, типа float я тут вообще ни разу не видел, хотя в языке он есть, int используется для всяких индексов массивов и параметров циклов, ну счетчики иногда). Т.е. язык обязан уметь работать с фиксированной точкой, причем, с минимальными накладными расходами. Без создания лишних объектов на каждый чих.

Тем и хорош DSL что он позволяет делать все это без лишних затрат.

Поэтому к любой штуке есть свой интерфейс. К БД как пример libpxx, у внешнего движка свой интерфейс и т. д

Зачем ещё прикручивать именно dsl? И к этому dsl интерфейсы доступа,?

Зачем тянуть dsl в проект. Если в итоге все использование сведется к методу GetMyData()?

Зачем, что DSL это очень часто не столько специализация, сколько ограничение.

Простейший пример: Java, C#, Go это DSLи рантайма с автоматическим управлением памятью. Ограничение возможностей программиста (полное или почти полное с ужатием до unsafe-кода) ограничивает его возможности, но за счёт избавления от проблем нарушения памяти резко увеличивает объём кода, разрабатываемого и сопровождаемого при той же квалификации коллектива и руководства.

SQL ещё больше ограничен, в нём не сделаешь, например, приём бинаря из Интернета на исполнение:), или это будет очень явно видно по характерным вызовам.

И это мы ещё не вспоминаем, что на нём банально проще писать, чем каждый раз рисовать самому код, который будет считать, по которому из индексов лучше итерироваться:)

Надо посмотреть, чего этот Spiral может в реальных условиях. Интересно, если это микс ИИ и брутфорс-компилятора, который вылизывает код до мопов. Я помнится в середине нулевых писал редактор памяти (WGC), ещё на Delphi, ибо был сильно недволен скоростью ArtMoney и прочих аналогов. В итоге на ассемблере код вылизал до мопов так, что до сих пор он в однопоточном режиме лучше моих-же многопоточных вариантов на любом высокоуровневом языке программирования. Особенно когда в гигабайтном объеме памяти надо найти все нулевые байты.

На ассемблере в виде выставок в Делфи или на чистом ассемблере?

Сначала были только вставки, потом появились ассемблерные файлы. Решил выложить ещё раз код https://github.com/alpet83/WGC - архив когда-то был на сайтах распространявших программу, и сгинул уже.

для архитектуры "правильный С++" это С#.

Для производительности числодробилки можн на С++ писать и дёргать их

Тогда уж на Си.

Чтобы использовать в дотнете плюсовые классы, нужно писать обёртку на языке C++/CLI, стиль и семантика которого напоминают творение доктора Франкенштейна.

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

C++/CLI.

Самое ужасное что числодробилка из C++ - такая себе. Мешают штуки типа pointer aliasing, динамическая аллокация памяти и т.д. Я видел вырожденные примеры, где даже Java обгоняла C++.

Динамическая аллокация, если ей злоупотреблять, действительно, может отъедать прилично времени и ресурсов по сравнению со статикой.

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

Даже без танцев с аллокаторами, простой вынос "числодробильности" в отдельные объекты, выделяющие память (да и просто выполняющие тяжёлые операции вроде создания вспомогательных файлов, соединений, потоков, каких-нибудь fftw_plan , чтения и обработки конфигов, и прочей вспомогательной ерунды) только при создании зачастую уже достаточно. А если не пересоздавать эти объекты-числодробилки с нуля, а переиспользовать старые (те самые worker pool) - так и вовсе красота.

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

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

А почему «даже Java»? В Java есть JIT-компилятор байт-кода в машинные коды целевой системы, потому она вполне себе обгоняет и не на вырожденных примерах.

Потому что под целевую систему и целевой профиль нагрузки/входных данных/чего-то ещё можно воспользоваться PGO. То, что этим далеко не всегда пользуются вопрос отдельный.

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

Тейк про ForwardCom совсем не понял. Разве не этим-же занимается LLVM ?! 🤔

Но я совершенно не уверен, что то приложение на C++, которое я в прошлом году собирал с CMake 3.21, соберётся и на будущий год с CMake 3.25.

Вот про CMake вообще не понял, каким боком он к способности кода к сборке через некоторое время?

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

Хм, успешно работаю с большим (более 200 таргетов) проектом через CMake почти пять лет, ещё ни разу не налетал на подобное :)

Даже с учётом того что у проекта 6 разных целевых платформ.

UPD: не исключаю что подобное возможно, но это скорее тревожный звоночек для сопровождающих конфигурации проекта что что-то явно идёт не туда :)

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

О, это выглядит как хорошая тема для статьи тут, на Хабре!

Могу лишь пожать вам руку, ибо сборка под видеокарты - звучит как лёгкая степень кошмара :)

Понимаете, у CMake есть куча очень несмешных приколов. И однажды наступив на них, разробравшись с ними, вы обрастете не знаниями. Вы получите понимание поведения крайне неадекватной системы сборки в каких-то специальных случаях.
Например (сталкивался лично, ушло два дня, вытянуло много сил и времени именно тогда, когда это было критично):
https://github.com/boostorg/boost_install/issues/12#issuecomment-508683006

А ну бегом писать игры на ассемблере и спиральке! Что? Не слышу, что такое? Ох, да нельзя ж

if (MSVC)
	add_compile_options("$<$<CONFIG:RELEASE>:/GS->")
	add_compile_options("$<$<CONFIG:RELEASE>:/Oi>")
	add_compile_options("$<$<CONFIG:RELEASE>:/Ot>")
	add_compile_options("$<$<CONFIG:RELEASE>:/Oy>")
	add_compile_options("$<$<CONFIG:RELEASE>:/MT>")
else()
	add_compile_options("$<$<CONFIG:RELEASE>:-static-libstdc++>")
endif ()

Ускоряет код в разы, если интенсивно используются std контейнеры и прочие функции стандартной библиотеки. Благодарность сюда: bc1qavr5sxnj420gt2ql0wnwtxeqwmhl08frw0ja8k

Прекрасные советы, особенно нравится идея ставить /GS- по-умолчанию. Ну и, конечно, удачи с dlopen после статической линковки libstdc++

Там человек отказался от раста в пользу C#, причем тут плюсы?

Только при том, что раст некоторые называют "убийцей C++".

Единственный убийца С++ это Бьёрн Страуструп.

"У каждого свои недостатки" (С)

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

Так-то и мы с другом тоже создали язык Си-подобный для PDP-11 c возможностью вставок машкода и компилятор к нему. Потом на нём коммерческие проекты в виде игр пилили для К 1801 ВМ1.

Слабый Си с мощным ассемблером уже смешиваются.

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

Что именно из высказываний Вирта вы тут имеете в виду?

Что надо создавать специализированные языки - каждый под свою нишу.

Я не смог нагуглить цитату и контекст, слишком много мусора в поиске. Есть точная ссылка?

Было в одной из его статей... Поищу при случае. Но точно помню, что он был про ив универсальных ЯП.

Отличная статья,

зацепила одна фраза

> Декораторы Python за вас превращают любой фрагмент кода в его абстрактное синтаксическое дерево, после чего вы можете делать с ним что угодно.

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

а парсинг AST это как-бы другая стадия, когда байткод *.pyc создается...

и вот теперь не знаю что думать, автор можете немного пояснить?

нашел https://docs.python.org/3/library/ast.html, вопрос отпал. AST можно получить в рантайме

Публикации

Истории