LINUX.ORG.RU

О божественном C и введение в него через ed

 , ,


4

2

Бытие (частично) определяет.

*Какая регулярка преобразует for с телом в while с телом ?

*Какой #define макрит for в while?

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

Так-c, я забенчил свои ассоциативные массивы с переопределением базовых типов:

#define LONG volatile LONG
#define int volatile int
#define char volatile char
Результат:
- 3.31-3.36 секунд на полностью volatile памяти;
- 3.18-3.25 секунд с переопределением int и volatile структурах;
- 2.22-2.24 секунд при полностью отключенных volatile.

Причина такого феномена простая: компилятор строго локальные переменные сует в стэк при наличии модификатора volatile — вполне возможно, что ваш компилятор такого не делает и картину вы увидите другую.

Простой int:

int try_count = 3;
    mov    ebx,3  
while (try_count > 0)
{
  try_count = try_count - 1;
    dec    ebx  
    mov    dword ptr [try_count],ebx

Volatile int:

int try_count = 3;
    mov    dword ptr [ebp-4],3
    mov    bl,dl  
while (try_count > 0)
    cmp    dword ptr [try_count],0  
    mov    esi,ecx  
    push   edi  
    jle    allocate_block+117h (01241247h)  
    xchg   ax,ax  
{
  try_count = try_count - 1;
    dec    dword ptr [try_count]

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

byko3y ★★★ ()
Ответ на: комментарий от byko3y

Причина такого феномена простая: компилятор строго локальные переменные сует в стэк при наличии модификатора volatile — вполне возможно, что ваш компилятор такого не делает и картину вы увидите другую

Проверил: gcc 9 и clang 6 примерно так же себя ведут, засовывая переменную в стэк. Судя по всему, создатели компиляторов просто вырубают с концами любые оптимизации в окрестностях volatile.

byko3y ★★★ ()
Ответ на: комментарий от qulinxao3

этот тред великолепен, ящитаю

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

про иероглиф судзуки, например? (если кроме лулзов)

вообще, любопытно было бы посмотреть на китайский метапрог в таком вот вышеописанном стиле.

ну и я там ссылок на форт литературный принёс: раз два

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

Forth Scientific Library descr src Methods of computational physics Win32Forth primer

конечно-автоматное для запихивания в SWITCH-техноломагию.

anonymous ()
Ответ на: комментарий от byko3y

Все 13 перечисленных тобой пунктов - это второстепенные мелочи.

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

Первостепенным же критерием является «ты хоть как-то напиши».

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

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

но на PL/I можно – доказано Multics. на Аде можно – тоже есть несколько таких ОС. на Modula-2, Modula-3, Oberon – тоже.

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

не только лишь все.

например, OS/360 и PL/I. не вышло – пришлось использовать низкоуровневый PL/S для системщины.

Multics – высокоуровневый PL/I, но! ты видел те исходники? там переносимость, портируемость, мобильность отсутствует как класс. ибо мейнфрейм с 36-битными регистрами и 18-битными указателями. в «Soul of the old machine» презентации про SIMH симулятор автор его пишет, что проще оказалось в симуляторе реализовать то специфическое железо, чем HAL слой хотя бы попытаться выделить. ибо там куски на ассемблере ЕМНИП, в дополнение к высокоуровневому PL/I. да и слишком завязано на железо конкретного мейнфрейма, уже исторически значимого, более никак.

CP/M – вот здесь могла бы взлететь цепочка метаязыков, на основе PL/I. Гари Килдал спортировал PL/I упростив и продав компилятор Intel’у. получился PL/M. в нём упрощенная типизация, упрощены контролируемые переменные и обработка ошибок, отладка – выкинуто в основном. препроцессор ЕМНИП тоже упрощён в макроассемблер.

получился такой низкоуровневый недоPL/I для системщины и байтоёбства. но, в исторических исходниках CP/M уже чётко выделены BIOS, BDOS слои как прообраз HAL. правда, настоящей многозадачности и ACL из Multics – ниасилили, нет её. да даже иерархическую ФС в виде дерева – в CP/M ниасилили. зато у нас есть резидент который перезагружает shell, командный процессор.

и постраничная подкачка оверлеями. например, исходники и бинарники PL/I и PL/M под CP/M компиляторов – доступны. PL/M: компактный бинарник, самодостаточный COM в несколько килобайт. плюс линкер и библиотекарь. PL/I: оверлеи на три дискеты, кроме базовой запускалки.

а так вааще занятно было бы, кабы не упрощать кинулись, и из низкоуровневого PL/M строить эту CP/M 8080, CP/M Z80 (надмножество 8080 от тех же разработчиков), CP/M 8086 (тут финты ушами, транспилятор для совместимости) и CP/M Motorola MC68k (тут появляется libc для портируемости и какой-то SDK).

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

и допилили бы ему HAL до портируемости?

anonymous ()
Ответ на: комментарий от gedisdone

Пишут, что в современных компиляторах volatile игнорируется. Или это касается компиляторов C++?

в proposal по плюсам вижу «частичный отказ» http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1152r3.html

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

olelookoe ()
Ответ на: комментарий от qulinxao3

ну и да - ща всяко Control Chacters aka <’ ’ почти анохронизм - их тож можно использовать для лаконичности - т.е utf8 - входной но ограничимся ascii используя семантику только для \n \r \t 0 - остальные символы - операторы - это не для офустикации - а для упрощения компилятора что бы все лексемы символьны - 120 при рекурсии( т.е появлении локальных имён) - отдельных лексем хватит на всё - при возможности обращаться к сущьностям через /составнойобьект/егосоставноеполе - и может даже для обращения полей аргументов инстанцированных функций и их внутренних переменных - которые всяко смещения на стеке — этот абзац если только он упрощает селф-компилятор

выдохнул телетайпно. терминальное чтиво

вот это напомнило, кстати. в описании формата PDF про формат COS.

tl;dr: по сути, формат PDF это база данных, где:

  1. ascii кодирование сущностей и ключевых слов. остальное бинарное.

  2. данные типизированы: boolean/numbers/strings/arrays/dictionaries/streams/null.

  3. root object aka catalog

  4. streams бинарные, сжатые, с обработчиками-фильтрами.

  5. текст в pdf это steams. уникодный. включая кодировку и шрифт.

  6. tagged PDF для сниппетов, чанков, «блоков кода» (в litprog).

  7. catalog 3 с остальными steams 4 в COS словаре (хешмапе) показывают бинарные индексы во всём этом, есть xref. то есть, упрощено для инкрементальной обработки, например

  8. если postscript интерпретатор всего подряд форта пока не /showpage, то тут PDL тот же, а интерпретируемый язык упрощён. но можно индексироваться, адресоваться в любую страницу сразу.

  9. есть расширения типа подписей, форм, валидации, даже 3D прикрутили.

quine PDFasm можно рассматривать как Literate programming на ассемблере. обычный quine воспроизводит сам себя в один поток – поток текста, кода на объектном языке. это tangle.

weave воспроизводит поток кода на метаязыке этого языка. это документация.

то есть, если есть комбинатор неподвижной точки = quine и естественное преобразование, то далее WEB=litprog условный nuweb.py это расширение этого квайна монад трансформерами, где старый quine = tangle, новый weave это язык форматирования документации (troff/pic/eq/tbl/…метапрог DSLи, много разных…) + человекопонятная его интерпретация, перетолмачество (в смысле weave документации).

есть ощущение, что можно из PDFasm.pdf запилить WEBlitprog_forth_or_asm_or_HLA_or_whatever.pdf, который будет самодостаточен дабы раскрутить самого себя или кого-нибудь ещё.

например, метапрог.

anonymous ()
Ответ на: комментарий от gedisdone

Пишут, что в современных компиляторах volatile игнорируется. Или это касается компиляторов C++?

Это правда. Как минимум GCC и MSVC это делают для структур, которые не влазят в регистр (то есть, отличных от 1, 2, 4, 8 байт), поскольку работа со структурой с volatile и без volatile в таком случае не отличается.

Там не пишут о том, почему volatile до сих пор живо на фоне его полнейшей бесполезности? Не, в случае MSVC оно еще как-то оправдано, поскольку там volatile значит атомарный доступ - однако же, такая функция в стандарт не добавлена, хотя Microsoft, насколько мне известно, участвует в комитете.

byko3y ★★★ ()
Ответ на: комментарий от olelookoe

в proposal по плюсам вижу «частичный отказ» http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1152r3.html

Вау, я удивлен. Не прошло и 40 лет - это очень оперативное реагирование для комитета. Если бы за последние 40 лет (до принятия первого стандарта) не возникло столько кривого кода и компиляторов, которые базируются на volatile, то его бы снесли полностью, заменив переносимыми атомарными операциями и асемблерными платформ-специфичными вставками.

byko3y ★★★ ()
Ответ на: комментарий от byko3y

базируются на volatile, то его бы снесли полностью, заменив переносимыми атомарными операциями

атомарные операции не имеют никакого отношения к volatile. существует 2 типа барьеров памяти: барьеры процессора и барьеры компиляции. volatile позволяет избегать только барьеров второго типа.

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

стандарт (The cv-qualifiers [dcl.type.cv]):

[Примечание: volatile - это подсказка (hint) компилятору, говорящая что надо избегать агрессивных оптимизаций, т.к. значение объекта может быть изменено чем-то, о чем компилятор не знает. Подробнее - в главе Program execution]

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

В любом случае, это «что-то» работает в текущем потоке, а не в параллельном.

Program execution [intro.execution] :

- обращения к volatile объектам выполняются строго согласно правилам абстрактной машины (т.е. например для volatile int& x; оптимизатор не может удалить код x = x; или заменить x - x на 0, если он не может доказать что такая оптимизация сохранит поведение программы);

- доступ к volatile объекту является побочным эффектом (side effect). Побочные эффекты в одном полном выражении выполняются раньше побочных эффектов в следующем полном выражении 

Согласно стандарта,

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

olelookoe ()
Ответ на: комментарий от olelookoe

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

Это особенность даже не столько MSVC в целом, сколько архитектуры x86. Дело в том, что в x86 всегда есть барьер release на всех операциях записи, и доступ по выравненным ячейкам с размером меньше регистра - атомарен на чтение и запись. Получается, что потокобезопасность уже почти есть - просто, нет гарантии порядка чтений (фактически атомарных). В то же время, MSVC на ARM по умолчанию использует для volatile семантику свободного доступа.

Но я должен заметить, что volatile изначально разрабатывался именно и только под CISC архитектуры: intel 8080 (и производный Z80), 8086, Motorola 68000. Интересно, хоть и довольно трудоемко, узнать про компании, которые участвовали в создании стандарта C89. Возглавляли комитет Whitesmiths и AT&T. Whitesmiths были разрабами Idris на PDP-11 - того самого PDP-11, на базе которого были созданы VAX (8600, 8800), Motorola 68000, System/370, Intel 8086. А AT&T в том числе потребляла эти системы и софт, и также продавала свой AT&T 6300, базировавшийся на 8086.

В чем же такая особенность этих процессоров именно в те годы? Когда не было многоядер. А в том, что вы могли сделать «inc dword [eax]» единой командой, в промежутке которой не могли произойти прерывания — таким образом, на однопроцессорной системе вы могли делать «атомарные» операции без лишних напрягов. В RISC, однако, такой команды нет, и для увеличения ячейки памяти на единицу вам нужно считать значение в регистр, увеличить значение, записать результат в регистр - здесь на любом шаге может быть прерывание, и потому прерывания нужно временно отключать или применять дополнительные приемы (linked-load/store-conditional). Очевидно, volatile был разработан с игнорированием этой проблемы, таким образом, стандарт C89 ориентировался на узкий спектр аппаратных и программных систем еще в восьмедисятые - его устаревание уже в девяностые (с появлением ARM, SPARC, PowerPC, MIPS, и просто многопроцессорных систем) не было какой-то случайностью. Где, например, разрабы MOS 6502 и производных? Ну, то есть, почему я не вижу Rockwell или Synertek в комитете? Sun тогда продавал 68k, только-только начиная свою SPARC, причем, SunOS 4.1 из девяностых всё еще имело глобальную блокировку в ядре, будучи таким образом однопоточным в режиме ядра. Для затравочки опишу еще несколько первых по алфавиту членов комитета:

  • Alliant Computer Systems — разрабы VAX систем;
  • Amdahl — разраб IBM System/370-совместимых мейнфреймов;
  • American Cimflex и Analog Devices — к сожалению, не знаю, при чем тут они;
  • Apollo Computer — разраб рабочих станций на Motorola 68000;
  • Apple Computers — тоже Motorola 680xx;
  • Arinc — поставщик бортовых систем. Использовал никсы для своих устройств, но в остальном его роль не совсем ясна здесь
  • ...

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

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

В процессорах уже давно недопустимо использование одного лишь volatile для этих целей. Потому что кэши. Обычно просто делают «asm volatile» с нужными командами.

byko3y ★★★ ()
Последнее исправление: byko3y (всего исправлений: 4)
Ответ на: комментарий от byko3y

Я не понял твоих претензий й к strict aliasing. Ведь именно его-то и сделали расширением стандарта - через restrict.

Что до volatile, то, очевидно, что тут был очень частный случай против общего, так что да, стандарт 88-89 годов ВНЕЗАПНО сломал обратную совместимость с мутными реализациями конца 70-ых в этом, как и во многих других вопросах, в пользу простоты использования.

С чего бугурт-то?

LamerOk ★★★★★ ()
Ответ на: комментарий от LamerOk

Ведь именно его-то и сделали расширением стандарта - через restrict

У меня вообще нет никаких претензий к restrict. Но ты не находишь забавным, что в моем примере ( О божественном C и введение в него через ed (комментарий) ) не было никакого restrict, однако же, оптимизатор умудрился сломать алгоритм? Провал strict aliasing в GCC заключается именно в том, что оно агрессивно - оно ломает работавший хорошо отлаженный код, потому что кому-то, видите ли, захотелось поиграться в следование стандартам.

стандарт 88-89 годов ВНЕЗАПНО сломал обратную совместимость с мутными реализациями конца 70-ых в этом, как и во многих других вопросах, в пользу простоты использования

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

byko3y ★★★ ()
Ответ на: комментарий от byko3y

оптимизатор умудрился сломать алгоритм

оптимизатор? ))

