LINUX.ORG.RU

Функциональщина на C++

 , ,


0

5

По мотивам: Си с классами

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

Как эффективно учиться? (комментарий)

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

Конкретно мне интересен функционально-процедурный подход к написанию кода на крестах, что-то похожее на Rust, только без абсурдной помешанности на безопасности памяти, так сказать «си с плюшками», но совсем НЕ «си с классами», как было в упомянутом треде. Для примера: Qt и UE — это примеры плохой архитектуры в данном контексте. Например, fstream — это плохая реализация файловых операций, поскольку скатывается в классы и исключения, в ней даже нельзя без исключений получить конкретную ошибку файловых операций.

Итак: какие есть конкретные хорошо проработанные приемы и библиотеки для писания на крестах в функционально-процедурном стиле?

★★★★

Ответ на: комментарий от EugeneBas

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

Ну слава тебе, Железный Господин! Разобрались)))

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

Не верю. :)

Хорошо, «матан». В переносном смысле, как общее название математики. Там теория вероятностей, много её, машины Тьюринга, очень много их всяких, опять же. Универсальные языки описаний на основе МТ и куча всяких забавных теорем про вот это вот всё. Мемория, кстати, на AIT (теории алгоритмической информации) построена, так как использует соответствующие принципы кодирования информации.

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

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

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

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

Речь идет про упорядоченность доступа к памяти, что ли?

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

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

в том, что при моделировании что-то не учли и неправильно выбрали абстракции

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

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

Я открою лорошкольником страшную тайну: виртуальные адресные пространства, защита памяти и векторные инструкции появились ДО возникновения языка Си

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

Ты и правда думаешь, что за 70 лет не было любителей слепить аппартных лисп-машин?

Много чего было. DEC Alpha умерла — и что теперь, плохая архитектура была? Интелю понадобилось лет пять, чтобы приблизиться к ее производительности — и это на фоне самого быстрого в истории роста производительности вычислителей.

byko3y ★★★★
() автор топика
Ответ на: комментарий от EugeneBas
  1. под рукой имплементации с++20 нет, чтобы посмотреть что там

https://github.com/microsoft/STL/commit/834baa6acacb8325367e255b0728c582c7e443ff

или

https://github.com/gcc-mirror/gcc/commit/ecba8547dd398ad4b627756013dbd22be417d4da

LLVM libc++ пока не реализовали…

Да я просто grepом прошёлся по stl, посмотреть сколько там dynamic_castов найдёт :)

4 штуки, не так много…

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

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

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

Пиком развития машины Тьюринга стал именно язык Си

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

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

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

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

но к идеалу надо стремиться.

Есть другая мудрость: не браться за физически нерешаемые задачи.

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

Но мы друг-друга поняли, спасибо! :)

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

Си - это типизированная дочка безтипового https://en.wikipedia.org/wiki/B_(programming_language) , который в свою очередь клон BCPL, где никакого ассемблера даже близко не было

Знакомься — первый «компилятор» Си, написанный на Си пополам с асмом:

https://github.com/mortdeus/legacy-cc/blob/master/last1120c/c02.c
https://github.com/mortdeus/legacy-cc/blob/master/last1120c/c03.c
https://github.com/mortdeus/legacy-cc/blob/master/last1120c/c10.c

Принимает на вход сорцы, через printf выдает ассемблерный текст. Про BCPL и B я не знаю, их сорцов не видел.

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

Пиком развития машины Тьюринга стал именно язык Си

Боюсь, пиком развития этой дискуссии стал этот тезис.

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

Да погугли уже в яндексе, едрить твою налево. Обратная совместимость на уровне машинных кодов - практически норма для любых широко распространённых архитектур, вплоть до PS2, исполняющей код PS1

При чем тут PS1/2? Это нулевые годы, а мы говорим про эпоху зарождения компьютеров.

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

Пиком развития машины Тьюринга стал именно язык Си

Боюсь, пиком развития этой дискуссии стал этот тезис

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

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

невозможно реализовать на машине Тьюринга?

Но можно реализовать на МТ с прерываниями или многопотоком (несколькими головками).

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

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

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

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

Более того, exactly-once семантика доставки сообщений требует по сути реплицируемой базы данных, в которой будет храниться информация о доставке и обработке сообщения

Сомневаюсь в острой необходимости модели «только однажды».

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

первый «компилятор» Си, написанный на Си пополам с асмом

Я с ним знаком.

Про BCPL и B я не знаю

Это я уже понял. Я вообще понял, что ты ещё много не знаешь, но уже любишь «напоминать» свои наглухо отбитые от реального мира фантазии.

написанный на Си пополам с асмом: пополам с асмом:

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

