TIL: javac в некоторых случаях генерирует байткод экспоненциального размера относительно длины исходника. (А ещё длина байткода ограничена 64k байтами, поэтому если добавить ещё один уровень вложенности, компилятор не сможет собрать class-файл вообще.)
https://godbolt.org/z/bP8Ta8Y7h
https://godbolt.org/z/bP8Ta8Y7h
godbolt.org
Compiler Explorer - Java (jdk 21.0.2)
class Square {
static void f() {
int x = 0;
try { x++; } finally { x++;
try { x++; } finally { x++;
try { x++; } finally { x++;
try { x++; } finally { x++;
try { x++; } finally { x++;
try { x++;…
static void f() {
int x = 0;
try { x++; } finally { x++;
try { x++; } finally { x++;
try { x++; } finally { x++;
try { x++; } finally { x++;
try { x++; } finally { x++;
try { x++;…
Storage system trivia time!
Воронка достаёт элементы из блока над собой и перекладывает в блок под собой или сбоку от себя.
Что такое "блок"? Если в этой позиции буквально есть блок с инвентарём — всё просто, берётся инвентарь конкретно этого блока. Если же блока там нет, или это обычный блок без инвентаря, рассматриваются все entity с инвентарём, хитбоксы которых пересекают границы блока, и из них берётся случайная. Если нет и таких, то в качестве инпута берётся первый подходящий item entity.
Выводы. Очевидные: если над воронкой неполный блок, например компостница, то айтемы, физически положенные внутрь компостницы, забираться не будут. Неочевидные: если над вашей воронкой две вагонетки или лодки, где айтемы есть только в одной, забираться они будут не с шагом в 8 gt, а чуть больше, потому что с вероятностью 1/2 выберется пустая вагонетка, и придётся ждать следующего геймтика (9 gt в матожидании, если я умею считать).
Вагонетки с воронками ведут себя примерно так же, но с нюансами. "Блок над собой" для вагонетки не определён, потому что вагонетка может стоять на нецелых координатах; в таком случае выбирается блок, содержащий центральную точку вагонетки (иными словами — блок, с которым вагонетка пересекается по максимальной площади). Вагонетка, стоящая под четырьмя блоками, будет всё равно забирать айтемы только из одного из них. Ещё вагонетка умеет собирать айтемы, не пересекающиеся с её видимым хитбоксом — границы расширены на четверть блока во все горизонтальные направления. В частности, если вагонетка стоит в центре блока, она может забрать айтемы, выровненные по границе соседнего блока.
Несмотря на рандом, мешать вагонетки с воронками и просто воронки — это круто, потому что позволяет объединять потоки виртуально, а не перекладывая из одного контейнера в другой, что быстро не делается.
У вагонеток с воронками можно считывать наполненность компаратором, если этот компаратор считывает сигнал с детекторного рельса, на котором эта вагонетка стоит. Воронка считается стоящей на рельсе, если она пересекается с ним как минимум на 0.2 блока (чуть больше 3 пикселей) по каждой из горизонатальных осей. Если таких вагонеток несколько — консистентно, но недетерминированно берётся одна из них. Вагонетка может "сойти с путей", если рельс убрать, подвинуть вагонетку, а потом вернуть рельс обратно. Заблокировать воронку можно подачей сигнала на активирующий рельс, на котором стоит вагонетка. Под "стоит" понимается, что центр вагонетки содержится в этом блоке; соответственно, такой рельс может быть только один. Если вагонеток на нём стоит несколько, активируются они все.
Вагонеткам не нравится находиться пересекаться хитбоксами, и заставить вагонетки ими пересечься у вас не получится. Но если они уже пересекаются сразу, то после этого они будут спокойно двигаться независимо, оставаясь пересечёнными. Такого можно добиться, если тыкнуть "use" на рельс несколько раз. Энтити в рамках тика симулируются по порядку и пересчитываются исходя из текущей позиции остальных энтити, а не позиции на предыдущем тике, поэтому даже если две вагонетки изначально имели одинаковые параметры, их можно разделить, толкая их в случайных направлениях.
Воронка достаёт элементы из блока над собой и перекладывает в блок под собой или сбоку от себя.
Что такое "блок"? Если в этой позиции буквально есть блок с инвентарём — всё просто, берётся инвентарь конкретно этого блока. Если же блока там нет, или это обычный блок без инвентаря, рассматриваются все entity с инвентарём, хитбоксы которых пересекают границы блока, и из них берётся случайная. Если нет и таких, то в качестве инпута берётся первый подходящий item entity.
Выводы. Очевидные: если над воронкой неполный блок, например компостница, то айтемы, физически положенные внутрь компостницы, забираться не будут. Неочевидные: если над вашей воронкой две вагонетки или лодки, где айтемы есть только в одной, забираться они будут не с шагом в 8 gt, а чуть больше, потому что с вероятностью 1/2 выберется пустая вагонетка, и придётся ждать следующего геймтика (9 gt в матожидании, если я умею считать).
Вагонетки с воронками ведут себя примерно так же, но с нюансами. "Блок над собой" для вагонетки не определён, потому что вагонетка может стоять на нецелых координатах; в таком случае выбирается блок, содержащий центральную точку вагонетки (иными словами — блок, с которым вагонетка пересекается по максимальной площади). Вагонетка, стоящая под четырьмя блоками, будет всё равно забирать айтемы только из одного из них. Ещё вагонетка умеет собирать айтемы, не пересекающиеся с её видимым хитбоксом — границы расширены на четверть блока во все горизонтальные направления. В частности, если вагонетка стоит в центре блока, она может забрать айтемы, выровненные по границе соседнего блока.
Несмотря на рандом, мешать вагонетки с воронками и просто воронки — это круто, потому что позволяет объединять потоки виртуально, а не перекладывая из одного контейнера в другой, что быстро не делается.
У вагонеток с воронками можно считывать наполненность компаратором, если этот компаратор считывает сигнал с детекторного рельса, на котором эта вагонетка стоит. Воронка считается стоящей на рельсе, если она пересекается с ним как минимум на 0.2 блока (чуть больше 3 пикселей) по каждой из горизонатальных осей. Если таких вагонеток несколько — консистентно, но недетерминированно берётся одна из них. Вагонетка может "сойти с путей", если рельс убрать, подвинуть вагонетку, а потом вернуть рельс обратно. Заблокировать воронку можно подачей сигнала на активирующий рельс, на котором стоит вагонетка. Под "стоит" понимается, что центр вагонетки содержится в этом блоке; соответственно, такой рельс может быть только один. Если вагонеток на нём стоит несколько, активируются они все.
Вагонеткам не нравится находиться пересекаться хитбоксами, и заставить вагонетки ими пересечься у вас не получится. Но если они уже пересекаются сразу, то после этого они будут спокойно двигаться независимо, оставаясь пересечёнными. Такого можно добиться, если тыкнуть "use" на рельс несколько раз. Энтити в рамках тика симулируются по порядку и пересчитываются исходя из текущей позиции остальных энтити, а не позиции на предыдущем тике, поэтому даже если две вагонетки изначально имели одинаковые параметры, их можно разделить, толкая их в случайных направлениях.
Это всё означает, что если у вас есть большой поток айтемов, которые нужно сортировать, и на каждый конкретный тип вполне хватило бы скорости одной воронки, но суммарно скорости импульс-сортера не хватает — вы можете распараллелить сортировку вот такой схемой.
Инициализируете верхние вагонетки, как в импульс-сортере, потом верхние воронки тоже инициализируете теми же айтемами со сдвигом на 45 градусов. Когда в вагонетках много айтемов — разблокируете верхнюю воронку; когда в верхней воронке много айтемов — разблокируете нижнюю. Фильтры нужно ставить и в вагонетки, и верхние воронки, потому что каждая воронка "видит" две вагонетки, из которых нужная только одна. Две воронки вместо одной нужны из-за неконсистентной скорости, чтобы скомпенсировать то, что воронки пушат айтемы раз в 8 gt, а пуллить будут только раз в ~9 gt, чтобы воронка с фильтром не опустошилась.
Вайринг редстоуна остаётся в качестве упражнения читателю.
Для сравнения сложности: типичный способ достичь того же эффекта требует дропать айтемы в воду, выравнивать их и отправлять на независимые сортеры слайм-блоками; в незере слайм-лаунчеров нужно ещё больше. Если инпутом выступает ферма с неравномерной частотой айтемов, начинаются проблемы с тем, что воронки в кулдауне пропускают айтемы, поэтому их нужно ещё и зацикливать. Делается, но далеко не так компактно, и если инпута настолько много, что нужно несколько дропперов, начинаются сложности с выравниванием при отсутствии воды.
Инициализируете верхние вагонетки, как в импульс-сортере, потом верхние воронки тоже инициализируете теми же айтемами со сдвигом на 45 градусов. Когда в вагонетках много айтемов — разблокируете верхнюю воронку; когда в верхней воронке много айтемов — разблокируете нижнюю. Фильтры нужно ставить и в вагонетки, и верхние воронки, потому что каждая воронка "видит" две вагонетки, из которых нужная только одна. Две воронки вместо одной нужны из-за неконсистентной скорости, чтобы скомпенсировать то, что воронки пушат айтемы раз в 8 gt, а пуллить будут только раз в ~9 gt, чтобы воронка с фильтром не опустошилась.
Вайринг редстоуна остаётся в качестве упражнения читателю.
Для сравнения сложности: типичный способ достичь того же эффекта требует дропать айтемы в воду, выравнивать их и отправлять на независимые сортеры слайм-блоками; в незере слайм-лаунчеров нужно ещё больше. Если инпутом выступает ферма с неравномерной частотой айтемов, начинаются проблемы с тем, что воронки в кулдауне пропускают айтемы, поэтому их нужно ещё и зацикливать. Делается, но далеко не так компактно, и если инпута настолько много, что нужно несколько дропперов, начинаются сложности с выравниванием при отсутствии воды.
Основная проблема всех подобных параллельных систем — неопределённое поведение в ситуации, когда на вход приходит неизвестный айтем. В данном дизайне такие айтемы останутся в сундуке и в итоге рискуют забить его целиком. У этой проблемы есть два решение: одно очевидное, но не работающее, второе работающее, но не очевидное.
Очевидное решение — поставить где-нибудь воронку, достающую произвольный айтем и сортирующую его обычным последовательным сортером. Идея в том, что хорошие айтемы будут сортироваться по большей части через fast path, но иногда ещё через slow path, а плохие айтемы (если их достаточно мало, конечно) будут всегда через slow path уходить в overflow последовательного сортера.
Нюанс следующий: поскольку воронки забирают айтемы из слотов слева направо, а добавляются айтемы все тоже слева направо, при достаточно высокой скорости входного потока воронка *всегда* будет забирать из первого слота, а в остальных будет скапливаться мусор. Когда мусором забьются все слоты, кроме первого, система потеряет возможность сортировать несколько типов айтемов параллельно, и сравняется по скорости с последовательным сортером.
Фикс очень простой: заменить сундук на активирующийся по клоку дроппер. Дроппер при активации выбрасывает элемент из случайного слота, а не первого, поэтому мусор сможет выбрасываться из всех слотов. Особенно интересно, что эта система умеет автоматически калиброваться под нагрузку в моменте. Если вам приходит мало мусора, в дроппере, вероятно, 4 слота будут забиты чем-то полезным, а 1 — мусором, и мусор будет выкидываться только с вероятностью 1/5. Но если доля мусора начнёт увеличиваться и, например, заполнит все оставшиеся 5 слотов, то мусор будет выбрасываться уже с вероятностью 5/9. Благодаря этому эффекту частота активации дроппера может быть сильно ниже пропускной способности системы, а это значит, что если вы умеете эффективно проверять поток айтемов на полезность, можно не сортировать их последовательно, а отправлять хорошие айтемы обратно в дроппер, откуда их когда-нибудь потом заберут вагонетки.
У меня на основе этого эффекта стала работать ферма золота, когда меня начала бесить дороговизна и некомпактность типичных дизайнов. На картинке весь механизм сортировки. Дропы мобов приходят сверху, тем или иным путём оказываются в дропперах. Из дроппера ~половину дропов забирает один 2x сортер на вагонетке (на gold nugget), сам дроппер со скоростью 2x перемещает оставшиеся айтемы (по большей части rotten flesh) в воронку. Из воронки rotten flesh забирает другой 2x сортер на вагонетке. Остаются мечи, gold ingot'ы, и overflow; первые два вида отфильтровываются последовательным сортером, третий возвращается обратно в дроппер. Вайринг оставляет желать лучшего, но оно почти влезает в 8x8, так что в целом ок.
Очевидное решение — поставить где-нибудь воронку, достающую произвольный айтем и сортирующую его обычным последовательным сортером. Идея в том, что хорошие айтемы будут сортироваться по большей части через fast path, но иногда ещё через slow path, а плохие айтемы (если их достаточно мало, конечно) будут всегда через slow path уходить в overflow последовательного сортера.
Нюанс следующий: поскольку воронки забирают айтемы из слотов слева направо, а добавляются айтемы все тоже слева направо, при достаточно высокой скорости входного потока воронка *всегда* будет забирать из первого слота, а в остальных будет скапливаться мусор. Когда мусором забьются все слоты, кроме первого, система потеряет возможность сортировать несколько типов айтемов параллельно, и сравняется по скорости с последовательным сортером.
Фикс очень простой: заменить сундук на активирующийся по клоку дроппер. Дроппер при активации выбрасывает элемент из случайного слота, а не первого, поэтому мусор сможет выбрасываться из всех слотов. Особенно интересно, что эта система умеет автоматически калиброваться под нагрузку в моменте. Если вам приходит мало мусора, в дроппере, вероятно, 4 слота будут забиты чем-то полезным, а 1 — мусором, и мусор будет выкидываться только с вероятностью 1/5. Но если доля мусора начнёт увеличиваться и, например, заполнит все оставшиеся 5 слотов, то мусор будет выбрасываться уже с вероятностью 5/9. Благодаря этому эффекту частота активации дроппера может быть сильно ниже пропускной способности системы, а это значит, что если вы умеете эффективно проверять поток айтемов на полезность, можно не сортировать их последовательно, а отправлять хорошие айтемы обратно в дроппер, откуда их когда-нибудь потом заберут вагонетки.
У меня на основе этого эффекта стала работать ферма золота, когда меня начала бесить дороговизна и некомпактность типичных дизайнов. На картинке весь механизм сортировки. Дропы мобов приходят сверху, тем или иным путём оказываются в дропперах. Из дроппера ~половину дропов забирает один 2x сортер на вагонетке (на gold nugget), сам дроппер со скоростью 2x перемещает оставшиеся айтемы (по большей части rotten flesh) в воронку. Из воронки rotten flesh забирает другой 2x сортер на вагонетке. Остаются мечи, gold ingot'ы, и overflow; первые два вида отфильтровываются последовательным сортером, третий возвращается обратно в дроппер. Вайринг оставляет желать лучшего, но оно почти влезает в 8x8, так что в целом ок.
Да, это канал про оптимизаторский подход в целом, а не только про классическое программирование, как вы догадались?
Алиса копается
https://lexi-lambda.github.io/blog/2025/05/29/a-break-from-programming-languages/
Недавно прочитала ещё The Copilot Delusion, занятная вещица. У меня отношение к LLMкам чуть другого рода, но в целом смежно.
Я периодически использую их как поисковик, чтобы вспомнить забытое слово, когда словари не помогают. Я прогоняю через них свои статьи, чтобы пофиксить опечатки, убрать лишние повторы и проверить, корректно ли считывается со стороны, на чём я пытаюсь сделать фокус (люди такую задачу выполнить не могут, потому что мои знакомые обычно и так понимают, о чём я пишу, и контекст у них в голове оказывается сразу правильным).
Но когда речь заходит о программировании, LLM мне не может помочь вообще никак. Я очень хочу понять, откуда такой хайп, и поэтому, когда у меня возникает задача, которую я не могу решить сходу, я запихиваю её в LLMку и пытаюсь решить с её помощью. Когда это сделать не получается, я трачу время и решаю её сама, я опять пытаюсь навести LLMку на нужную мысль.
Короче говоря, не работает это никогда, у меня ноль успешного опыта в подобном плане. Вроде как должно быть очевидно, что они не умеют думать, но насколько не умеют — для меня было шоком. Всё взаимодействие сводится к "вот решение" — "оно не работает, контрпример X" — "хорошо, вот исправленное решение" — "Y" — "вот проверенное и работающее решение" — "нет, всё ещё Z" — "вот решение с эвристиками" — "я не хочу эвристики" — "вот решение без эвристик, опирающееся на NP-трудную задачу" — "я хочу эффективный код" — "вот хорошее решение" — "нет, оно всё ещё не работает", после чего я забиваю и иду думать своей головой.
И ладно ещё неэффективный код, он всё равно может сойти для прототипа; основная проблема в коде, который абсолютно очевидно не работает и работать не может, но LLM об этом даже не задумываются, и в итоге приходится по сотне раз повторять одно и то же, и это всё занимает настолько много времени и даёт в итоге настолько мелкий результат, что возникает вопрос, почему бы это просто не сделать сразу самой.
Здесь обычно говорят, что LLMки пока не умеют работать с алгоритмическим кодом. (Ну, как "говорят"; то, что Claude Sonnet это умеет, тоже говорят, но вот я проверила, и не умеет.) А для кода без алгоритмов его использовать, получается, можно. Это не так: в коде, не требующем знать никакие известные алгоритмы, они тоже спокойно ошибаются, если над этим кодом надо думать, чтобы написать его корректно. Хорошо у них не с неалгоритмическим кодом, а исключительно с бойлерплейтом.
Но ведь с бойлерплейтом мы, программисты, всегда справлялись иным образом. Если какой-то простой код повторяется — мы его выносим в функции, классы, библиотеки. Если работать с базой данных неудобно — мы пишем для этого фреймворк, ORM. Такое выделение позволяет начертить зоны ответственности, тестировать каждый компонент в изоляции, использовать проверенные реализации. Если LLMки уберут эту болевую точку, у нас просто исчезнет DRY как понятие, а код превратится в багованное и нечитаемое спагетти и разростётся до таких объемов, где никакой человек и никакая LLMка уже не сможет впиливать фичи.
Так что как-то странно и непонятно это всё. Максимум, что LLM может сделать — написать мне
Я периодически использую их как поисковик, чтобы вспомнить забытое слово, когда словари не помогают. Я прогоняю через них свои статьи, чтобы пофиксить опечатки, убрать лишние повторы и проверить, корректно ли считывается со стороны, на чём я пытаюсь сделать фокус (люди такую задачу выполнить не могут, потому что мои знакомые обычно и так понимают, о чём я пишу, и контекст у них в голове оказывается сразу правильным).
Но когда речь заходит о программировании, LLM мне не может помочь вообще никак. Я очень хочу понять, откуда такой хайп, и поэтому, когда у меня возникает задача, которую я не могу решить сходу, я запихиваю её в LLMку и пытаюсь решить с её помощью. Когда это сделать не получается, я трачу время и решаю её сама, я опять пытаюсь навести LLMку на нужную мысль.
Короче говоря, не работает это никогда, у меня ноль успешного опыта в подобном плане. Вроде как должно быть очевидно, что они не умеют думать, но насколько не умеют — для меня было шоком. Всё взаимодействие сводится к "вот решение" — "оно не работает, контрпример X" — "хорошо, вот исправленное решение" — "Y" — "вот проверенное и работающее решение" — "нет, всё ещё Z" — "вот решение с эвристиками" — "я не хочу эвристики" — "вот решение без эвристик, опирающееся на NP-трудную задачу" — "я хочу эффективный код" — "вот хорошее решение" — "нет, оно всё ещё не работает", после чего я забиваю и иду думать своей головой.
И ладно ещё неэффективный код, он всё равно может сойти для прототипа; основная проблема в коде, который абсолютно очевидно не работает и работать не может, но LLM об этом даже не задумываются, и в итоге приходится по сотне раз повторять одно и то же, и это всё занимает настолько много времени и даёт в итоге настолько мелкий результат, что возникает вопрос, почему бы это просто не сделать сразу самой.
Здесь обычно говорят, что LLMки пока не умеют работать с алгоритмическим кодом. (Ну, как "говорят"; то, что Claude Sonnet это умеет, тоже говорят, но вот я проверила, и не умеет.) А для кода без алгоритмов его использовать, получается, можно. Это не так: в коде, не требующем знать никакие известные алгоритмы, они тоже спокойно ошибаются, если над этим кодом надо думать, чтобы написать его корректно. Хорошо у них не с неалгоритмическим кодом, а исключительно с бойлерплейтом.
Но ведь с бойлерплейтом мы, программисты, всегда справлялись иным образом. Если какой-то простой код повторяется — мы его выносим в функции, классы, библиотеки. Если работать с базой данных неудобно — мы пишем для этого фреймворк, ORM. Такое выделение позволяет начертить зоны ответственности, тестировать каждый компонент в изоляции, использовать проверенные реализации. Если LLMки уберут эту болевую точку, у нас просто исчезнет DRY как понятие, а код превратится в багованное и нечитаемое спагетти и разростётся до таких объемов, где никакой человек и никакая LLMка уже не сможет впиливать фичи.
Так что как-то странно и непонятно это всё. Максимум, что LLM может сделать — написать мне
match
; но даже сделать это я ей не доверю, потому что и в match
ах надо думать, а тыкать LLMку, когда и так на 100% понимаешь, что хочешь написать, бессмысленно.Blogmobly
The Copilot Delusion
Disclaimer: This post was written May 2025, and the arguments apply to AI code capabilities at this time. The arguments around lack of competence are certainly likely to become less prevalent-while the parts about the desecration of the joys of programming…
Это всё был ответ на The Copilot Delusion, если что — ссылка первое время была не та, простите.
Раз в несколько месяцев я возвращаюсь к какому-нибудь старому проекту и перезапускаю на нём CI последнего коммита. И почему-то постоянно результаты не радуют. Lithium упал на трёх платформах, как такое может быть — загадка. Потом когда-нибудь надо будет сесть разобраться...
Ещё боль на тему LLMок в программировании, на которую как будто бы не обращают внимание вообще — это цена. Понятно, что все взрослые люди, очередная подписка — это не шок, но в контексте IT, как мне кажется, это рискует стать огромной проблемой.
Когда мне нужна IDEшка, я не иду её покупать. Есть VS Code, есть плагины для Sublime, есть Zed, у IDE от JetBrains и у Visual Studio есть Community-версии. Компиляторы — gcc, clang, javac, rustc, tsc — все бесплатные и открытые. Рантаймы тоже. В сети есть полно качественных тьюториалов и курсов. Да Linux со всем набором GNU-шных утилит свободный и бесплатный, в конце концов.
Так было далеко не всегда. Первые операционки, IDE, компиляторы, были платными, зачастую их могли себе позволить только корпорации и универы. GCC создавался в противовес, на открытый JDK перешли только недавно, и так далее. Заниматься разработкой не на компанию, а для себя или в open-source было возможно, но либо результаты выходили менее профессиональными, либо нужно было вкладывать кучу времени и других ресурсов. LLMки как друзья программиста — это по сути инструмент той же категории, только появляющийся, а не исчезающий.
Чего я боюсь — что такими темпами в программирование нельзя будет войти вообще, не прогибаясь под какую-то компанию. Нельзя будет, как я, найти в шкафу книжку по PHP, скачать Apache с php-cgi, написать "Hello, world" и почувствовать себя всемогущей. Я боюсь, что тулинг станет настолько неподъёмным, что родители подумают дважды, перед тем как вложить такие ресурсы в обучение детей. Я переживаю, как бы open-source не заглох, если доступ к сильным моделям смогут себе позволить только корпораты.
Когда мне нужна IDEшка, я не иду её покупать. Есть VS Code, есть плагины для Sublime, есть Zed, у IDE от JetBrains и у Visual Studio есть Community-версии. Компиляторы — gcc, clang, javac, rustc, tsc — все бесплатные и открытые. Рантаймы тоже. В сети есть полно качественных тьюториалов и курсов. Да Linux со всем набором GNU-шных утилит свободный и бесплатный, в конце концов.
Так было далеко не всегда. Первые операционки, IDE, компиляторы, были платными, зачастую их могли себе позволить только корпорации и универы. GCC создавался в противовес, на открытый JDK перешли только недавно, и так далее. Заниматься разработкой не на компанию, а для себя или в open-source было возможно, но либо результаты выходили менее профессиональными, либо нужно было вкладывать кучу времени и других ресурсов. LLMки как друзья программиста — это по сути инструмент той же категории, только появляющийся, а не исчезающий.
Чего я боюсь — что такими темпами в программирование нельзя будет войти вообще, не прогибаясь под какую-то компанию. Нельзя будет, как я, найти в шкафу книжку по PHP, скачать Apache с php-cgi, написать "Hello, world" и почувствовать себя всемогущей. Я боюсь, что тулинг станет настолько неподъёмным, что родители подумают дважды, перед тем как вложить такие ресурсы в обучение детей. Я переживаю, как бы open-source не заглох, если доступ к сильным моделям смогут себе позволить только корпораты.
Алиса копается
Раз в несколько месяцев я возвращаюсь к какому-нибудь старому проекту и перезапускаю на нём CI последнего коммита. И почему-то постоянно результаты не радуют. Lithium упал на трёх платформах, как такое может быть — загадка. Потом когда-нибудь надо будет сесть…
Пофиксили с Юлей, всё как всегда — ноль проблем в коде и десяток в тулинге вокруг него. Куча капельку сломанных таргетов, в половине ситуаций непонятно кого винить.
У меня только один вопрос, может кто знает ответ: внезапно сломалось очень много тулинга, связанного с доктестами. В куски кода, которые раньше ставили только RUSTFLAGS, пришлось добавить RUSTDOCFLAGS. Что за последние месяцы такого могло случиться, что на это могло повлиять? Логов за тот период, чтобы сравнить, к сожалению, нет, хотя при желании можно попробовать просто со старым cargo сравнить.
—
Ах, понятно, конечно, чёт я сходу не догадалась: доктесты наконец-то начали запускаться для нехостовых таргетов: https://github.com/rust-lang/cargo/pull/15462
У меня только один вопрос, может кто знает ответ: внезапно сломалось очень много тулинга, связанного с доктестами. В куски кода, которые раньше ставили только RUSTFLAGS, пришлось добавить RUSTDOCFLAGS. Что за последние месяцы такого могло случиться, что на это могло повлиять? Логов за тот период, чтобы сравнить, к сожалению, нет, хотя при желании можно попробовать просто со старым cargo сравнить.
—
Ах, понятно, конечно, чёт я сходу не догадалась: доктесты наконец-то начали запускаться для нехостовых таргетов: https://github.com/rust-lang/cargo/pull/15462
GitHub
Stabilize doctest-xcompile by ehuss · Pull Request #15462 · rust-lang/cargo
This stabilizes the doctest-xcompile feature by unconditionally enabling it.
Closes #7040
Closes #12118
What is being stabilized?
This changes it so that cargo will run doctests when using the --ta...
Closes #7040
Closes #12118
What is being stabilized?
This changes it so that cargo will run doctests when using the --ta...
Я долгое время думала, что в десктопной версии Telegram нельзя посмотреть статистику канала. Потому что в мобильной версии это заметный пункт в меню, а в десктопной его как будто бы нет (см. фотокарточку 1). В менюшке, которая "Channel Info" в сайдбаре, тоже нет ничего похожего, как и в окне, которое открывается по нажатию "Manage Channel".
Так вот: если вы нажмёте на заголовок канала, откроется окно "Channel Info", которое полностью совпадает с сайдбаром, но ещё содержит отдельную кнопку меню. И вот это меню сильно похоже на меню, которое доступно сразу в заголовке, но содержит ещё парочку опций — в частности статистику.
Дизайнеры, блин...
Так вот: если вы нажмёте на заголовок канала, откроется окно "Channel Info", которое полностью совпадает с сайдбаром, но ещё содержит отдельную кнопку меню. И вот это меню сильно похоже на меню, которое доступно сразу в заголовке, но содержит ещё парочку опций — в частности статистику.
Дизайнеры, блин...
https://purplesyringa.moe/blog/fast-limited-range-conversion-between-ints-and-floats/
Что смешно, статья написана потому, что мои попытки нагуглить этот метод первой ссылкой показывают пост на Reddit, где этот метод описан в моём комментарии
Что смешно, статья написана потому, что мои попытки нагуглить этот метод первой ссылкой показывают пост на Reddit, где этот метод описан в моём комментарии
purplesyringa's blog
Fast limited-range conversion between ints and floats
This post is about a popular but niche technique I can never find a succinct reference for. I didn’t invent it, I just need a page I can link when giving optimization advice.
Integer ↔ float casts that utilize specialized processor instructions, i.e. those…
Integer ↔ float casts that utilize specialized processor instructions, i.e. those…
Генераторы — очень удобная штука.
Вот, например, есть у вас AST, и вы хотите пройтись по всем его нодам. В норме вы делаете на AST-ноде рекурсивный метод, которому подаётся callback, и этот callback он потом вызывает на разных уровнях вложенности. Можно ещё встретить visitor-класс, но это суть то же самое. Проблем с таким подходом много:
Генераторы же упрощают и API, и его реализацию. С точки зрения пользователя — вы просто пишете обычный
Но есть нюанс. Пример с AST не просто так: AST может быть достаточно сильно вложенный, и должен возникнуть вопрос: а как же во время
В большинстве языков ответ — "никакая". Генератор — это суть штука, которой можно сказать "покрутись и дай следующий элемент". При первом запуске генератора, как только управление доходит до
А это значит, что если у вас есть AST из N вложенных нод, итерация по этому AST генераторами будет работать за квадрат — в отличие от подхода с коллбеками, который даст здесь линию, потому что ему прыгать наверх не надо, он коллбек откуда угодно запустить может.
Ровно то же самое, кстати, происходит и с асинхронными функциями. Когда вы делаете
Это всё максимально забавно ещё потому, что JavaScript, который всегда в асинках опирался на неудобные callback'и и промисы, таки умудряется свой async/await сделать линейным — как раз благодаря коллбекам. Потому в джаваскрипте рантайм сидит не сверху стека вызовов, а отдельно, и рекурсивные вызовы по сути образуют просто море маленьких корутин, которые друг на друга повесили event handler'ы. И когда промис резолвится, его обработчики вызывается напрямую — а их родители вызовутся только если зарезолвятся дети. На генераторы это, впрочем, не распространяется. У C# история примерно такая же; больше языков с таким подходом я не знаю.
Глупо, что это максимально легко оптимизировать — поддерживать стек вызовов генераторов, и сначала пытаться запрашивать элемент у самого вложенного. Но так никто не делает, потому что, ну, не принято, что ли. Не знаю.
Короче говоря, из-за всего этого я не использую рекурсивные генераторы, а жру коллбеки. И вам того же советую.
Вот, например, есть у вас AST, и вы хотите пройтись по всем его нодам. В норме вы делаете на AST-ноде рекурсивный метод, которому подаётся callback, и этот callback он потом вызывает на разных уровнях вложенности. Можно ещё встретить visitor-класс, но это суть то же самое. Проблем с таким подходом много:
break
не сделаешь, ошибку без костылей не выбросишь, комбинаторы типа .map
/.fold
не используешь. Часть можно исправить, но только ценой усложнения собственно кода метода.Генераторы же упрощают и API, и его реализацию. С точки зрения пользователя — вы просто пишете обычный
for
по итератору и можете при желании использовать комбинаторы, с точки зрения реализации — написать yield
/yield from
в паре мест.Но есть нюанс. Пример с AST не просто так: AST может быть достаточно сильно вложенный, и должен возникнуть вопрос: а как же во время
yield
управление передаётся из глубины стека куда-то наружу, а потом обратно внутрь? Какая такая магия позволяет эффективно делать эти операции?В большинстве языков ответ — "никакая". Генератор — это суть штука, которой можно сказать "покрутись и дай следующий элемент". При первом запуске генератора, как только управление доходит до
yield
, значение возвращается по всему стеку вызовов наверх, прям как с return
. При этом сохраняются значения переменных на стеке и для каждой функции — строка кода, которая исполнялась на тот момент. При следующем запросе элемента стек вызовов из этих данных восстанавливается и управление передаётся обратно. Никакого явного сохранения локальных переменных обычно, конечно, нет, но рекурсивные вызовы и возвраты никуда не деваются.А это значит, что если у вас есть AST из N вложенных нод, итерация по этому AST генераторами будет работать за квадрат — в отличие от подхода с коллбеками, который даст здесь линию, потому что ему прыгать наверх не надо, он коллбек откуда угодно запустить может.
Ровно то же самое, кстати, происходит и с асинхронными функциями. Когда вы делаете
await
на какой-то task, он поднимается по стеку вызовов наверх до рантайма. Рантайм ждёт, когда задача исполнится, и после этого пропихивает её результат обратно вниз.Это всё максимально забавно ещё потому, что JavaScript, который всегда в асинках опирался на неудобные callback'и и промисы, таки умудряется свой async/await сделать линейным — как раз благодаря коллбекам. Потому в джаваскрипте рантайм сидит не сверху стека вызовов, а отдельно, и рекурсивные вызовы по сути образуют просто море маленьких корутин, которые друг на друга повесили event handler'ы. И когда промис резолвится, его обработчики вызывается напрямую — а их родители вызовутся только если зарезолвятся дети. На генераторы это, впрочем, не распространяется. У C# история примерно такая же; больше языков с таким подходом я не знаю.
Глупо, что это максимально легко оптимизировать — поддерживать стек вызовов генераторов, и сначала пытаться запрашивать элемент у самого вложенного. Но так никто не делает, потому что, ну, не принято, что ли. Не знаю.
Короче говоря, из-за всего этого я не использую рекурсивные генераторы, а жру коллбеки. И вам того же советую.