девелопер под вдохновением от неизвестно каких веществ вывалил кучу UB кода, а виноват, конечно же, оптимизатор. л - логика.

оно ломает работавший хорошо отлаженный код

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

olelookoe ()
Ответ на: комментарий от byko3y

«Простота» — это когда тебе не нужно писать никаких бессмысленных модификаторов у указателей

нагибание раком всех кодеров поголовно

Где используется volatile

  1. static volatile объекты представляют MMIO, static const volatile представляют MMI. такие например как часы реального времени.
  2. static volatile объекты типа sig_atomic_t используются для связи с обработчиками сигналов .
  3. volatile переменные, которые являются локальными для функции, которая содержит вызов макроса setjmp, являются единственными локальными переменными, которые гарантированно сохранят свои значения после возврата longjmp.
  4. Кроме того, volatile переменные могут использоваться для отключения определенных форм оптимизации, например для dead store elimination или constant folding для микробенчмарков.

ну короче, считанное количество очень специфичных случаев. настолько специфичных, что in general, you shouldn’t need to write volatile in your Linux kernel code.

чтобы чей-то там компилятор выдал на 10% быстрее код.

вообще-то, исходя из твоих собственных бенчей, прирост составляет >30% . лично мне приходилось бороться и за меньшее. да и хрен с ним, правда? зато volatile не встретится больше в коде нигде и никогда )