С таким уровнем владения вопросом тебе бы в read only посидеть пару тройку годиков. пока мозгов не наберёшься.

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

Сомневаюсь в острой необходимости модели «только однажды».

Блокировочки как? А без блокировочек никак. Не всё в lock-free делается. И не всё, что делается в принципе, работает достаточно хорошо в реальности. :)

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

Я тебя удивлю

Я на лоре больше 20 лет уже. Ты меня вряд ли чем-то удивишь.

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

И? Контрпример будет тогда, когда у тебя все записанные данные никто не будет никогда нигде читать, расчитывая получить оттуда записанное. Хоть фп, хоть декларативное, хоть метапрог.
Пока у тебя нет такого, у тебя нет контрпримера

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

Да насрать на ЯП и их уровни абстракций. Консистентность данных - это базовое требование к любым вычислениям. 2+2 должно давать 4, иначе аппаратуру и все языки программирования для неё можно засунуть туда, куда не светит солнце

2+2 = 4 можно сделать вообще не имея оперативной памяти. Вон, китайские микроконтроллеры со считанными байтами памяти работают, и им норм.

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

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

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

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

aist1 ★★★
()

Короче, мы отвлеклись. Я тут пока с темы про dynamic_cast и МТ сольюсь.

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

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

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

В С++20 добавили thread_safe обёртки над потоками.

https://en.cppreference.com/w/cpp/io/basic_osyncstream

Они обычно передают данные в деструкторе. Или когда делают emit. (захватывают mutex и безопасно передают данные в родительский буфер)

fflush они откладывают.

Но это можно изменить.

synced_out.rdbuf()->set_emit_on_sync(true);

Но эти basic_osyncstream наследуются от обычных basic_ostream.

И их можно передавать в функции, которые ждут basic_ostream.

Но у basic_ostream.rdbuf() нет метода set_emit_on_sync.

Поэтому есть манипуляторы:

out << std::emit_on_flush;
out << std::noemit_on_flush

Они не делают ничего, если это обычный basic_ostream.

И делают

out.rdbuf()->set_emit_on_sync(true);
out.rdbuf()->set_emit_on_sync(false);

если out это basic_osyncstream.

Вот для этого и нужен dynamic_cast.

Но да.

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

отдельно для basic_ostream (считать, что это не thread_safe)

и отдельно для basic_osyncstream.

Тогда эти манипуляторы будут не нужны…

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

Справедливости ради, нижележащей моделью для С является не MT, а архитектура фон-Неймана (машина с хранимой программой)

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

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

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

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

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

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

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

Вот это не асм:

https://github.com/mortdeus/legacy-cc/blob/master/last1120c/cctab.s

И вот это не асм:

https://github.com/mortdeus/legacy-cc/blob/master/last1120c/regtab.s

Или Ъ даже если прошел по ссылке — не будет пытаться вникать в написанное там? Как же тогда

первый «компилятор» Си, написанный на Си пополам с асмом

Я с ним знаком.

?

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

Блокировочки как? А без блокировочек никак. Не всё в lock-free делается

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

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

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

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

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

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

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

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

Это просто шардинг. Попробуй представь, почему мы не разбиваем систему на «миллиарды» (произвольное, не зависящее от данных число) шардов, и увидишь, почему дробление на домены работает до какого-то предела, и не далее.

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

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

Кому «им» искать? Блокировкам? Прямо сейчас на твоем компьютере есть куча программ, которые работают, не парясь про целостность мало интересующих эти программы состояний. Например, можно удалить bookmarkbackups у Firefox, и ему от этого не станет ни лучше, ни хуже. В файловой системе вообще такой бардак творится, что мама не горюй. потому что часто программы изменяют файлы безо всяких блокировок, а значит несколько программ могут изменить один файл с непредсказуемыми последствиями — но никто об этом не парится, не вводит CRDT семантику в ОС/ФС, со слезными просьбами и без них. Единственное что — это ОС проверяет конфликты имен файлов и не дает удалять непустые каталоги.

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

Попробуй представь, почему мы не разбиваем систему на «миллиарды» (произвольное, не зависящее от данных число) шардов, и увидишь, почему дробление на домены работает до какого-то предела, и не далее

Потому что система и ее утилиты написаны на C/C++, и потому программировать такую систему будет очень сложно. А теперь представь, что программирование системы строится на цикле поиска отклонения от целевого состояния и постепенно устранение этого отличия. Примерно как Virtual DOM в React.js, ЕВПОЧЯ. Пример чуть ближе к низкоуровневщине — это почти любой lock-free алгоритм, который как раз и представляет собой несколько таких циклов, которые заканчиваются при достижении целевого состояния. Но этот подход можно расширить и построить вообще всю систему на базе такого цикла. В гуях же есть главный цикл.

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

