Pull to refresh
143
2
Искандер @quasilyte

store.steampowered.com/app/3024370/NebuLeet

Send message

Самый честный ответ: никак. Все оптимизации локальны для одной функции.


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


  • Встраивание не только листовых функций (сейчас оно доступно, но не включено по умолчанию).
  • Улучшение cost модели для принятия решений о встраиваемости функций (cmd/compile: improve inlining cost model).

Возможно если бы кто-то показал, что глобальная оптимизация Go программ имеет эти свойства:


  1. Может быть реализована без значительного замедления компиляции.
  2. Значительно ускоряет программы, которые чувствительны к производительности.

То со временем это бы вливали в мастер. Сначала в виде эксперимента, потом в виде поведения по умолчанию, если бы не было значительных недостатков.

Новая оптимизация — это раздувание кодовой базы, усложнение проекта.

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


А нет ли какого-то правила, определяющего, можно ли добавлять данную оптимизацию в компилятор?

Каждый коммит в компилятор рекомендуется сопровождать бенчмарками и, если релевантно, статистикой изменения размера бинарников (тут помогают утилиты benchstat и bincmp). Самой ленивой проверкой является запуск ./make.bash с подсчётом срабатываний нового правила.


Если оптимизация именно на Go дистрибутиве не очень срабатывает, но выглядит полезной, стоит найти другой проект, где она срабатывает и даёт измеримый прирост.


Чем больше аргументов и статистики на руках — тем лучше.


При генерации кода из Rules можно передать параметр -log:


flag.Bool("log", false, "generate code that logs; for debugging only")

Тогда при ./make.bash (и вообще любом компилировании) будут подсчитываться все срабатывания правил. Только осторожно, там между запусками будет append в файл, так что он может до нескольких гигабайт вырасти достаточно незаметно; не забудьте выключить. :)

paramTypeCombine

В ней присутствуют false positive срабатывания для безымянных параметров. Если бы их не было, всё равно был бы вредным?


И если вы хотите иметь opinionated проверки

В недавнем обновлении по умолчанию experimental и opinionated выключены из -enable=all. В идеале eventually придём к более-менее нормальному default набору, но в качестве интеграции куда-то всё равно лучше явный список использовать.


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

Уже в golangci-lint добавиться планируем, там есть интеграции в том числе с редакторами. В gometalinter, наверное, тоже можно. Для проекта эти интеграции важны, согласен полностью.

Есть общепризнанный и лаконичный аналог на Русском, или только полная форма «статический анализатор»? Возможно, «анализатор» был бы уместным компромиссом.

В Лондоне, кстати, совсем скоро будет workshop:
https://www.meetup.com/Go-London-User-Group/events/251046966/
Организует упомянутый в статье Daniel Martí.