olelookoe ()
Ответ на: комментарий от byko3y

Но ты не находишь забавным, что в моем примере ( О божественном C и введение в него через ed (комментарий) ) не было никакого restrict, однако же, оптимизатор умудрился сломать алгоритм?

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

В этом, как бы, сам смысл оптимизации.

И эта оптимизация ВНЕЗАПНО не совместима с UB (кроме полутора общеизвестных исключений, вроде реализации offsetof).

LamerOk ★★★★★ ()
Ответ на: комментарий от byko3y

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

А здесь вообще передёргивание каких-то космических масштабов.

создает ложную уверенность в неизменяемости данных,

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

Почувствуйте разницу, она есть!

LamerOk ★★★★★ ()
Ответ на: комментарий от olelookoe

девелопер под вдохновением от неизвестно каких веществ вывалил кучу UB кода, а виноват, конечно же, оптимизатор. л - логика

Если код 20 лет был не UB, а потом стал UB — каким образом в этом виноват разраб? Более того, нет надежного способа это «UB» отловить на этапе компиляции, поскольку стандарт разрешает почти неограничено превращать указатели — добавление такого UB в язык почти преступно. Я первый раз когда прочитал параграф про превращение указателей - у меня вообще глаза на лоб вылезли: компилятор каким-то магическим образом должен проверять код не по превращению указателей, не по типу переменных - он должен делать проверку по фактическому доступу к памяти. В кислотных трипах членов комитета это было просто - но как должен это делать реальный компилятор, если указатели могут передаваться между функциями, проходя неограниченные касты? На вот этот код О божественном C и введение в него через ed (комментарий) хотя бы один компилятор ругается? Как минимум GCC прожевал его без единого предупреждения.

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