Кому «им» искать? Блокировкам?

Работодателям. Я такие вещи буду делать только если мне скажут, что «я хучу выжать все по максимуму из своих датацентров, и я представляю, сколько мне это будет стоить». Потому что с хотелками у всех ого-го, а вот с бюджеты уже разбазарены (если вообще и были). Поэтому, честные и откровенные разговоры про web-scale заканчиваются на «ладно, нам и postgresql с репликацией тут хватит, возьмем машину побольше».

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

Пока мне не скажут, что «мы хотим именно продвинутых техник для достижения максимальной утилизации оборудования», я даже смотреть в этом направлении не стал бы. И другим не советовал бы. Scale-UP до тех пор, пока можно. И только потом Scale-Out.

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

Потому что система и ее утилиты написаны на C/C++, и потому программировать такую систему будет очень сложно.

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

Примерно как Virtual DOM в React.js, ЕВПОЧЯ.

Я понимаю, о чем ты. Ты понимаешь, что ФСД не являются lock-free в строгом смысле? В самом лучшем случае там будет куча блокировок/атомиков в сборщике мусора. Ты можешь реализовать распределенный сборщик мусора на идемпотентных распределенных атомарных счетчиках, но это уже довольно большое пространство разделяемого состояния. И уж ты точно не можешь реализовать распределенный децентрализованный сборщик мусора. По организационным соображениям.

Ну и в большинстве случаев тебе понадобится блокировка (строгая консистентность) на «голове» истории, как минимум. Если не на всей истории версий.

Все преимущество ФСД/ПСД в том, что как только новая версия создана, дальше вся работа будет идти уже в wait-free режиме, т.е. вообще без блокировок и атомиков до самого коммита, а данные версий могут свободно кэшироваться как угодно, потому что они иммутабельные. Но работа воркеров с историей требует транзакций. Метаданные версий должны становиться известными воркерам, и тут без транзакций не обойтись.

А так — да. Я даже такую распределенную систему делал. Где заменял в реакт-подобной («распределенный flux/redux») системе immutable.js на Меморию. И оно работало именно так, как ты говорил, только с теми нюансами, которые я привел))

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

иначе ты обречен на полный отказ от стандартной библиотеки

Не велика потеря. Там мусор по большей части. Крупные проекты пишут свои аналоги.

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

Там мусор по большей части

Там был мусор. Сейчас качество растет. Но всё равно библиотеке еще развиваться и развиваться.

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

Поэтому, честные и откровенные разговоры про web-scale заканчиваются на «ладно, нам и postgresql с репликацией тут хватит, возьмем машину побольше»

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

Точно также сделать PostgreSQL с нуля сильно дороже, чем написать на коленке свою СУБД под свои задачи. Но постгрес уже есть, да еще и бесплатный, а своей СУБД еще нету. Одна из моих идей как раз заключалась в том, чтобы создать готовую распределенную универсальную СУБД, которую просто взял, настроил, и пользуешься — сразу получается и масштаб, и надежность, и производительность, и без постгреса. Как я уже упоминал, гарантии целостности и атомарности почти никому не нужны, даже банкам, которые раз в день по ночам неспешно закрывают взаиморасчеты. Причем, некоторые из подобных процессинговых систем вполне себе работают на самопальных БД в коболе без каких-либо намеков на ACID.

А иначе можно и на SQLite делать систему. Причем, это весьма достойный и недооцененный вариант, поскольку предоставляет максимальную целостность и атомарность, которой нет даже у MySQL/PostgreSQL/MS SQL/Oracle. И ты, между прочим, сам забыл, что PostgreSQL тоже является отходом от цельного состояния в сторону разрозненных снимков, пусть и небольшим.

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

Части стандартной библиотеки С++ не требуется C++ рантайм.

bit
cassert
cctype
cerrno
cfenv
cfloat
cinttypes
climits
clocale
compare
concepts
coroutine
csetjmp
csignal
cstdarg
cstddef
cstdint
cstdio
cstdlib
cstring
ctime
cuchar
cwchar
cwctype
initializer_list
limits
numbers
ratio
source_location
type_traits
utility
version

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

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

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

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

О чем это я? Тебе в любом раскладе понадобится единственный опорный пункт как источник последней истины по некоторому вопросу. Ему не обязательно знать всё состояние, а хотя бы ту «голову истории», которую ты упомянул. И при всем при этом остальное функционирование системы может происходить как угодно, с какими угодно задержками, и при этом не нужно никаких хитровымудренных протоколов распределенных транзакций для эмуляции никому не нужного и принципиально невозможного на распределенных системах (но не на одном узле) ACID. Почему ACID возможен на одном узле? Потому что в случае проблем этот узел, как правило, не отваливается частями, а падает весь целиком. То есть, у него «атомарное» состояние «исправен» или «неисправен». При повышении требований к надежности одного узла и игнорировании частичных отказов в нем ACID становится невозможен даже на одном узле.