В англоязычном слаке (https://gophers.slack.com) есть канал contributor-workshop, где он предложил нам разместиться при следующем проведении. Из плюсов: там очень много людей из "Go core team".

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

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

Можем закрыть эту тему?
Я не понимаю ваших мотивов и того, чего вы хотите.
Очевидно, мои ответы вам не помогают/не нравятся.
Go в том числе в датацентрах используется, а там есть понятие tail latency.

Издержки на него можно сократить, если Go работает быстрее.
Сделать Go быстрее для всех, без надобности учить людей писать более быстрый код можно с помощью ускорения рантайма и улучшения компилятора (например, научить его использовать дополнительные регистры для уменьшения количества spill'ов, правда это немного надуманный пример).

Хуже от того, что Go будет становиться более быстрым и/или потреблять меньше памяти, никому не будет.
Конкретно пример с VADDPD привёл для подчёркивания того, что старый код будет собираться по-старому, с VEX префиксом. В редких ситуациях EVEX префикс мог бы дать небольшой выигрыш в размере машинного кода за счёт compressed displacement, но для более детерменированного поведения и в том числе для обратной совместимости, выбрана цитируемая вами схема. Плюс тем, кто «без словаря» не помнит всех нюансов, спокойнее (например мне).

AVX-512 и EVEX может появляться только в новом коде, когда программист сознательно запросил данное поведение (использовал новые регистры или суффиксы опкода).

Способа «переопределить» поведение нет. То есть нельзя попросить тот VADDPD кодировать по схеме AVX-512.
Не всегда != никогда.

У Go тулчейна есть задача быть максимально независимым от других утилит, в частности внешних ассемблеров.

Тем более если кому-то векторизация не нужна, он о ней не задумывается.
А в рантайме Go, может быть, даже применение найдётся.
Я сейчас не обязательно про новые «модные» инструкции и 512-битные регистры, к которым у многих есть своё отношение, а про приятные дополнения к предыдущим AVX. Насколько мне известно, положительные эксперименты как минимум в пакетах крипты уже были.

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

Нет, их мало и они не совсем веб-разработчики. То, что они делают, потом используют в том числе веб-разработчики.

Ещё одним «пользователем» ассемблера является сам тулчейн.
Можете пробежаться по статье Архитектура ассемблера Go. Разница лишь в том, что нет этапа парсинга.

Ассемблер в Go — одно из промежуточных представлений компилятора (низкоуровневый IR).
Если через 10 лет везде будет AVX-512, то, возможно, вместо 16 векторных регистров он будет использовать 32 (в них float'ы хранятся). Либо у нас будет что-то типа вот этого: GOAMD64 architecture flag и получить преимущества EVEX префикса можно будет значительно раньше.
Возможно кому-то такой формат в дополнение к другим источникам будет полезен:
quasilyte.github.io/blog/post/go-asm-complementary-reference

Стараюсь все интересности туда по мере обнаружения (и добавления) дописывать.

Как будет готов AVX512, впишу туда детали (ссылка на обсуждения там приведена).
Ещё раз, разговор не о том, что все обязаны перерабатывать, ночевать и все выходные на работе проводить да ещё бесплатно. И не в том, что конкретный работник уходит ровно. А в том, что десяток человек не могут быть вашими клонами и вести себя одинаково.


Я эту часть упустил в ветке.

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

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

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

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

Расписываюсь, что в курсе холиварности этой темы. Тут куча простора для недопонимания и альтернативных точек зрения.

[Картинка «This is fine», всё в огне]
В такой формулировке с вами гораздо проще согласиться.
Спасибо.
А зачем именно закапывать?

По мне так наоборот, пусть живёт и процветает.
Забавно, что в итоге конвертация из XED таблиц легла на меня. :)
Первый CL из этой серии уже улетел на go-review.

Возможно в Go 1.11 будет новый x86.csv. И ещё кое-что полезное
А выгорание едва ли начнётся, если к домашнему опенсорсу относиться как к хобби.


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

Большое количество покинутых проектов меня сильно демотивирует.
В крайних случаях может сопутствовать выгоранию.
Возможно мне действительно не хватит знаний продолжить диалог, но стало любопытно: а если член структуры является полиморфным типом, разве компилятор сможет в каких-то случаях избежать перерасхода памяти?

Про спекулятивную девиртуализацию поищу.

Anyway, в Go можно явно сказать: «Тут мне полиморфный тип, а тут просто структру T».
В C++ же полиморфность является свойством самого типа T. Right?
Возможно проблема в том, что у Go нет своего pip.
Go build и то, что близ него — это довольно низкоуровневые вещи.

Мне не очевидно, насколько github.com/constabulary/gb юзабелен, но скорее всего со временем появится хороший project-based тул, который станет тем самым pip для Go.
Я хотел бы заметить, что в случае C++ добавив в класс [тип T] виртуальный метод, у вас каждый экземпляр типа T будет содержать таблицу виртуальных методов.
В случае Go сам тип T, реализующий интерфейсы, таблицы виртаульных методов не содержит никогда, поэтому когда мы не используем полиморфное поведение, не будет лишнего жира. Когда T передаётся как интерфейсный тип, вот там уже будет боксинг с аллокацией.

Насколько это хорошо или плохо — не хочу полемизировать, но подчеркнуть, что это не «Как в C++», всё-таки захотелось.

Information

Rating
706-th
Location
Грузия
Registered
Activity