byko3y ★★★ ()
Ответ на: комментарий от olelookoe

Где используется volatile

Я снова напоминаю, что основной мой аргумент против volatile был о том, что оно дает карт бланш на кривые оптимизации, поскольку как бы позволяет исправляет возникающие из-за этих оптимизаций ошибки работы бинарников, то есть «ломайте — мы потом всё исправим». Более того, я приводил пример компиляторов паскаля, которые прекрасно оптимизируют и при этом позволяют работать с железом, сигналами, и общей памятью безо всяких volatile. Вот элементарный пример, где в программу на паскале добавляют прерывание, и компилятор сам принимает решение о необходимости оптимизации в зависимости от кода, всегда выдавая корректную программу:
https://forum.lazarus.freepascal.org/index.php/topic,38961.msg266921.html#msg...

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

вообще-то, исходя из твоих собственных бенчей, прирост составляет >30%

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

byko3y ★★★ ()
Ответ на: комментарий от LamerOk

Нет, так как включив оптимизацию ты отказался от гарантий, что каждая строка исходного кода будет транслирована в инструкции целевой машины 1:1
И эта оптимизация ВНЕЗАПНО не совместима с UB

И как же ты собрался найти это UB? Я представляю позицию прагматичного кодера, потому у меня возникает простой и логичный вопрос: как мне избежать UB, если взятие указателя на «const» от переменных данных является абсолютно легальным, если я могу делать "(struct S*)(ptr + 123)", получая работающий код, но при этом должен помнить, что на каких-то компиляторах с какими-то опциями этот код может внезапно перестать работать, потому что какие-то там поля пересекутся, и я должен сам лично за этим проследить?

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

