В вычислениях , сглаживание описывает ситуацию , в которой данные о местоположении в памяти могут быть доступны через различные символические имена в программе. Таким образом, изменение данных с помощью одного имени неявно изменяет значения, связанные со всеми псевдонимами, чего программист может не ожидать. В результате псевдонимы особенно затрудняют понимание, анализ и оптимизацию программ. Анализаторы псевдонимов предназначены для получения и вычисления полезной информации для понимания псевдонимов в программах.
Примеры
Переполнение буфера
Например, в большинстве реализаций языка программирования C не выполняется проверка границ массива . Затем можно использовать реализацию языка программирования компилятором и соглашения на языке ассемблера в компьютерной архитектуре для достижения эффектов сглаживания путем записи вне массива (тип переполнения буфера ). Это вызывает неопределенное поведение в соответствии со спецификацией языка C; однако многие реализации C будут демонстрировать эффекты наложения имен, описанные здесь.
Если в стеке создается массив с переменной, размещенной в памяти непосредственно рядом с этим массивом , можно индексировать за пределами массива и напрямую изменять переменную, изменяя соответствующий элемент массива. Например, если есть int
массив размером 2 (для этого примера, вызывая его arr
), рядом с другой int
переменной (назовите ее i
) arr[2]
(т.е. 3-й элемент) будет назначен псевдоним, i
если они находятся рядом в памяти.
# включить int main () { int arr [ 2 ] = { 1 , 2 }; int i = 10 ; / * Записываем после окончания обр. Неопределенное поведение в стандарте C, в некоторых реализациях будет записывать в i. * / arr [ 2 ] = 20 ; printf ( "элемент 0:% d \ t " , arr [ 0 ]); // выводит 1 printf ( "element 1:% d \ t " , arr [ 1 ]); // выводит 2 printf ( "element 2:% d \ t " , arr [ 2 ]); // выводит 20, если сглаживание произошло printf ( "i:% d \ t \ t " , i ); // может также выводить 20, а не 10 из-за наложения имен, но компилятор может сохранить i в регистре и вывести 10 / * размер arr по-прежнему равен 2. * / printf ( "arr size:% d \ n " , sizeof ( arr ) / sizeof ( int )); }
Это возможно в некоторых реализациях C, потому что массив является блоком непрерывной памяти, а ссылки на элементы массива просто смещены от адреса начала этого блока, умноженного на размер одного элемента. Поскольку в C нет проверки границ, возможна индексация и адресация за пределами массива. Обратите внимание, что вышеупомянутое поведение псевдонимов является неопределенным . Некоторые реализации могут оставлять пространство между массивами и переменными в стеке, например, для выравнивания переменных по ячейкам памяти, которые кратны собственному размеру слова архитектуры . Стандарт C обычно не определяет, как данные должны быть размещены в памяти. (ИСО / МЭК 9899: 1999, раздел 6.2.6.1).
Для компилятора не будет ошибкой пропустить эффекты наложения имен для обращений, выходящих за пределы массива.
Указатели с псевдонимом
Другая разновидность псевдонимов может иметь место на любом языке, который может относиться к одному месту в памяти с более чем одним именем (например, с указателями ). См. Пример C алгоритма замены XOR, который является функцией; он предполагает, что два переданных ему указателя различны, но если они фактически равны (или являются псевдонимами друг друга), функция завершается ошибкой. Это обычная проблема с функциями, которые принимают аргументы-указатели, и их допуск (или отсутствие такового) к алиасингу должен быть тщательно задокументирован, особенно для функций, которые выполняют сложные манипуляции с переданными им областями памяти.
Указанный псевдоним
В некоторых случаях может быть желательно контролируемое поведение псевдонимов (то есть, поведение псевдонимов, которое указано, в отличие от того, что разрешено макетом памяти в C). Это обычная практика в Фортране . В некоторых конструкциях язык программирования Perl определяет поведение псевдонимов, например, в циклах. Это позволяет изменять определенные структуры данных напрямую с меньшим количеством кода. Например,foreach
мой @array = ( 1 , 2 , 3 );foreach my $ element ( @array ) { # Увеличивает $ element, тем самым автоматически # модифицируя @array, так как $ element имеет псевдонимы # для каждого из элементов @ array по очереди. $ element ++ ; }напечатать "@array \ n" ;
в результате распечатает «2 3 4». Если кто-то хочет обойти эффекты сглаживания, можно скопировать содержимое индексной переменной в другую и изменить копию.
Конфликты с оптимизацией
Оптимизаторам часто приходится делать консервативные предположения о переменных, когда возможно сглаживание. Например, знание значения переменной (например, x
5) обычно позволяет выполнять определенные оптимизации (например, постоянное распространение ). Однако компилятор не может использовать эту информацию после присвоения другой переменной (например, в C *y = 10
), потому что это может быть *y
псевдоним x
. Это могло произойти после задания вроде y = &x
. В результате этого присвоения *y
значение x также будет изменено, поэтому распространение информации, равной x
5, на следующие инструкции *y = 10
было бы потенциально неверным (если *y
это действительно псевдоним x
). Однако, если есть информация об указателях, процесс распространения константы может сделать запрос вроде: может x
быть псевдонимом *y
? Затем, если ответ отрицательный, x = 5
можно безопасно распространять.
Еще одна оптимизация, на которую влияет наложение псевдонимов, - это переупорядочение кода. Если компилятор решит, что x
это не псевдоним *y
, то код, который использует или изменяет значение, x
может быть перемещен перед назначением *y = 10
, если это улучшит планирование или позволит выполнить больше оптимизаций цикла .
Чтобы обеспечить такую оптимизацию предсказуемым образом, стандарт ISO для языка программирования C (включая его новую редакцию C99 , см. Раздел 6.5, параграф 7) указывает, что недопустимо (за некоторыми исключениями) доступ к той же ячейке памяти с использованием указателей различные виды. Поэтому компилятор может предположить, что такие указатели не являются псевдонимами. Это правило, известное как правило строгого псевдонима , иногда позволяет добиться впечатляющего увеличения производительности [1], но, как известно, нарушает некоторую корректность кода. Некоторые программные проекты намеренно нарушают эту часть стандарта C99. Например, Python 2.x сделал так , чтобы осуществить подсчет ссылок , [2] и необходимые изменения в основных структуры объекта в Python 3 , чтобы включить эту оптимизацию. Linux ядро делает это потому , что строгое сглаживание вызывает проблемы с оптимизацией коды встраиваемой. [3] В таких случаях при компиляции с помощью gcc эта опция -fno-strict-aliasing
вызывается, чтобы предотвратить нежелательную оптимизацию, которая может привести к непредвиденному коду.
Аппаратный псевдоним
Термин « наложение имен» также используется для описания ситуации, когда из-за выбора конструкции оборудования или отказа оборудования один или несколько доступных битов адреса не используются в процессе выбора памяти. [4] Это может быть конструктивное решение, если доступно больше адресных битов, чем необходимо для поддержки установленных устройств памяти. В случае отказа один или несколько адресных битов могут быть закорочены вместе или могут быть принудительно подключены к заземлению (логический 0) или к напряжению питания (логическая 1).
- Пример
В этом примере предполагается, что структура памяти состоит из 8 ячеек, требующих только 3 адресных строк (или битов ), поскольку 2 3 = 8). Биты адреса (с именами от A2 до A0) декодируются для выбора уникальных ячеек памяти следующим образом стандартным способом двоичного счетчика :
A2 | A1 | A0 | Место в памяти |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 |
0 | 1 | 0 | 2 |
0 | 1 | 1 | 3 |
1 | 0 | 0 | 4 |
1 | 0 | 1 | 5 |
1 | 1 | 0 | 6 |
1 | 1 | 1 | 7 |
В приведенной выше таблице каждая из 8 уникальных комбинаций адресных битов выбирает разные ячейки памяти. Однако, если бы один адресный бит (скажем, A2) был закорочен на землю, таблица была бы изменена следующим образом:
A2 | A1 | A0 | Место в памяти |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 |
0 | 1 | 0 | 2 |
0 | 1 | 1 | 3 |
0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 |
0 | 1 | 0 | 2 |
0 | 1 | 1 | 3 |
В этом случае, когда A2 всегда равен нулю, первые четыре ячейки памяти дублируются и снова появляются как вторые четыре. Ячейки памяти с 4 по 7 стали недоступны.
Если бы это изменение произошло с другим адресным битом, результаты декодирования были бы другими, но в целом эффект был бы таким же: потеря одного адресного бита сокращает доступное пространство памяти наполовину, что приводит к дублированию (псевдониму) оставшееся место.
Смотрите также
- Сглаживание
- Псевдонимы для использования этого слова в применении к обработке сигналов, включая компьютерную графику
Рекомендации
- ↑ Майк Эктон (01.06.2006). «Понимание строгого псевдонима» .
- ^ Нил Шеменауэр (17 июля 2003 г.). «Строгий псевдоним ANSI и Python» .
- ^ Линус Торвальдс (26 февраля 2003 г.). «Re: Неверная компиляция без -fno-strict-aliasing» .
- ^ Майкл Барр (27 июля 2012 г.). «Программное тестирование памяти» .
Внешние ссылки
- Понимание строгого псевдонима - статья Майка Эктона
- Псевдонимы, приведение указателей и gcc 3.3 - информационная статья в списке рассылки NetBSD
- Анализ псевдонимов на основе типов в C ++ - Информационная статья об анализе псевдонимов на основе типов в C ++
- Understand C / C ++ Strict Aliasing - статья о строгом псевдониме, изначально взятая из вики-страницы разработчика boost