#c #gcc #clang #cpu-registers #cpython
#c #gcc #лязг #cpu-регистры #cpython
Вопрос:
Обобщенные вопросы:
- Как я узнаю, когда следует переназначить переменную в коде C, чтобы избежать утечки регистра? Зависит ли ответ от структуры процессора целевых машин и компилятора?
- Если я добавлю несколько новых переменных в исходный код, должен ли я проверить строку, чтобы избежать утечки регистра, все еще полезно?
В CPython я нашел строку, пытающуюся избежать утечки регистра путем переназначения переменной. Изменение строки можно проследить до фиксации, созданной в 2015 году. Мне любопытен принцип строки.
Когда пользователь запускает Python, в память или регистры загружается много данных и кодов. Почему эта строка кода для предотвращения утечки регистра все еще работает, если изменены другие части кода CPython? Постоянно CPython построен на разных архитектурах ЦП и разных операционных системах. Почему в комментарии к фиксации только что упоминались типы компиляторов?
Комментарии:
1. Я бы предположил, что тот, кто внес это изменение, невежественен, и что либо их изменения были удалены оптимизатором компилятора и не имели никакого значения вообще (вероятно), либо ухудшили производительность сгенерированного кода (маловероятно).
2. Трудно предотвратить это в общем случае, когда различные системы так сильно различаются по количеству доступных им регистров. Кроме того, утечка регистров — это не совсем конец света с точки зрения снижения производительности.
3. В комментарии говорится «микрооптимизация на основе наблюдаемых выходных данных компилятора». Итак, автор решил изменить порядок своего кода на основе компилятора (ов), который он использовал в то время. Это правильная стратегия для цикла, критичного ко времени, тем более что в этом случае он на самом деле не менял семантику функции, он просто переупорядочил некоторые промежуточные операции. Однако это никоим образом не является «постоянным» — будущие обновления компилятора или даже другие флаги компиляции не гарантируют сохранения этой оптимизации.
Ответ №1:
Когда у генератора кода заканчиваются регистры, в которых хранятся промежуточные и часто используемые значения, он сбрасывает регистры, записывая их в память, обычно в область, выделенную из стека. В общем, это недорогая операция, которая сильно зависит от количества регистров в вашем процессоре, параметров, предоставляемых компилятору, и версии самого компилятора.
Вы должны стремиться писать понятный код, а не пытаться перепроектировать генератор кода. Часто генератор кода вознаграждает вас за это, генерируя довольно хороший код. Когда вы сталкиваетесь с проблемами производительности, профилировщик должен направить вас к коду, который необходимо пересмотреть. Улучшения обычно выполняются структурно или алгоритмически. Кодовые приемы для улучшения сгенерированного кода часто вращаются вокруг раскрытия информации компилятору; например, значение, которое он считает динамическим, на самом деле статично, или организация структур данных для лучшего использования ресурсов кэша.
Если вам нужно перепроектировать генератор кода, вы действительно можете переписать последовательность на машинном языке — по крайней мере, есть руководство по машинному языку, где в качестве взломов компилятора всегда используются племенные знания. Откуда вы знаете, какие биты переписывать на машинном языке — снова обратитесь к профилировщику.
Комментарии:
1. Я не думаю, что они зашли так далеко, чтобы «перепроектировать генератор кода». Я уже выполнял подобную оптимизацию раньше, только из дизассемблирования, показанного моей IDE. Найдите конкретную функцию в профилировщике, посмотрите на функцию, нет очевидной оптимизации, посмотрите на разборку, и, возможно, она не встроила что-то в цикл, который я ожидал, или развернула, используйте SSE, как я хотел, или что-то в этом роде. Я бы предпочел попытаться «побудить» компилятор сделать это, в наихудшем случае, чем переписывать все это на ассемблере (который он никогда не оптимизирует, встроенный, будущий перенос и т.д.).