byko3y ★★★ ()
Ответ на: комментарий от LamerOk

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

Вообще-то «If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined». Потому все-таки const был про неизменяемые входные данные. Это уже потом, после принятия стандарта, выяснилось, что в реальном коде добиться этого практически не получится, поскольку куча кода уже сейчас нарушает это правило.

byko3y ★★★ ()
Ответ на: комментарий от qulinxao3

кроссплатформные ассемблеры

внезапно, не такие уж и плохие ассемблеры потомки JWasm: asmc и uasm, алсо до кучи, есть ещё такой MasmBasic

оба потомки JWasm, который переписанный Wasm из OpenWatcom. то есть, умеет генерировать PE32/62, ELF32/64, MachO «из коробки». в watcom-е переписали все тулзы «сами на себе», с 0 зависимостями от обычных libc. поэтому оно из коробки кроссплатформное.

внутри исходников asmc есть, например, dz – файловый менеджер-двухпанельник типа нортона, фара и т.п.

на ассемблере. если посмотреть внимательно, то вот это обкоцанный libc для dz. а вот это crtl.inc по сути прообраз обкоцанного c runtime library для самого asmc.

далее можно читать исходники того же dz. как пример приложения хотя и на ассемблере, но с HLL (high-level language) расширениями. все вместе они составляют стандартную библиотеку.

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

