Гепардово гнездо
637 subscribers
41 photos
1 file
81 links
El nido del guepardo.

@gepardius тащит в гнездо всякое интересное из мира программирования (и не только).

Etaoin shrdlu cmfwyp!

Еще один мой канал: @sofcheck.
Download Telegram
Перемещение в самого себя в C++

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

Foo &operator=(const Foo &other) {
if (this == &other) {
return *this;
}
// ...
return *this;
}

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

std::vector<int> v{1, 2, 3, 4};
v = std::move(v);
std::cout << v.size() << std::endl;

Думаете, что этот код выведет 4? А вот и нет! При компиляции в GCC под libstdc++ мы получим вывод 0 (т.е. вектор опустеет)! То есть, авторы STL не считают, что перемещение объекта самого в себя должно быть эквивалентно no-op и при реализации вектора не костылили этот случай.

Почему так? Пойдем на StackOverflow и возьмем оттуда цитату из Стандарта:

Each of the following applies to all arguments to functions defined in the C++ standard library, unless explicitly stated otherwise.
- ...
- If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to this argument. [Note: If the parameter is a generic parameter of the form T&& and an lvalue of type A is bound, the argument binds to an lvalue reference (14. 8. 2. 1) and thus is not covered by the previous sentence. — end note] [Note: If a program casts an lvalue to an xvalue while passing that lvalue to a library function (e.g. by calling the function with the argument move(x)), the program is effectively asking that function to treat that lvalue as a temporary. The implementation is free to optimize away aliasing checks which might be needed if the argument was an lvalue. — end note]

Здесь приводится более общее утверждение: считается, что если функция из STL принимает rvalue-ссылку на объект, то ссылка считается уникальной. А в случае перемещения в самого себя нам надо еще иметь вторую ссылку на тот же объект: то, куда мы присваиваем.

Стоит отметить, что правила выше относятся только к STL, но не к пользовательскому коду. Но если перемещение в себя не работает даже со стандартными классами, то есть ли вообще какой-то смысл поддерживать этот случай в своем коде?
🔥8🤡2👍1
Хаос в английском произношении

Dearest creature in creation
Studying English pronunciation,
I will teach you in my verse
Sounds like corpse, corps, horse and worse.
...
Hiccough has the sound of сup...
My advice is: GIVE IT UP!

The Chaos — стихотворение на английском от голландца по имени Gerard Nolst Trenité, которое наглядно показывает, насколько произношение в английском неконсистентно с написанием (My advice is: GIVE IT UP!). Оно существует в разных версиях, но смысл один и тот же: демонстрируются слова, которые пишутся очень похоже и, казалось бы, должны похоже читаться, но нет :) Еще в стихотворении иногда рифмуются слова, которые совсем по разному пишутся (например, Banquet is not nearly parquet, \\ Which exactly rhymes with khaki.)

Почитать дополнительно про стихотворение можно, например, на Википедии.

Текст можно найти здесь и в PDF-ке вместе с транскрипцией. Послушать можно здесь (аудио с транскрипцией) или здесь (видео на Youtube). Осторожно: все версии несколько отличаются друг от друга.

Так что, если хочется perfect english pronunciation, то можно взять или выучить это стихотворение :) Или просто прочитать и посмотреть на свое количество ошибок.
👍9🔥3😱2🐳2
Про std::launder()

Если кто-то утверждает, что хорошо знает C++, то я прошу его объяснить, зачем нужен std::launder() :) Даже если открыть cppreference и почитать, понять это оказывается непросто. А написано там вот что:

> Returns a pointer to the same memory that p points to, but where the referent object is assumed to have a distinct lifetime and dynamic type.
> Formally, given
> - the pointer p represents the address A of a byte in memory
> - an object X is located at the address A
> - ...
> Then std::launder(p) returns a value of type T* that points to the object X.

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

Почему так? Можно почитать вот в этой замечательной статье:
https://blog.panicsoftware.com/objects-their-lifetimes-and-pointers/
Там же раскрываются и связанные вещи: reinterpret_cast, union'ы, type punning, std::launder, объекты в понимании C++ (это не то же самое, что экземпляры класса из мира ООП!)
🤯6👍4🔥1🤔1💩1
Вдогонку к предыдущему посту.

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

Таким образом, даже если хранимые в указателях адреса одинаковые, а «теги» разные, то их сравнение вполне себе может вернуть false (вообще, это UB сравнивать такие указатели). Иногда похожие проблемы стреляют в неожиданных местах: например в Rust (который основан на LLVM, как и Clang) обнаружилась бага, где два одинаковых целых числа (полученных путем каста указателя в число) оказываются не равны друг другу.

Еще по этой же причине невалиден reinterpret_cast между разными типами: в указателе есть «тег», что он указывает на объект одного типа, но при этом мы по этому же указателю пытаемся прочитать объект другого типа. Возникает UB. Впрочем, для unsigned char и еще нескольких подобных типов есть исключение: по указателю на unsigned char можно обращаться к объекту любого типа и читать его побайтовое представление. Такое исключение иногда приводит к интересной просадке производительности: например, вот.

Вообще, тема указателей — вещь очень интересная и непростая :)
👍11
Итак, немного моего кода (и не только) поехало в open source :)

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

Пост с кучей букв здесь:
https://habr.com/ru/company/yandex/blog/721526/

Репозиторий с кодом здесь:
https://github.com/ytsaurus/ytsaurus

Добавлю лишь, что в плане кода проект очень велосипедный самодостаточный: там есть и свои stackless корутины, и свой формат данных (YSON), и куча библиотек для метрик, RPC, логирования, сериализации и всего на свете. Причем написано все довольно качественно и под high performance (чего, как мне кажется, было бы достичь сложнее с внешними библиотеками)

А про архитектуру и возможности можно почитать по ссылке выше :)

В общем, я очень рад, что я работал в команде YTsaurus, и что такой крутой проект наконец-то публично доступен :)
🔥18👍10🤯3
2303.12712.pdf
7 MB
https://arxiv.org/pdf/2303.12712.pdf

Интересная статья с обзором возможностей GPT-4 от OpenAI ClosedAI. Написано куда интереснее, чем официальный репорт (и еще с кучей примеров!) В статье показывается, как GPT-4 справляется с разными задачами и где он превосходит ChatGPT (спойлер: много где):

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

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

У авторов есть интересная идея, который у меня тоже какое-то время назад возникала: что все большие текстовые модели — это хорошая реализация Системы 1 по Канеману (которая про ассоциативное быстрое мышление), а Системы 2 (которая про медленное мышление) им пока не хватает. В частности, в разделе 8 в статье наглядно показывается, что GPT-4 мыслит «линейно», дополняя постепенно уже существующий текст, а откатиться назад, откинуть решение, перебрать варианты и начать думать с нуля, в отличие от людей, она не может.

И главный вывод такой (выделение добавлено мной):
> The central claim of our work is that GPT-4 attains a form of general intelligence, indeed showing sparks of artificial general intelligence. This is demonstrated by its core mental capabilities (such as reasoning, creativity, and deduction), its range of topics on which it has gained expertise (such as literature, medicine, and coding), and the variety of tasks it is able to perform (e.g., playing games, using tools, explaining itself, ...).

В общем, лучше почитайте статью целиком, очень рекомендую :)
🤯9👍6🔥1
Слишком часто вспоминаю этот мем в последнее время, поэтому пусть будет здесь :)
😁17😢1
ClickHouse и баги в Poco

В мире C++ есть такая известная библиотека, как Poco. По сути, это универсальный фреймворк для Web-приложений на C++, который содержит кучу всего: JSON, парсинг конфигов, HTTP клиент и сервер, работу с БД и много чего еще. Библиотека довольно старая, написана в объектно-ориентированном стиле. Ее код довольно простой и содержит всякой сложной шаблонной магии, в которой можно закопаться [передаю привет Boost.Asio :)] И HTTP-сервер в ней написан просто, без корутин и прочей асинхронности: когда серверу приходит соединение, то с начала его жизни и до конца оно обрабатывается потоком из тредпула, причем при ожидании данных от клиента поток блокируется.

Удивительно, но это одна из немногих широко известных и при этом простых реализаций HTTP-сервера на C++ :) А еще эту библиотеку использует ClickHouse (правда, у них есть свой форк Poco, но об этом попозже)

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

Решил посмотреть, как с этим багом живет ClickHouse. А оказалось, что они пропатчили этот баг в своем форке и никому об этом не сказали :) Поэтому я решил довести этот фикс до upstream'а.

Обидно, что ClickHouse держит патчи в своем форке и не пытается их законтрибутить :( Но буквально недавно разработчики ClickHouse пошли еще дальше и влили свой форк Poco в основной репозиторий ClickHouse, поформатировав при этом код 🐳 При этом в качестве одной из причин назвали «upstream development does not look very active these days», хотя, судя по репозиторию Poco, недавние PRы все еще вливаются. Таким образом, дальнейший перенос патчей будет еще более затруднительным :(
😢7👍4😱4
Pro tip: не используйте Google Картинки для чтения манов по командам и функциям Linux
🔥13😁10👍2🤯1
Гепардово гнездо
Животные делятся на: а) принадлежащих Императору, б) набальзамированных, в) прирученных, г) молочных поросят, д) сирен, е) сказочных, ж) бродячих собак, з) включённых в эту классификацию, и) бегающих как сумасшедшие, к) бесчисленных, л) нарисованных тончайшей…
Классификация языков программирования по Борхесу

According to the Celestial Emporium of Benevolent Knowledge, programming languages can be classified into the following categories:

