~~~~~~~~~~~~~~~~~~~~ Arc.Ask3.Ru ~~~~~~~~~~~~~~~~~~~~~ 
Номер скриншота №:
✰ 6ED6D8BC0ED0B797117AB37741BF07A1__1705897020 ✰
Заголовок документа оригинал.:
✰ Interprocedural optimization - Wikipedia ✰
Заголовок документа перевод.:
✰ Межпроцедурная оптимизация — Википедия ✰
Снимок документа находящегося по адресу (URL):
✰ https://en.wikipedia.org/wiki/Interprocedural_optimization ✰
Адрес хранения снимка оригинал (URL):
✰ https://arc.ask3.ru/arc/aa/6e/a1/6ed6d8bc0ed0b797117ab37741bf07a1.html ✰
Адрес хранения снимка перевод (URL):
✰ https://arc.ask3.ru/arc/aa/6e/a1/6ed6d8bc0ed0b797117ab37741bf07a1__translat.html ✰
Дата и время сохранения документа:
✰ 21.06.2024 10:37:40 (GMT+3, MSK) ✰
Дата и время изменения документа (по данным источника):
✰ 22 January 2024, at 07:17 (UTC). ✰ 

~~~~~~~~~~~~~~~~~~~~~~ Ask3.Ru ~~~~~~~~~~~~~~~~~~~~~~ 
Сервисы Ask3.ru: 
 Архив документов (Снимки документов, в формате HTML, PDF, PNG - подписанные ЭЦП, доказывающие существование документа в момент подписи. Перевод сохраненных документов на русский язык.)https://arc.ask3.ruОтветы на вопросы (Сервис ответов на вопросы, в основном, научной направленности)https://ask3.ru/answer2questionТоварный сопоставитель (Сервис сравнения и выбора товаров) ✰✰
✰ https://ask3.ru/product2collationПартнерыhttps://comrades.ask3.ru


Совет. Чтобы искать на странице, нажмите Ctrl+F или ⌘-F (для MacOS) и введите запрос в поле поиска.
Arc.Ask3.ru: далее начало оригинального документа

Межпроцедурная оптимизация — Википедия Jump to content

Межпроцедурная оптимизация

Из Википедии, бесплатной энциклопедии

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

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

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

Для языков, которые компилируются пофайлово, эффективный IPO для единиц перевода (файлов модулей) требует знания «точек входа» программы, чтобы всю оптимизацию программы ( WPO можно было запустить ). Во многих случаях это реализуется как проход оптимизации времени компоновки ( LTO ), поскольку компоновщику видна вся программа.

Анализ [ править ]

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

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

Предположим, что существует процедура, которая вычисляет F(x), и что F является чистой функцией , и код запрашивает результат F(6), а затем снова F(6). Эта вторая оценка почти наверняка не нужна: вместо этого результат можно было сохранить и использовать позже. Эта простая оптимизация срывается в тот момент, когда реализация F(x) становится нечистой; то есть его выполнение включает ссылки на параметры, отличные от явного аргумента 6, который был изменен между вызовами, или побочные эффекты, такие как печать некоторого сообщения в журнал, подсчет количества оценок, накопление затраченного процессорного времени, подготовка внутренних таблиц. чтобы облегчить последующие вызовы связанных параметров и т.д. Утрата этих побочных эффектов из-за отсутствия оценки во второй раз может быть приемлемой, а может и нет.

В более общем смысле, помимо оптимизации, второй причиной использования процедур является избежание дублирования кода, которое будет давать одни и те же или почти одинаковые результаты при каждом выполнении процедуры. Таким образом, общий подход к оптимизации состоит в том, чтобы обратить это вспять: некоторые или все вызовы определенной процедуры заменяются соответствующим кодом с соответствующей заменой параметров. Затем компилятор попытается оптимизировать результат.

WPO и LTO [ править ]

Полная оптимизация программы ( WPO ) — это оптимизация программы компилятором с использованием информации обо всех модулях программы. Обычно оптимизация выполняется для каждого модуля, «компилятора», на основе ; но этот подход, хотя его легче писать и тестировать и он менее требовательн к ресурсам во время самой компиляции, он не позволяет быть уверенным в безопасности ряда оптимизаций, таких как агрессивная встраивание, и, следовательно, не может их выполнить, даже если они на самом деле окажутся прирост эффективности, который не меняет семантику создаваемого объектного кода.

Оптимизация времени компоновки ( LTO ) — это тип оптимизации программы, выполняемой компилятором программы во время компоновки . Оптимизация времени компоновки актуальна в языках программирования, которые компилируют программы пофайлово, а затем связывают эти файлы вместе (например, C и Fortran ), а не все сразу (как, например, Java , в компиляция (JIT)).

После того как все файлы скомпилированы по отдельности в объектные файлы , традиционно компилятор связывает (объединяет) объектные файлы в один исполняемый файл . Однако в LTO, реализованном коллекцией компиляторов GNU (GCC) и LLVM , компилятор может выгружать свое промежуточное представление (IR), то есть байт-код GIMPLE или бит-код LLVM, соответственно, так что все различные единицы компиляции, которые будут отправлены в Создание одного исполняемого файла может быть оптимизировано как один модуль, когда ссылка наконец произойдет. Это расширяет возможности межпроцедурной оптимизации, охватывая всю программу (или, скорее, все, что видно во время компоновки). Благодаря оптимизации времени компоновки компилятор может применять различные формы межпроцедурной оптимизации ко всей программе, что позволяет провести более глубокий анализ, дополнительную оптимизацию и, в конечном итоге, повысить производительность программы.

На практике LTO не всегда оптимизирует всю программу — библиотечные функции , особенно динамически связанные общие объекты, намеренно исключены, чтобы избежать чрезмерного дублирования и обеспечить возможность обновления. Статическое связывание, естественно, соответствует концепции LTO, но оно работает только с библиотечными архивами, содержащими IR-объекты, а не с объектными файлами, содержащими только машинный код. [1] Из-за проблем с производительностью даже не весь модуль всегда используется напрямую — программа может быть разделена по принципу LTO в стиле «разделяй и властвуй», например, WHOPR GCC. [2] И, конечно же, когда создаваемая программа сама является библиотекой, оптимизация сохранит каждый доступный извне (экспортированный) символ, не пытаясь слишком усердно удалить их как часть DCE. [1]

Гораздо более ограниченная форма WPO по-прежнему возможна без LTO, примером чему является GCC. -fwhole-programвыключатель. Этот режим заставляет GCC предполагать, что компилируемый модуль содержит точку входа всей программы, так что все остальные функции в нем не используются извне и могут быть безопасно оптимизированы. Поскольку это применимо только к одному модулю, оно не может по-настоящему охватить всю программу. Его можно комбинировать с LTO в смысле одного большого модуля, что полезно, когда компоновщик не сообщает GCC о том, какие точки входа или символы используются извне. [1]

Пример [ править ]

 программы  Пример  ; 
     целое число   б  ;                 {Глобальная переменная для процедуры Silly.} 
    Процедура   Silly  (  a  ,  x  ) 
       if   x   <   0   then   a  :=  x   +   b   else   a  :=-  6  ; 
     Конец   глупости  ;                 {Ссылка на b, а не на параметр, вообще делает Silly "нечистым".} 
    целое число   a  ,  x  ;               {Эти переменные видны Глупому только в том случае, если они являются параметрами.} 
    x  :=  7  ;    б  :=  5  ; 
     Глупый  (  а  ,  х  )  ;    написать  (  х  )  ; 
     Глупый  (  х  ,  а  )  ;    написать  (  х  )  ; 
     Глупый  (  б  ,  б  )  ;    написать  (  б  )  ; 
  Конец   примера  ; 

Если параметры Silly передаются по значению , действия процедуры не влияют на исходные переменные, и поскольку Silly ничего не делает со своей средой (чтение из файла, запись в файл, изменение глобальных переменных , таких как b и т. д.) .) его код и все вызовы могут быть полностью оптимизированы, оставив значение неопределенным (что не имеет значения), так что только write операторы остаются, просто печатая постоянные значения.

Если вместо этого параметры передаются по ссылке , то действие над ними в Silly действительно влияет на оригиналы. Обычно это делается путем передачи процедуре машинного адреса параметров, чтобы изменения процедуры относились к исходной области хранения. Таким образом, в случае передачи по ссылке процедура Silly действительно имеет эффект. Предположим, что его вызовы развернуты на месте с параметрами, идентифицируемыми по адресу: код равен

х  :=  7  ;    б  :=  5  ; 
  если   x   <   0   , то   a  :=  x   +   b   else   a  :=-  6  ;    написать  (  х  )  ;      {a изменено.} 
 если   a   <   0   , то   x  :=  a   +   b   else   x  :=-  6  ;    написать  (  х  )  ;      {Потому что параметры поменялись местами.} 
 if   b   <   0   then   b  :=  b   +   b   else   b  :=-  6  ;    написать  (  б  )  ;      {Две версии переменной b в Silly плюс глобальное использование.} 

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

х  :=  7  ;    б  :=  5  ; 
  а  :=-  6  ;    написать  (  7  )  ;               {b не упоминается, поэтому такое использование остаётся «чистым».} 
 x  :=-  1  ;    написать  (  -1  )  ;              {ссылка на b...} 
 b  :=-  6  ;    написать  (  -6  )  ;              {b модифицируется посредством проявления его параметра.} 

А поскольку присвоения a , b и x ничего не передают внешнему миру - они не появляются ни в выходных операторах, ни в качестве входных данных для последующих вычислений (результаты которых, в свою очередь, приводят к выходным данным, иначе они также не нужны) - существует в этом коде тоже нет смысла, и поэтому результат

написать  (  7  )  ; 
  написать  (  -1  )  ; 
  написать  (  -6  )  ; 

Вариантным методом передачи параметров, которые выглядят как «по ссылке», является копирование-вход-копирование, при котором процедура работает с локальной копией параметров, значения которых копируются обратно в оригиналы при выходе из процедуры. Если процедура имеет доступ к одному и тому же параметру, но разными способами, как при вызовах, таких как Silly(a,a) или Silly(a,b) , могут возникнуть несоответствия. Итак, если бы параметры передавались путем копирования и копирования в порядке слева направо, тогда Silly(b,b) расширился бы до

р1  :=  б  ;    р2  :=  б  ;                                  {Скопировать. Локальные переменные p1 и p2 равны.} 
 if   p2   <   0   then   p1  :=  p2   +   b   else   p1  :=-  6  ;         {Таким образом, p1 больше не может быть равным p2.} 
 b  :=  p1  ;    б  :=  р2  ;                                  {Скопировать.   В порядке слева направо значение из p1 перезаписывается.} 

И в этом случае копирование значения p1 (которое было изменено) в b бессмысленно, поскольку оно немедленно перезаписывается значением p2 , значение которого не было изменено внутри процедуры, по сравнению с исходным значением b , и поэтому третье утверждение становится

написать  (  5  )  ;             {Не -6} 

Такие различия в поведении, вероятно, вызовут недоумение, усугубленное вопросами о порядке копирования параметров: будет ли копирование слева направо при выходе, а также при входе? Эти детали, вероятно, недостаточно подробно объяснены в руководстве по компилятору, а если и да, то они, скорее всего, будут проигнорированы как не имеющие отношения к непосредственной задаче и давно забыты к моменту возникновения проблемы. Если (что вполне вероятно) временные значения предоставляются через схему хранения стека, то вполне вероятно, что процесс обратного копирования будет в порядке, обратном копированию, что в этом примере будет означать, что p1 будет последним. Вместо этого значение возвращается в b .

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

Другими словами, тщательно написанная тестовая программа может сообщать о том, передаются ли параметры по значению или по ссылке, и если используются, то какая схема копирования и копирования. Однако вариации безграничны: простые параметры могут передаваться путем копирования, тогда как большие агрегаты, такие как массивы, могут передаваться по ссылке; простые константы, такие как ноль, могут генерироваться специальными машинными кодами (такими как Clear или LoadZ), в то время как более сложные константы могут храниться в памяти, помеченной как доступная только для чтения, и любая попытка ее изменения приведет к немедленному завершению программы и т. д.

В общем [ править ]

Этот пример чрезвычайно прост, хотя сложности уже очевидны. Скорее всего, это будет случай множества процедур, имеющих множество выводимых или объявленных программистом свойств, которые могут позволить оптимизации компилятора найти некоторые преимущества. Любой параметр процедуры может быть доступен только для чтения, для записи, для чтения и записи или вообще игнорироваться, что приводит к появлению таких возможностей, как константы, не нуждающиеся в защите с помощью временных переменных, но то, что происходит при любом данном вызове, вполне может зависеть от сложная сеть соображений. Другие процедуры, особенно процедуры, подобные функциям, будут иметь определенное поведение, которое при определенных вызовах может позволить избежать некоторой работы: например, функция Gamma , если она вызывается с целочисленным параметром, может быть преобразована в вычисление, включающее целочисленные факториалы.

Некоторые компьютерные языки допускают (или даже требуют) утверждения относительно использования параметров и могут дополнительно предлагать возможность объявить, что переменные имеют свои значения, ограниченные некоторым набором (например, 6 < x ≤ 28), тем самым обеспечивая дополнительную основу для процесс оптимизации, а также обеспечение целесообразных проверок связности исходного кода для обнаружения ошибок. Но этого никогда не бывает достаточно — только некоторым переменным можно задать простые ограничения, в то время как другие потребуют сложных спецификаций: как можно указать, что переменная P должна быть простым числом, и если да, включено или нет значение 1? Сразу возникают сложности: каковы допустимые диапазоны для дня месяца D, учитывая, что M — это номер месяца? И все ли нарушения достойны немедленного прекращения? Даже если со всем этим можно справиться, какая от этого будет польза? И какой ценой? Полные спецификации будут означать переформулирование функции программы в другой форме, и, независимо от времени, которое компилятор потратит на их обработку, они, таким образом, будут подвержены ошибкам. Вместо этого допускаются только простые спецификации с проверкой диапазона во время выполнения.

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

История [ править ]

В процедурных языках, таких как ALGOL , межпроцедурный анализ и оптимизация, похоже, вошли в коммерческую практику в начале 1970-х годов. от IBM Оптимизирующий компилятор PL/I выполнил межпроцедурный анализ, чтобы понять побочные эффекты как вызовов процедур, так и исключений (в терминах PL/I это означает «при условиях»). [3] и в статьях Фрэн Аллен . [4] [5] Работа над компиляцией языка программирования APL обязательно была межпроцедурной. [6] [7]

Методы межпроцедурного анализа и оптимизации были предметом академических исследований в 1980-х и 1990-х годах. Они вновь появились в мире коммерческих компиляторов в начале 1990-х годов с компиляторами как от Convex Computer Corporation («Компилятор приложений» для Convex C4), так и от Ardent (компилятор для Ardent Titan ). Эти компиляторы продемонстрировали, что технологии можно сделать достаточно быстрыми, чтобы их можно было использовать в коммерческом компиляторе; впоследствии межпроцедурные методы появились в ряде коммерческих и некоммерческих систем.

Флаги и реализация [ править ]

Unix-подобный [ править ]

Коллекция компиляторов GNU включает встраивание функций на всех уровнях оптимизации. В -O1 это касается только тех, кого вызвали только один раз ( -finline-functions-once), в -O2 это ограничение ослаблено ( -finline-functions). По умолчанию это поведение только для одного файла, но с оптимизацией времени компоновки. -flto это становится целой программой. [1] Интерфейс командной строки Clang аналогичен интерфейсу GCC, за исключением того, что здесь нет -fwhole-program вариант. [8]

Объектные файлы, созданные LTO, содержат промежуточное представление (IR), специфичное для компилятора, которое интерпретируется во время компоновки. Чтобы убедиться, что это хорошо работает со статическими библиотеками , новые компоновщики GNU имеют интерфейс «плагина компоновщика», который позволяет компилятору при необходимости преобразовывать объектные файлы в форму машинного кода. Этот плагин также помогает управлять процессом LTO в целом. В качестве альтернативы можно создать «толстый» объект LTO, содержащий как машинный код, так и IR, но это займет больше места. [1]

Поскольку и GCC, и LLVM (clang) способны создавать IR из различных языков программирования, IPO во время компоновки может происходить даже за пределами языковых границ. Чаще всего это демонстрируется на C и C++. [9] но LLVM делает возможным работу с Rust и всеми другими компиляторами на основе LLVM. [10]

Варианты без LTO [ править ]

GCC и Clang по умолчанию выполняют IPO на уровне оптимизации 2. Однако степень оптимизации ограничена, когда LTO отключен, поскольку IPO может происходить только внутри объектного файла, а нестатические функции невозможно исключить. Последняя проблема имеет решение, отличное от LTO: -fwhole-program переключатель можно использовать, предполагая, что только main() нестатичен, т.е. виден снаружи. [11]

Другой метод, не связанный с LTO, — это «разделы функций» ( -ffunction-sectionsв GCC и Clang). Помещая каждую функцию в отдельный раздел объектного файла, компоновщик может выполнить удаление мертвого кода без IR, удалив разделы, на которые нет ссылок (используя опцию компоновщика --gc-sections). [12] Аналогичная опция доступна для переменных, но она приводит к созданию гораздо худшего кода.

Другое [ править ]

Компиляторы Intel C/C++ допускают IPO всей программы. Флаг включения межпроцедурной оптимизации для одного файла: -ip, флаг включения межпроцедурной оптимизации для всех файлов в программе: -ipo. [13] [14]

Компилятор MSVC , интегрированный в Visual Studio, также поддерживает межпроцедурную оптимизацию всей программы. [15]

Независимый от компилятора интерфейс для обеспечения межпроцедурной оптимизации всей программы осуществляется через INTERPROCEDURAL_OPTIMIZATION свойство в CMake . [16]

См. также [ править ]

Ссылки [ править ]

  1. ^ Перейти обратно: а б с д Это «Параметры оптимизации» . Использование коллекции компиляторов GNU (GCC) . Оптимизация времени компоновки не требует наличия всей программы для работы. Если программа не требует экспорта каких-либо символов, можно объединить -flto и -fwhole-program, чтобы позволить межпроцедурным оптимизаторам использовать более агрессивные предположения, которые могут привести к улучшению возможностей оптимизации. Использование -fwhole-program не требуется, когда плагин компоновщика активен (см. -fuse-linker-plugin).
  2. ^ «Обзор ЛТО» . Внутреннее устройство коллекции компиляторов GNU (GCC) .
  3. ^ Томас К. Спиллман, «Выявление побочных эффектов в оптимизирующем PL/I компиляторе», в Proceedings of IFIPS 1971 , North-Holland Publishing Company, страницы 376-381.
  4. ^ Фрэнсис Э. Аллен, «Межпроцедурный анализ потоков данных», Труды IFIPS, 1974.
  5. ^ Фрэнсис Э. Аллен и Джек Шварц, «Определение взаимосвязей потоков данных в наборе процедур», Отчет IBM Research Report RC 4989, август 1974 г.
  6. ^ Филип Абрамс , «Машина APL», факультет компьютерных наук Стэнфордского университета, отчет STAN-CS-70-158, февраль 1970 г.
  7. ^ Терренс К. Миллер, «Предварительная компиляция: конструкция компилятора APL», доктор философии. Диссертация, Йельский университет, 1978 г.
  8. ^ «Справочник по аргументам командной строки Clang» . Документация Clang 11 .
  9. ^ Рейнхарт, Джонатан. «Может ли LTO для gcc или clang оптимизироваться с помощью методов C и C++» . Переполнение стека .
  10. ^ Веристер, Майкл (19 сентября 2019 г.). «Устранение разрыва: межъязыковой LTO между Rust и C/C++» . Блог разработчиков LLVM .
  11. ^ «Параметры оптимизации» . Использование коллекции компиляторов GNU (GCC) .
  12. ^ «Функциональные разделы» . elinux.org .
  13. ^ «Документация по компилятору Intel 8» . Архивировано из оригинала 21 сентября 2006 г. Проверено 13 февраля 2007 г.
  14. ^ Intel Visual Fortran Compiler 9.1, стандартная и профессиональная версии, для Windows * — Intel Software Network
  15. ^ «/GL (Вся оптимизация программы)» . Документы Майкрософт . 12 марта 2019 г. Проверено 26 января 2020 г.
  16. ^ «ИНТЕРПРОЦЕДУРАЛЬНАЯ_ОПТИМИЗАЦИЯ» . Документация CMake 3.17.2 .

Внешние ссылки [ править ]

Arc.Ask3.Ru: конец оригинального документа.
Arc.Ask3.Ru
Номер скриншота №: 6ED6D8BC0ED0B797117AB37741BF07A1__1705897020
URL1:https://en.wikipedia.org/wiki/Interprocedural_optimization
Заголовок, (Title) документа по адресу, URL1:
Interprocedural optimization - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть, любые претензии не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, денежную единицу можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)