типа как FreshLib из Fresh IDE на fasm, только тут – потомки JWasm. есть макросы типа CStr(«blabla»), (создать строку и поместить в сегмент данных, сам макрос в вызове вывода строки по месту). в итоге выглядит всё довольно похоже на сишечку.

но ассемблер.

uasm написан на си полностью (asmc в основном на ассемблере, сам на себе).

в нём появились дополнительно ООП расширения.

Masm Basic показывает, как можно макросами ассемблера накостылять свой велосипедный «типа бейсик». который по сути транслируется в ассемблер. это ещё макросы masm-а, в котором тоже есть HLL штуки – но не настолько продвинутые, как в uasm/asmc.

типа PureBasic-а, который компилировался в fasm. только тут в макросы Masm-а.

вообще далее можно брать например jonesforth и переписать на этом, uasm с HLL макроснёй с ООП расширениями. и родить в итоге стандартную библиотеку, кроссплатформную везде: Win32/64, Lin32/64, MacOSX32/64.

потом на этом форте написать асм. потом написать паскаль, и какой-то web Дональда Кнута. потом на нём написать всё остальное.

jonesforth в целом понятно откомментированный, но не очень-то кроссплатформный. на uasm или asmc можно сделать форт покроссплатформнее .

anonymous ()
Ответ на: комментарий от byko3y

как мне избежать UB

Прекратить стрелять себе в ногу из крупнокалиберного пулемёта.

не столько кодер пишет UB, сколько компилятор провоцирует UB

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

LamerOk ★★★★★ ()
Ответ на: комментарий от byko3y

Вообще-то ... Потому все-таки const был про неизменяемые входные данные.
Это уже потом, после принятия стандарта, выяснилось

Не надо фантазировать, и излагать какие-то свои левые догадки.

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

Ровно для этого задумывался и ровно это значит const. То, что const'ом можно положить конкретные переменные в .rodata, - это лишь детали реализации конкретных платформ.

LamerOk ★★★★★ ()
Ответ на: комментарий от anonymous

всё это было ещё в оптимистичных 60х! а потом словно какая-то разруха, деградация и mass total упрощение началось: Multics в unics, PL/I в сишечку (ублюдочный потомок BCPL), нормальные идеи и реализацию исходников Multics: Emacs, Lisp и т.п. – в костыли и огрызки.

Я тебе больше скажу CISC в RISC. Костыли и огрызки, да.

no-such-file ★★★★★ ()
Ответ на: комментарий от LamerOk

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

Плохая аналогия с бензопилой. Я работал бензопилой, болгаркой, циркуляркой. Strict aliasing - это скорее предложение нацепить на болгарку циркулярочный диск и снять защиту, чтобы быстрее резать и снизить вес инструмента — оптимизация, так сказать. Почему так? Потому что strict aliasing создает потенциал для написания UB, которое нельзя обнаружить инструментами, которое тяжело обнаружить читая код, и которое никогда не проявляется в отладочных сборках. И это всё при том, что strict aliasing дает незначительный прирост производительности кода, около 2-5%.

