В компиляторе gc (который знаком большинству людей) нет векторизации циклов. Вообще. By design. Никто пока не предложил как её внедрить, чтобы не нарушить одно из:
1. Не замедляет время компиляции.
2. Реализация не слишком сложная (maintainability).
3. Имеет примеры важного кода, который будет сильно ускоряться, кроме синтетики.
Я не утверждаю, что это бесполезные вещи, просто напоминаю, что приоритет у этого всего довольно низкий, а порог для включения этих оптимизаций в ядро Go довольно высокий. Как-то так.
Математический код и HPC может и получит буст от векторизации, но для gc по-моему это не самые частые пользователи. Возможно в будущем что-то изменится, но пока ситуация такая. Где-то ещё были разные связанные с этим proposal'ы, в том числе о введении примитивов для использования FMA инструкций, но под рукой списка нет, можете поискать на github трекере, если интересно.
Возможно LLVM-based компилятор будет лучше, но по-моему там пока ещё не достаточно всё зрелое.
Я так, понимаю, линтер собранный lintpack-ом берет задачу парсинга и прохода по коду на себя, а линтеры ответственны только за саму проверку и ничего больше. Это сильно уменьшает затраты на написание (и включение в pipeline) нового линтера.
Да, всё так.
Плюс из коробки всё для тестирования (работает с coverage) и интеграционного тестирования.
Нет, в нём нет интеграций, да и сами утилиты совсем недавно появились.
Меня терзают некоторые сомнения насчёт запуска go-consistent вместе с другими проверками.
Лично я это писал для переодических уборок в коде, а не для постоянных проверок. Если на каждом ревью к такому придираться, может быть больше вреда, чем пользы. А править предупреждения go-consistent можно и автоматически, возможно сделаю даже флаг автоматического исправления, оно не должно ничего ломать, там как все альтернативы польностью эквивалентны (по крайней мере должны быть таковыми).
Если брать в расчёт стандартную библиотеку и произвольные функции, которые могут выполнять одинаковые операции, список будет стремиться к бесконечности. :)
Обычно в строку всё же через string(b) приводят, линтеры типа gosimple на некоторые схожие конструкции предлагают более простые замены.
go-consistent скорее о тех случаях, когда не очевидно, какой из вариантов "правильный", поэтому ответ ищется внутри исходных кодов проекта.
Внимание стоит обратить на disclaimer в bytebuf пакете. Это больше эксперимент, который позволил немного сдвинуться с мёртвой точки. В ближайшем времени могут появиться улучшения в стандартном bytes.Buffer (без изменений API, разумеется).
Статья не о том, что Go медленный, а о том, что операции над строками можно при желании ускорить. И в следующем релизе Go, возможно, будет одна или более оптимизаций, связанных с этим. А если эти оптимизации в Go не примут, то можно их будет как стороннюю библиотеку выложить, если кому-то действительно нужно как можно быстрее строки склеивать (хотя это довольно сомнительный случай).
Сразу говорю, что приятно видеть такие комментарии и мои ответы — это не придирки, а попытки полностью понять ваши предложения и, возможно, добавить контекста.
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, то это локальная переменная, которая начинается с заглавное буквы, что не имеет особого смысла, потому что это не влияет на семантику.
Оно само собой, что лучше перенять опыт других компиляторов, но тут скорее вопрос насколько большой выигрыш именно для Go и наиболее типичных программ.
Если у кого-то HPC может стать на 30% быстрее, то это не настолько весомый аргумент, потому что всё равно будет медленнее, чем на некоторых других языках.
Если же каким-то образом из-за этого компилятор станет быстрее за счёт более оптимального его же кода, процентов на 5, то вот на это волосы сообщества будут резонировать.
Активность в репозитории до сих пор вроде бы есть, звёздочку поставил. :)
Apocalypse на момент выхода действительно ощущалась слишком сырой.
Возможно как-нибудь гляну что успели доработать в проекте OpenApoc.
1. Не замедляет время компиляции.
2. Реализация не слишком сложная (maintainability).
3. Имеет примеры важного кода, который будет сильно ускоряться, кроме синтетики.
Я не утверждаю, что это бесполезные вещи, просто напоминаю, что приоритет у этого всего довольно низкий, а порог для включения этих оптимизаций в ядро Go довольно высокий. Как-то так.
Математический код и HPC может и получит буст от векторизации, но для gc по-моему это не самые частые пользователи. Возможно в будущем что-то изменится, но пока ситуация такая. Где-то ещё были разные связанные с этим proposal'ы, в том числе о введении примитивов для использования FMA инструкций, но под рукой списка нет, можете поискать на github трекере, если интересно.
Возможно LLVM-based компилятор будет лучше, но по-моему там пока ещё не достаточно всё зрелое.
Способов несколько.
Самый простой — это передача компилятору ключа
-gcflags=-S
.Для примера, соберите вот этот файл (назовём его
foo.go
):С помощью команды
go tool compile -S foo.go
.Помимо прочего, вы увидите вызовы
runtime.concatstring2(SB)
в f1 иruntime.concatstring3(SB)
в f2.Одна из причин, почему мне Go нравится и почему я пробую работать над статическим анализом для него — мне это по силам.
Да, всё так.
Плюс из коробки всё для тестирования (работает с coverage) и интеграционного тестирования.
Конечно, пишут. Библиотеки для написания линтеров и металинтеров, например:
https://go-toolsmith.github.io/
go-namecheck
верифицирует (некоторые) конвенции, чтобы не проверять глазками.Я бы предпочёл, чтобы как можно больше работы по незначительным вещам выполнялось автоматически или, хотя бы, детектировались программами.
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, разумеется).Почему именно C?
Статья не о том, что Go медленный, а о том, что операции над строками можно при желании ускорить. И в следующем релизе Go, возможно, будет одна или более оптимизаций, связанных с этим. А если эти оптимизации в Go не примут, то можно их будет как стороннюю библиотеку выложить, если кому-то действительно нужно как можно быстрее строки склеивать (хотя это довольно сомнительный случай).
Обычный язык программирования со своими недостатками и преимуществами, который нравится некоторым людям. Аналогично с талисманом.
Но, если честно, это похоже на толстый троллинг.
(P.S. минусы вам не я поставил, но такую реакцию можно было ожидать, думаю, вы знали, на что идёте.)
Почему бы и нет?
А к чему этот комментарий? Если это шутка, то не очень смешная, а если это всерьёз, то я не понимаю, откуда такая категоричность.
Сразу говорю, что приятно видеть такие комментарии и мои ответы — это не придирки, а попытки полностью понять ваши предложения и, возможно, добавить контекста.
А если
buf
имеет алиасы? В Go строки не копируются при присваивании. Но вообще да, можно статически вывести безопасные случаи, когдаbuf
гарантированно можно перезаписать.Интересно. Но это потребует разных специализаций
S
, потому что в общем случае нам нужно выделять новую строку, если, например, результат присваивается без конкатенации. Само тело функции для такого будет выделять новую память, а специализация для вашего случая потребует второго определенияS
, если я правильно понял, где не будет выделения памяти перед возвратом результата.Это не беспроигрышный вариант, потому что подразумевает некоторый перерасход памяти. С другой стороны аллокатор всё равно обычно выделяет немного больше памяти, просто глядя на структуру
stringStruct
мы эту информацию получить не можем (нет поляcap
).Согласен, но тут вопрос в том, насколько это важный случай, который вообще стоит оптимизировать.
Впрочем, анализ провести лишним не будет.
Для выражения
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, то вот на это волосы сообщества будут резонировать.