В самом лучшем случае там будет куча блокировок/атомиков в сборщике мусора

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

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

Части стандартной библиотеки С++ не требуется C++ рантайм.
В основном это враперы над хедерами C, но не только

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

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

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

Я привёл список заголовочных файлов, которые не требуют рантайма.

Вот, например:

https://en.cppreference.com/w/cpp/header/numbers

https://en.cppreference.com/w/cpp/header/type_traits

два хороших заголовочных файла, которые нет смысла реализовывать самому…

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

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

Ну да. В чём-то ты прав. Одно дело, когда требуются аллокации. Тогда понятно, и исключения ожидаемы тоже. Но несколько жаль, что string_view и optional, variant требуют рантайма, хотя и не требуют аллокаций :(

Из-за: https://en.cppreference.com/w/cpp/string/basic_string_view/at

и https://en.cppreference.com/w/cpp/utility/optional/value

и https://en.cppreference.com/w/cpp/utility/variant/get

и т.п.

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

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

Я просто оставлю это тут. ))

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

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

А иначе можно и на SQLite делать систему. Причем, это весьма достойный и недооцененный вариант

Кстати, https://www.actordb.com/.

SQLite сейчас почти идеальная встраиваемая SQL OLTP DB.

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

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

Но нужны атомики, а для них — сильная консистентность.

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

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

Я просто оставлю это тут

Мало ли говнософта в мире? Я бы в том случае поднял вопрос «на каком основании вообще осуждались люди»? Вот в штатах всё просто и весело — ты осужден потому, что мне так захотелось. А дальше ты уже прыгаешь и выкручиваешься, чтобы найти деньги на адвоката. Деньги, которых у тебя нет, потому что ты осужден за кражу. А вы думали, что только в России закон на стороне тех, у кого больше денег? Я подозреваю, что в бриташке ситуация не сильно отличается.

А есть задачи, типа сборщика мусора, которым нужна строгая консистентность. И нужны соответствующие базы данных. Поэтому у нас состояние кластера хранится в ZooKeeper/Etcd, а не в Cassandra

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

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

Кстати, https://www.actordb.com/

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

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

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

Но несколько жаль, что string_view и optional, variant требуют рантайма, хотя и не требуют аллокаций :(

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

#include <string_view>
using namespace std;

extern "C" void __gxx_personality_v0() {}

int main() {
	const char *c = "djfkdjfk";
	string_view v(c);
	int sz = v.size();
}

$ gcc 1.cc

Вставил заглушку __gxx_personality_v0() - видимо где-то в недрах есть catch блок (но throw нет, иначе нужна __cxa_throw).

Ну а если свои аллокаторы, то можно и vector заюзать. Если памяти не хватает, то можно попробовать сохранять образ процесса через CRUI, ну а там «втыкай RAM» и пробуй снова.

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

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

Но нужны атомики, а для них — сильная консистентность

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

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

Ты про механизмы переиспользования памяти epoch-based/quiescent-state? Это более-менее универсальные методы (достаточно) быстрого и точного переиспользования памяти, но в системе с пониженной целостностью можно использовать критерий «все агенты какое-то время назад проявили признаки отсутствия ссылок на старые данные». Вроде классического «ID самой старой активной транзакции». В системе с пониженной целостностью это будет звучать как «ID самой старой из известных старых транзакций» — очевидно, что старые транзакции не могут внезапно появиться из ниоткуда спустя какое-то время, на то они и старые. Чтобы не перегружать систему обменом инфы между агентами, эти оповещения группируются и передаются изредка, а не на каждый чих.

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

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

Не, про CAP-теорему тут не стоит. Её еще понимать надо правильно.

Ладно. Если говорить строго (хотя бы в CAP-стиле), то у тебя нет ни одного надежного компонента даже в одномашинной системе. Которая, к слову, тоже распределенная. Просто «тесный» кластер.

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

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

И есть частные случаи. Маленькие кластеры из 3-5 машин многоядерных машин (или стоек, не важно) будут достаточно надежны, чтобы обеспечить весьма высокую производительность в рамках сильной консистентности. Конечно, если ты в облаке, то всё будет сильно печальнее в сравнении с теми боярами, которые сидят на своем железе.

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

Гораздо проще ослабить консистентность изначально сильно консистентного хранилища, чем костылять усиление консистентности над EC-хранилищем. Хотя последние и быстрее.

aist1 ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.