По этой причине монополист на рынке десктопной C/C++ разработки в девяностых — MS, принципиально отказался от реализации strict aliasing, добавил атомарную семантику для volatile, а также ввел безопасные функции для работы со строками strcat_s, strcpy_s, strncpy_s и их аналоги для юникода. «Эта причина» - повышение надежности работы кода при незначительном изменении его производительности.

byko3y ★★★ ()
Ответ на: комментарий от LamerOk

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

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

byko3y ★★★ ()
Ответ на: комментарий от no-such-file

Я тебе больше скажу CISC в RISC. Костыли и огрызки, да

Ты это серьезно или шутишь? В 2020 абсолютно все процессоры работают на RISC наборе инструкций - разница лишь в том, транслируют ли они устаревшие CISC инструкции в родные или исполняют RISC напрямую.

byko3y ★★★ ()
Ответ на: комментарий от byko3y

Вообще-то «If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined». Потому все-таки const был про неизменяемые входные данные.

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

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

olelookoe ()
Ответ на: комментарий от byko3y

Strict aliasing - это скорее предложение

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

«я так привык! я художник - я так вижу!» ну ок.

olelookoe ()
Ответ на: комментарий от qulinxao3

а в техническом плане - что значит устаревшее?

Дело в том, что CISC неплохо подходила для миникомпьютеров и ПК восьемедисятых - однопоточных машин архитектуры Фон-неймана низкой производительности, которые выполняли строго один шаг за раз. Проблему низкой производительности пытались решать построением суперскалярной машины уже в 1966 году, но неизбежное усложнение такой конструкции сдерживало развитие этого направления. Intel 8086 содержал 30 тыс транзисторов, Motorola 68000 - 68 тысяч соответственно.

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

Именно первые RISC процессоры конца 80-х приобрели суперскалярную реализацию в нищесегменте, и именно поэтому они обогнали CISC машины по производительности. Сам Intel пытался заскочить на едущий поезд, создав свои RISC процессоры, но в итоге смог выехать за счет банальной трансляции CISC в RISC. Однако, CISC архитектура оставила себе большой груз - это требование соблюдения наблюдаемой последовательности выполнения сложных команд, которой нет у RISC архитектуры. Даже PowerPC с поддержкой Total Store Order в этом плане устроен проще, потому что на этой архитектуре нет необходимости заморачиваться с поддержкой сложных команд.

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

byko3y ★★★ ()
Ответ на: комментарий от byko3y

трансформация мультипликативного аддитивным

в твоём случае замена сложнокомандного CISC потока с командами разной длины в тактах и усложнённой моделью переходов состояний единого объекта CPU на агрегат RISC ядер где общее состояние CPU есть произведение состояний ядер

qulinxao3 ()
Ответ на: комментарий от qulinxao3

CPU на агрегат RISC ядер где общее состояние CPU есть произведение состояний ядер

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

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

byko3y ★★★ ()
Ответ на: комментарий от anonymous

CISC подразумевает безупречно последовательное выполнение

Нет. Не надо натягивать сову на глобус.

Нет да. Именно отсюда возник total store order. Конечно, современные процессоры пытаются распараллелить операции для ускорения, но должны при этом старательно мимикрировать под последовательное выполнение.

byko3y ★★★ ()
Ответ на: комментарий от byko3y

два независимых автомата нельзя абстрагировать до одного автомата

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

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

anonymous ()
Ответ на: комментарий от byko3y

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

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

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

anonymous ()
Ответ на: комментарий от anonymous

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

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

byko3y ★★★ ()
Ответ на: комментарий от anonymous

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

a = 1
b = 2
c = 3

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

byko3y ★★★ ()
Ответ на: комментарий от byko3y
a = 1
b = 2
c = 3

На счет «однозначности преобразования» я, конечно, переборщил (но я подстраховался, написав в начале «вроде»).

Суть была в следющем, что должно сводится к последовательному выполнению.

anonymous ()