Telegram Group Search
2023 не будет простым

Потому что, как известно, 2023 = 7 × 17 × 17.

Надеемся на то, что 2023 год не доставит нам всем неожиданных неприятностей и неприятных неожиданностей, как 2022 :)

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

Всех с Новым годом! 🎉🎄
Трудности перевода с 🇹🇷

(спойлер: Google и Deepl здесь правы, а Яндекс дезинформирует)
Снова забавный Юникод

1) Пусть в Python верно одновременно
>>> assert x != y
>>> assert len(x) == 1 and len(y) == 1
>>> assert x.upper().lower() == y.upper().lower()
>>> assert x.lower().upper() == y.lower().upper()
>>> assert x.upper().lower() == x
>>> assert y.upper().lower() != y

2) Пусть в Python верно одновременно
>>> assert x != y
>>> assert len(x) == 1 and len(y) == 1
>>> assert x.upper().lower() != y.upper().lower()
>>> assert x.upper().lower()[0] == y.upper().lower()[0]
>>> assert x.lower().upper() != y.lower().upper()
>>> assert x.lower().upper()[0] == y.lower().upper()[0]
>>> assert x.lower().upper() != x
>>> assert y.lower().upper() == y

Угадайте, какими могут быть x и y в обоих случаях
Forwarded from commit -m "better"
https://quick-lint-js.com/blog/cpp-vs-rust-build-times/

Текст про сравнение скорости сборки Rust vs. C++.

От кучи подобных текстов отличает монументальность подхода - коллега переписал 17 тыщ строк с С++ на Rust, для корректности сравнения.

Измеряется вообще ВСЕ - инкрементальные vs полные сборки, различные трюки для ускорения сборки как С++, так и для Rust, в том числе, (за это отдельный респект и уважуха) - попытка собрать тулчейны с #BOLT.

Короче, автор явно одержим скоростью, почитайте.

Выводы? Все сложно!
≥ 100

Ура, в Гнезде уже более 100 подписчиков :) Приветствую всех, кто присоединился недавно и надеюсь, что вам понравится контент здесь :)

Эх, теперь мне придется писать еще более годные посты :\ Ну ладно
Снова забавный Юникод: ответы

Пора посмотреть, что за строки x и y были в https://www.group-telegram.com/gepardchan.com/78.

Воспользуемся буквами турецкого алфавита.

1)
>>> x = 'i'
>>> y = 'ı'

Легко видеть, что вызов .upper() приводит оба символа к 'I', после чего они неожиданным образом оказываются равны.

2)
>>> x = 'İ'
>>> y = 'I'

Здесь все чуть интереснее. .lower() на 'I' ожидаемо приводит к 'i', а вот применение .lower() на 'İ' почему-то дает два символа: сначала 'i', а затем '‌ ' (т.е. точку как диакритический знак). Получается 'i‌', что неотличимо от обычного 'i', но по факту у нас две точки, слившиеся в одну.

А вообще, алгоритмы Юникода здесь не очень совместимы с турецким языком, потому что для ı заглавная — это I, а для i заглавная — İ. А применение обычных .lower() и .upper() приводит к другим результатам
Как повесить компилятор Rust

(продолжение к http://www.group-telegram.com/gepardchan.com/10, но тогда было про Go, а сейчас про Rust!)

Скормите ему вот такой вот код:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=4c9d40ee85823a6574df8b57075380c0

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

Еще одно соображение: понятно, что Rust — язык богатый, и наверняка существует куда больше способов добиться экспоненциально длинного типа, или как-нибудь еще повесить компилятор. Это лишь один из способов, да и то самый близкий к оригинальному посту про Go.

С Go было интереснее, потому что язык куда более примитивный :)
Поговорим об абстрактном

Как вы интерпретируете реакцию 🐳?