1) Those that generate code through interpretive dance
2) Those that can only be programmed using an abacus and Morse code
3) Those that are written entirely in emojis
4) Those that communicate only through telepathy and require the programmer to have a specific set of psychic abilities
5) Those that can only be executed during specific planetary alignments
6) Those that are programmed by feeding data to a swarm of bees
7) Those that require the programmer to have a PhD in theology to understand the syntax
8) Those that can only be executed on a computer that is submerged in water
9) Those that are programmed by casting spells and incantations
10) Those that use only punctuation marks and symbols, such as "%%%", ">>>", or "&&&"
11) Those whose syntax is based on the mating habits of insects
12) Those that require the programmer to wear a top hat while coding
13) Those that are written entirely in reverse order
14) Those that are only understood by computers with a certain level of emotional intelligence
15) Those that are designed specifically for time travelers
16) Those that use a language that only the gods can understand
17) Those that use curly braces, but only for aesthetic purposes
18) Those that only allow for single-letter variable names
19) Those that are based on the principle of chaos theory
20) Those that require the programmer to chant a mantra before compiling
21) Those that can only be run on a computer that has been blessed by a priest or shaman
22) Those that are made entirely out of cheese
23) Those that are written in a language that does not exist yet
24) Those that are powered by the thoughts of a telepathic octopus
25) Those that are created by a computer program that has achieved consciousness
26) Those that are written in binary code by extraterrestrial beings
27) Those that are created by ancient civilizations that have been lost to time
28) Those that are invented by mad scientists in their secret labs
29) Those that are created by wizards to cast spells
30) Those that use curly braces {} and those that don't
31) Those that can only be understood by dogs
32) Those whose syntax resembles a recipe for banana bread

Классификация была честно сгенерена ChatGPT с таким запросом:

Generate a classification of programming languages, as it was taken from Borges' Celestial Emporium of Benevolent Knowledge. The classification must be absurd and chaotic, just like the Borges' version

Генерация повторялось несколько раз, и лучшие варианты вы видите в списке выше :)
😁9
https://github.com/rhboot/shim

> shim is a trivial EFI application that, when run, attempts to open and execute another application.

Тем временем trivial application:
$ cloc .
--------------------------------------------------------------------------------
Language files blank comment code
--------------------------------------------------------------------------------
C 471 16913 36193 110168
C/C++ Header 163 6244 14252 38215
make 8 166 8 1048
Markdown 4 175 0 770
Bourne Shell 3 11 2 570
Bourne Again Shell 1 40 47 469
YAML 2 7 2 343
Text 2 22 0 150
diff 3 9 87 55
Assembly 2 5 27 44
CSV 1 0 0 2
--------------------------------------------------------------------------------
SUM: 660 23592 50618 151834
--------------------------------------------------------------------------------

Конечно же, если внимательно посмотреть на репозиторий, то там можно увидеть завендоренные библиотеки (например, посмотрите на папку Cryptlib/). Тем не менее, даже если учитывать .c-файлы только в корне репозитория (исключая все, что начинается на test) — то выйдет около 11000 строк кода (исключая пустые строки и комменты)
🤯2
Объяснение формулы Байеса

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

Рассмотрим следующую задачу:

> Пусть существует заболевание с частотой распространения среди населения 0,001 и метод диагностического обследования, который с вероятностью 0,9 выявляет больного, но при этом имеет вероятность 0,01 ложноположительного результата — ошибочного выявления заболевания у здорового человека. Найти вероятность того, что человек здоров, если он был признан больным при обследовании.

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

Понятно, что можно решать ее, используя формулу Байеса напрямую (посчитать P(A), P(B), P(B|A) и из этого вывести ответ на задачу P(A|B)), но есть и более короткий путь.

Поскольку вероятность быть больным до теста равна 0.001, то получается, что шансы болезни изначально 1:999 (или отношение больных и здоровых равно 1/999). Далее нам требуется поделить вероятность true positive на вероятность false positive — получится 0.9/0.01 = 90. Наконец, умножаем 1/999 на 90 и получаем 90/999, или в переводе обратно в вероятность быть здоровым, 999/(90+999) ≈ 0.9174.

Строгое математическое доказательство остается в качестве упражнения читателю :) Но интуитивно это решение можно понимать с помощью водопада (картинка взята отсюда) На картинке синяя вода — это вероятность здорового, а красная — вероятность больного. Фиолетовая область — это то, что мы наблюдаем после теста: либо больного с положительным тестом, либо здорового с ложноположительным тестом. При этом доля обнаруженных случаев (true positive rate, TPR) — 90%, а ложноположительных срабатываний (false positive rate, FPR) — 30%. Тогда изначально отношение синей и красной воды было 80/20 = 4, а в фиолетовой области оно стало (80*30)/(20*90) = 4/3, т.е. разделилось на 90/30 = 3 — отношение TPR и FPR.

(продолжение ниже)
👍8