Comments 35
Первый CL из этой серии уже улетел на go-review.
Возможно в Go 1.11 будет новый x86.csv. И ещё кое-что полезное…
А в компиляторы С можно перенести новую архитектуру ассемблера?
Интересно в чем принципиальная разница между GO ASM, .NET CIL, JVM кодом и LLVM кодом, помимо регистровой/стековой направленности?
Дело в удобстве? В том смысле «это сделано не нами», а у нас мы легко и быстро поправим генерацию под любой CPU?
x86 с их таймингами, разным количеством исполнительных блоковЗачем тайминги в ассемблере, ни разу завязку на тайминги не видел, это же не real time OS и если не параллельное исполнение команд, то какая разница сколько исполнительных блоков…
Компилятор должен выбрать, что быстрее — инструкция сдвига влево на N разрядов, или
add eax,eax
, повторённый N раз, или конструкции типа lea eax, [eax*8]
.Причём на разных процессорах в пределах линейки x86 предпочительные инструкции разные, отсюда и проблема зоопарка.
Поэтому, выбор только по тактам приемлемых последовательностей инструкций, подставляемых компилятором, после i8086, представляется «неумным», в топовых компиляторах предоставляются ключи выбора типа процессора, и системы SIMD команд, меняющих генерируемый код(оптимизируется под конкретное устройство), в котором (по идее) все последствия учтены, что не факт, например компиляторы MS достаточно долго были заточены на генерацию кода под i386, и на пентиумах исполнялись очень-очень не оптимально, из-за не учета спаривания инструкций и предсказания переходов (и ядра выходили с оптимизацией под i386, но это не факт, что очень плохо, например gentoo-пользователи оптимизировали всю OS тотально, и получали несколько процентов прироста или никакого, сам компилятор накладывал ограничения — падал или генерировал некорректный код). В общем, интел создал систему «интегральных костылей», а компиляторы должны по сложности приближаться к искусственному интеллекту(или не должен,… как выше в топике описан), чтобы один «костыль» не блокировал другой.
Хмм… всё это хорошо, конечно, но такой код вряд ли будет высокопроизводительным. По сути, универсальный ассемблер — это «наибольший общий делитель» всех архитектур. Но каждая платформа имеет свои особенности, а x86/amd64, в частности, большое количество дополнительных инструкций, позволяющих в разы ускорить вычисления. Взять хотя бы эту статью. Как я понял, Go никак не стремится использовать эти расширения, т.к. они не кроссплатформенны.
Здесь говорится о более компактном и эффективном коде, но я не вижу ничего касающегося платформозависимых оптимизаций. Полагаю, что компактность и эффективность опять же относится к общим для всех платформ оптимизациям (это хорошо, конечно). Потому что находить возможности для применения векторных инструкций, судя по приведённой мной ссылке на статью, не так-то просто. А если это делается, то смысл уже этой статьи ускользает от меня.
Скажем, если на какой-то платформе отсутствует инструкция умножения или деления (например, на микроконтроллерах), то либо придётся делать для этой платформы хак в виде сложений/сдвигов, либо делать хак в виде одной инструкции умножения для платформ, где оно есть, либо везде использовать сложения/сдвиги. И судя по вот этой статье будет применяться именно последний вариант.
Есть ещё вопрос производительности: иногда можно вручную написать код, который работает эффективнее, чем результат работы компилятора. Например, существенная часть пакета math/big стандартной библиотеки Go написана на ассемблере, потому что базовые процедуры этой библиотеки гораздо более эффективны, если вы обходите компилятор и реализуете что-то лучше, чем сделал бы он. Иногда ассемблер нужен для работы с новыми и необычными функциями устройств или с возможностями, недоступными для более высокоуровневых языков — например, новыми криптографическими инструкциями современных процессоров.
Вы имеете в виду вот это? Тут нет ничего удивительного, конечно же, стандартную библиотеку оптимизируют вручную для разных архитектур, если есть такая возможность. Но речь шла о компиляторе, который компилирует пользовательский код. В этом случае он тоже сумеет автоматически, без помощи разработчика, использовать расширенный набор инструкций, присущий целевой платформе?
Я говорил вот про этот пакет https://github.com/golang/go/tree/master/src/crypto/aes Как видите, для оптимизации под конкретные архитектуры используется соответствующий постфикс. Похожий подход используется в Go во многих местах. _test.go файлы означают тесты и компилируют отдельно от остального кода. Помимо архитектур в постфиксе можно указать операционную систему, чтобы использовать их уникальные особенности. Например вот так https://github.com/golang/go/tree/master/src/os
Это и есть ручная оптимизация — под каждую платформу код пишется на ассемблере с учётом особенностей этой платформы. Я же говорил про оптимизацию компилятором, как это делает GCC или clang. AES вряд ли соптимизирует, это уж слишком мощное распознавание должно быть, но SSE/AVX применяются автоматически, разработчик же продолжает писать на C или C++, никак не вникая в ассемблер и не подсказывая компилятору. Т.к. эти векторные инструкции (вроде как) недоступны, например, на ARM, там GCC скомпилирует код без них, используя стандартные возможности. Я полагаю, что Go так не умеет, и только об этом и говорил. А использовать интринсики или напрямую ассемблерный код, конечно, можно во многих языках и компиляторах, но это неинтересно в рамках данной темы.
Спасибо за проделанную работу по переводу. Отличнейший доклад был!
Кто вник в детали сильнее?
Чем их "изобретение" отличается от классических
https://en.m.wikipedia.org/wiki/Object_file
Но чем некий машинный код (который почти ассемблер) отличается от некоего «универсального» ассемблера (читай ассемблера некой виртуальной машины)…
Вот это мне не очевидно.
И на LLVM чуть ниже сосласлись совершенно справедливо.
В общем пока мне идеология непонятна. Написали потому что «захотелось» такое же, но с перламутровыми пуговицами (на Го).
Возможно Go на LLVM работал бы даже лучше, но мы этого не узнаем…
К тому же в свое решение вносить изменения намного легче, плюс улучшая его ты помогаешь именно себе, а не всем (что выгодно корпорации).
Это та же причина, почему Microsoft .Net имеет свой runtime, вместо того, чтобы переиспользовать от Java в свое время.
Архитектура ассемблера Go