Обновить
61
Василий Писарев@Pand5461

Пользователь

15
Подписчики
Отправить сообщение

Видимо, непонятно написал - имеется в виду, что -O3 и -Ofast не отличаются по производительности.
В Julia ещё проверял, там есть эффект в 10-15% от отключения проверок границ массива (fastmath, как и в Си, на скорость не повлиял). Если для numba есть такой флажок, то он, скорее всего, тоже с 200 до 170 мс время скинет.

Намеряны какие-то очень не те времена. У меня получается с numpy примерно 20 секунд на 10М уравнений, с numba примерно 200 мс, на Си с -Ofast и -O3 130 мс (AMD Ryzen 5 3500U). И объяснений, почему вдруг Numba быстрее Си, уже не требуется.

А я вот так и узнал, что в LAPACK алгоритм чуть сложнее стандартной прогонки, как в первой ссылке - обнаружил, что своя реализация по учебнику быстрее библиотечной процентов на 10-20. Но в реальных задачах часто гарантируется, например, диагональное преобладание - а этого достаточно для устойчивости даже без выбора главного элемента.
С параллелизмом есть вопросы. Не так часто возникают задачи, где в основе будет гигантская трёхдиагональная система. По ссылке, впрочем, говорится также про оптимизированный солвер для большого числа трёхдиагональных систем, это как раз реально.

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

Тут полностью согласен. В Julia тут была бы стандартная идиома - функция thomas_solver!(rhp, ld, d, ud), которая перезаписывает d и ud, а в rhp записывает решение, и thomas_solver(rhp, ld, d, ud), которая создаёт временные массивы и с ними запускает предыдущую.

Для трёхдиагональных систем библиотеки мало что дадут и могут быть даже медленнее ручного кода, если, например, дают гарантию устойчивости для любой системы. Прогонка всё равно в скорость доступа к памяти упирается, там же алгоритм последовательный и не векторизуемый.
Бонусом - если писать вручную, то можно соптимизировать для специальных типов матриц (например, если на диагонали все значения одинаковые, можно их не хранить в явном виде). А в задачах, где тридиагональные системы возникают, они как раз имеют специальный вид часто.

Наверное потому что я в уме держал, что переписывать чужой нерабочий код - само по себе надо больше брать, а если навайбкоженный то еще х5)

А, ну если x5 по сравнению с зп программиста, то это ок. Я так понял, что за x5 от стоимости работы нейросетки.

Сильно это все похоже на копирование из гугла или SO?

Там имелась в виду не непосредственная похожесть процесса, а скорее идея "если быстро результата от машины добиться не получилось, считаем её для данной задачи непригодной". Там конкретно шла речь, конечно, про большое число рутинных задач, где человек был уверен, что сам он всё сделает, но требуется время. Именно в этом проекте из текущих задач (по поддержке) на момент обсуждения у него было большинство таких, что LLM достаточно быстро и качественно сама генерит. Если на уровне поиска решения или мозгового штурма - там, думаю, имеет смысл и подольше покопаться с промптами, если в процессе сам что-то узнаёшь.

Ну, зависит от задачи. Если мне надо скрипт, который отсортирует фоточки котиков в одну папку, собачек в другую, и вообще не хочется разбираться в том, как оно работает - можно, в принципе, и доверить такое нейросетке (естественно, сделав бэкап предварительно).
Насчёт "человек бессилен" - я имел в виду допиливание нейровыхлопа до требований производительности/безопасности. Если вы имели в виду то же самое всего за x5 денег - вы прямо ценный специалист :)

На форумах Julia недавно обсуждалось использование ИИ-кода в библиотеках SciML, главный разработчик там ответил примерно так. Отдавать весь кодинг нейронке контрпродуктивно, надо пользоваться ей примерно как поисковиком. Мы не просматриваем всю выдачу гугла, когда ищем ответ на какой-либо вопрос. Смотрим первые 2-3 страницы, если ответа нет, надо думать самостоятельно. С вайбкодингом так же - если нет удовлетворительного кода после 2-3-<вставьте свой предел> уточнений запроса - будем считать, что его уже не будет и пишем код сами.

Да можно, но как-то всё оказывается, что если от решения что-то требуется помимо просто работоспособности (производительность ли, безопасность) - нейросеть не справляется, а человек на этой кодовой базе бессилен.

С одной стороны, компилятор за 20к$ - это впечатляет.
С другой стороны:

  • набор тестов, без которых невозможно бы было сделать проект, взят из открытых источников

  • производительность сгенерированного кода со всеми оптимизациями ниже, чем у GCC без оптимизаций

  • часть тестов таки не проходят, проект остановили на этапе сборки жизнеспособного линукса

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

Грубовато, согласен, прошу прощения.
Но тут такая задача, что можно целую статью написать, какие оптимизации улучшают работу, а какие полезны только с виду, а тут как-то без каких-либо доказательств - мол, учтём специфику задачи и оптимизируем. Кто-нибудь же прочитает да и будет так оптимизировать направо и налево. Или весь пост был сарказм про то, что LLM так увлекается учётом контекста, что рушит всю производительность?

Ну да, хреновый инженер напишет с if и && (неявный if) и битовым вектором. И убьёт любую возможность векторизации компилятором.
А нормальный сделает нормальный массив, перед внутренним циклом поставит одну проверку x[i] == 0 и внутри оставит energy += x[j] * Q[i, j].

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

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