Я, например, использую эту реакцию достаточно часто (и «кит» в числе тех немногих реакций, которые разрешены в этом канале!). Для меня 🐳 значит что-то среднее между 😁 и 🤡, т.е. когда одновременно кто-то сотворил фигню, и это немного смешно и (или) странно. Хотя описание выше лишь примерное, а точные ситуации, когда я ставлю «кита», определяются интуитивно и не поддаются точному описанию.
https://codeforces.com/blog/entry/111449

Немного написал про участников, которые сдают на контесты Codeforces сгенеренный компилятором ассемблер вместо исходного кода
Животные делятся на:
а) принадлежащих Императору,
б) набальзамированных,
в) прирученных,
г) молочных поросят,
д) сирен,
е) сказочных,
ж) бродячих собак,
з) включённых в эту классификацию,
и) бегающих как сумасшедшие,
к) бесчисленных,
л) нарисованных тончайшей кистью из верблюжьей шерсти,
м) прочих,
н) разбивших цветочную вазу,
о) похожих издали на мух.
(оригинал)
https://www.group-telegram.com/itpgchannel/921
https://stal-ix.github.io/

Есть вот такой интересный проект пакетного менеджера и Linux (но не GNU/Linux!) -дистрибутива от pg. Коротко расскажу про отличия от каноничного GNU/Linux:
- во-первых, все бинарники полностью собраны статически, и никаких so'шек нет. Что позволяет как переносить существующие программы куда угодно, так и собирать свои программы статически
- во-вторых, каждый пакет может существовать более чем в одной версии. Система позволяет держать набор realm'ов (чем-то похожи на virtualenv в питоне, только они питоном не ограничиваются)
- в-третьих, дистрибутив пытается еще и упростить задачу кросс-компиляции (то есть, например, можно легко компилить бинарники для любой платформы, находясь при этом на x86)
- в-четвертых, сам способ описания пакетов довольно необычен: для этого используется Jinja-шаблоны, которые собираются в JSON с shell-командами и метаданными. Описание пакета выглядит компактно и настраиваемо, но иногда приходится копаться в слоях шаблонов, чтобы дойти до сути

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

Еще дистрибутив пробует в bootstrap: собирает всю систему, имея лишь исходники, компилятор C++, Python и shell. Казалось бы, очевидно, что из исходников можно собрать что угодно, но на самом деле все сложнее: например, для сборки компилтора Rust нужен компилятор Rust прошлой версии, и, имея лишь исходники, придется сначала собирать очень древний rustc, а потом многократно собирать все новые и новые версии, пока не дойдет до актуальной.

Я пробовал stal/ix где-то в августе, тогда он еще был очень сырой и почти без документации. Сейчас, скорее всего, ситуация уже сильно получше, но надо смотреть. В целом, основная часть работает как ожидалось, и статические бинарники вполне себе собираются и работают. Из минусов — чтобы это все установить, надо почувствовать себя немного пользователем Gentoo и пару раз дожадаться полной сборки Clang из исходников (а это часа два на восьми потоках CPU). А еще сейчас для установки требуется самому конфигурировать ядро (я тогда ставил в VirtualBox и смог написать и законтрибьютить конфиг ядра для VirtualBox).

А еще пакетный менеджер ix можно поставить отдельно, параллельно с любым классическим дистрибутивом. Увы, документации про это пока нет. Такой режим установки точно избавит от необходимости собирать ядро, но вот Clang собирать придется.
Перемещение в самого себя в 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, но не к пользовательскому коду. Но если перемещение в себя не работает даже со стандартными классами, то есть ли вообще какой-то смысл поддерживать этот случай в своем коде?
Хаос в английском произношении

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, то можно взять или выучить это стихотворение :) Или просто прочитать и посмотреть на свое количество ошибок.
Про 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++ (это не то же самое, что экземпляры класса из мира ООП!)
Вдогонку к предыдущему посту.

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

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

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

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

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

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

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

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

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

В общем, я очень рад, что я работал в команде YTsaurus, и что такой крутой проект наконец-то публично доступен :)
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, ...).

В общем, лучше почитайте статью целиком, очень рекомендую :)
2025/06/26 12:48:51
Back to Top
HTML Embed Code: