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

store.steampowered.com/app/3024370/NebuLeet

Send message
Только сейчас нашёл статью и новость об OpenApoc.
Активность в репозитории до сих пор вроде бы есть, звёздочку поставил. :)

Apocalypse на момент выхода действительно ощущалась слишком сырой.
Возможно как-нибудь гляну что успели доработать в проекте OpenApoc.
В компиляторе gc (который знаком большинству людей) нет векторизации циклов. Вообще. By design. Никто пока не предложил как её внедрить, чтобы не нарушить одно из:
1. Не замедляет время компиляции.
2. Реализация не слишком сложная (maintainability).
3. Имеет примеры важного кода, который будет сильно ускоряться, кроме синтетики.

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

Математический код и HPC может и получит буст от векторизации, но для gc по-моему это не самые частые пользователи. Возможно в будущем что-то изменится, но пока ситуация такая. Где-то ещё были разные связанные с этим proposal'ы, в том числе о введении примитивов для использования FMA инструкций, но под рукой списка нет, можете поискать на github трекере, если интересно.

Возможно LLVM-based компилятор будет лучше, но по-моему там пока ещё не достаточно всё зрелое.
Как Вы узнали, какой код генерирует компилятор? Где это посмотреть?

Способов несколько.
Самый простой — это передача компилятору ключа -gcflags=-S.


Для примера, соберите вот этот файл (назовём его foo.go):


package foo

func f1(x, y string) string { return x + y }
func f2(x, y, z string) string { return x + y + z }

С помощью команды go tool compile -S foo.go.
Помимо прочего, вы увидите вызовы runtime.concatstring2(SB) в f1 и runtime.concatstring3(SB) в f2.

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

Одна из причин, почему мне Go нравится и почему я пробую работать над статическим анализом для него — мне это по силам.
Я так, понимаю, линтер собранный lintpack-ом берет задачу парсинга и прохода по коду на себя, а линтеры ответственны только за саму проверку и ничего больше. Это сильно уменьшает затраты на написание (и включение в pipeline) нового линтера.

Да, всё так.
Плюс из коробки всё для тестирования (работает с coverage) и интеграционного тестирования.

На Go вообще что-нибудь пишут кроме линтеров и металинтеров?

Конечно, пишут. Библиотеки для написания линтеров и металинтеров, например:
https://go-toolsmith.github.io/

Так, это, надо вырабатывать best practices, составлять code conventions

go-namecheck верифицирует (некоторые) конвенции, чтобы не проверять глазками.


и обязывать разрабов следовать им.

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


Ну тогда введите ещё code review.

Code review это не отменяет. Тут как с линтерами. Они ревью не заменяют, но могут сильно помогать.

Нет, в нём нет интеграций, да и сами утилиты совсем недавно появились.
Меня терзают некоторые сомнения насчёт запуска go-consistent вместе с другими проверками.


Лично я это писал для переодических уборок в коде, а не для постоянных проверок. Если на каждом ревью к такому придираться, может быть больше вреда, чем пользы. А править предупреждения go-consistent можно и автоматически, возможно сделаю даже флаг автоматического исправления, оно не должно ничего ломать, там как все альтернативы польностью эквивалентны (по крайней мере должны быть таковыми).

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


Обычно в строку всё же через string(b) приводят, линтеры типа gosimple на некоторые схожие конструкции предлагают более простые замены.


go-consistent скорее о тех случаях, когда не очевидно, какой из вариантов "правильный", поэтому ответ ищется внутри исходных кодов проекта.

По-моему оптимизирование путём удаления "оптимизаций" — это просто прекрасно.


История с bytes.Buffer примерно такая же.
Улучшения получилось достигнуть с помощью удаления small buffer оптимизации:
https://go-review.googlesource.com/c/go/+/133715

Внезапно для себя обнаружил обсуждение на reddit.
https://www.reddit.com/r/golang/comments/9d5cp9/intelgobytebuf_replacement_for_bytesbuffer_that/


Внимание стоит обратить на disclaimer в bytebuf пакете. Это больше эксперимент, который позволил немного сдвинуться с мёртвой точки. В ближайшем времени могут появиться улучшения в стандартном bytes.Buffer (без изменений API, разумеется).

А почему не на Rust, например?
Почему именно C?

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

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

Но, если честно, это похоже на толстый троллинг.

(P.S. минусы вам не я поставил, но такую реакцию можно было ожидать, думаю, вы знали, на что идёте.)
> Кто то пишет в здравом уме на Го?

Почему бы и нет?

А к чему этот комментарий? Если это шутка, то не очень смешная, а если это всерьёз, то я не понимаю, откуда такая категоричность.

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


buf += fmt.Sprintf(" %d", val)

А если buf имеет алиасы? В Go строки не копируются при присваивании. Но вообще да, можно статически вывести безопасные случаи, когда buf гарантированно можно перезаписать.


s3 = s1 + S()

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


то у нас выделяется память под s3 и туда копируется и s1, и s2. Хотя, если бы мы при выделении памяти под s1 зарезервировали в конце достаточно места, до было бы достаточно скопировать только s2 в конец s1, чтобы получить s3

Это не беспроигрышный вариант, потому что подразумевает некоторый перерасход памяти. С другой стороны аллокатор всё равно обычно выделяет немного больше памяти, просто глядя на структуру stringStruct мы эту информацию получить не можем (нет поля cap).


Ну, есть способы, конечно (например strconv.AppendInt() вместо += strconv.Itoa(), но они заставляют программиста опускаться к деталям реализации, вместо того чтобы переложить эту работу по оптимизации на компилятор.

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

Например, если компилятор видит, что к строке идет много прибавлений — он может выделить сразу большой буффер и копировать только правую (после '+') часть.

Для выражения x + y + z + ... буффер выделяется единожды. А вот как копировать только правую часть я не понял. У нас же должна новая строка в результате получиться, при этом ни один из операндов не должен изменяться.


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

Не уверен, что понимаю, как это может быть возможно.


Вообще + не так часто используется в самых горячих участках. Если конкатенация нетривиальная и нам нужно больше 2-3 сложений, то лучше использовать какой-нибудь bytes.Buffer или strings.Builder.


Эх, мечты, мечты… ))

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

Хорошее замечание. Сейчас добавлю.

Понимаю ваше недоумение.


Но фукнция IsExported(s) проверяет лишь то, что переданная ей строка начинается с заглавной буквы. Метод VisitLocalDef() вызывается только для локальных определений,
поэтому если мы уже внутри и IsExported(s) вернул true, то это локальная переменная, которая начинается с заглавное буквы, что не имеет особого смысла, потому что это не влияет на семантику.

А в англоязычном Golang community есть канал #performance. :)
https://gophers.slack.com

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


Если у кого-то HPC может стать на 30% быстрее, то это не настолько весомый аргумент, потому что всё равно будет медленнее, чем на некоторых других языках.


Если же каким-то образом из-за этого компилятор станет быстрее за счёт более оптимального его же кода, процентов на 5, то вот на это волосы сообщества будут резонировать.

Information

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