Данные передаются по ссылке. Всегда.

Ну тут можно и дальше зайти. Почему запись мутабельного объекта в аргументах функции будет передачей данных, а запись в теле функции нет тогда? Для меня, наоборот, более консистентной моделью было бы, что в записи функции всё передаётся by-expression, включая значения аргументов, а вот при вызове эти выражения вычисляются и создают необходимые объекты.

Для Python 1/2 я ещё мог бы согласиться с предположением о производительности. Для Python 3 - сомнительно. Учитывая, что этот пример с мутабельным аргументом по умолчанию упоминается в каждом первом руководстве для новичков как типичный ногострел - ну уж можно было бы подумать, как его убрать. Опять же, с учётом поломки обратной совместимости в любом случае. Думаю, можно это было сделать без потерь производительности, Лисп же как-то справляется, в конце концов. Скорее, никто из core developer'ов не счёл такое поведение проблемным.
С нормальными областями видимости, такое ощущение, та же история - не было в core developer'ах ярого функциональщика, которого бы поведение из 4-го примера прямо бесило. А так плоская область видимости внутри функции особо не мешает, пока не начинаешь активно пользоваться замыканиями.
Думаю, если питон основной язык - то к особенностям привыкаешь и перестаёшь замечать, но вот если надо постоянно переключаться с одной семантики на другую - это раздражает.

Примерно так же, как и в Python

function make_generator(default::Integer)
    function generator(arg::Integer=default)
        return arg
    end
    return generator
end

function baz(generator=make_generator(1))
    return nothing
end

Но это, в общем, неинтересно. Интереснее то, что с мутабельными аргументами будет всё то же самое, без танцев с None.

function make_collector(init::Vector=[])
    function collector(args...)
        return append!(init, args...)
    end
    return collector
end

julia> c1 = make_collector();

julia> c2 = make_collector();

julia> c1(3, 4, 5)
3-element Vector{Any}:
 3
 4
 5

julia> c2(5, 12, 13)
3-element Vector{Any}:
  5
 12
 13

julia> c1(5, 12, 13)
6-element Vector{Any}:
  3
  4
  5
  5
 12
 13

Ещё замечание: except Exception: таки считается более культурным вариантом, чем просто except:, потому что системные исключения наследуются не от Exception, и программу как раз-таки можно завершить без kill -9.

А какие бы возникли вопросы?
Вот в Julia значения по умолчанию вычисляются в момент вызова функции, и там это ни у кого проблем не вызывает. Бонусом это даёт возможность ссылаться в значении по умолчанию на предыдущие аргументы. Наоборот, предостережения видел только в руководствах по Python и по Common Lisp. Причём в Lisp это относится только к литералам типа '(1 2), т.к. они по стандарту могут быть вычислены однократно при определении функции, вместо этого рекомендуется значения по умолчанию просто задавать как (list 1 2), тогда они гарантированно вычисляются в момент вызова.

А оно с bar=1 одинаковое, вот с bar=[] будет разное.
Вообще согласен, непонятно, зачем в Python 3 не сделали интерпретацию аргументов по умолчанию как выражений и не сделали нормальные области видимости, коль уж всё равно с Python 2 обратную совместимость сломали.

Насколько я помню, магнитное поле проистекает из электрического только если постулировать отсутствие магнитных зарядов. Тогда уравнения Максвелла становятся асимметричны, и действительно, магнитное поле может порождаться только электрическими зарядами.
А если найдётся магнитный заряд, то уравнения Максвелла будут полностью симметричны относительно электрического и магнитного полей, и даже можно электрическое поле считать релятивистской поправкой к магнитному в отсутствие электрических зарядов.

Ещё вроде как есть поддержка работы с зависимостями в рамках блокнота, но в статье вопрос не раскрыли.

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

Сорри за некропост.
Вообще, что там как выделяется - в джулии не специфицируется.
Если есть константная структура - на неё тоже может не аллоцироваться память. constant folding и всё такое. Так-то даже банальное x.attribute - это в джулии синтаксический сахар для getproperty(x, "attribute"). Но никакая строка там, конечно, не передаётся в явном виде, компилятор видит, что это константа времени компиляции, и преобразует её в статический байтовый сдвиг относительно адреса структуры в памяти.
Гарантируется только то, что неизменяемые структуры в массивах и в полях других структур хранятся "in-line", т.е. массив N структур размером c sizeof(T) = s будет занимать N * s байт. Изменяемые же в массив или в поле другой структуры кладутся всегда по ссылке, потому что семантика требует отдельного времени жизни для внешнего и внутреннего объекта.
При этом изменяемая структура тоже может выделяться на стеке, если компилятор сможет доказать, что у неё время жизни ограничено конкретной областью видимости.

Вроде как, обычно "Космос" - это то, что оставалось на околоземной орбите.
Смею предположить, что если аппарат ушёл с околоземной орбиты, то уже бессмысленно было скрывать истинную цель запуска, т.к. иностранные астрономы уже могли отследить и рассчитать траекторию.

1
23 ...

Информация

В рейтинге
5 188-й
Откуда
Москва, Москва и Московская обл., Россия
Зарегистрирован
Активность