Thunk

редактировать
Найдите thunk в Wiktionary, бесплатном словаре.
Вычислительный термин

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

Термин возник как юмористический, неправильный, причастие прошедшего времени слова «думать». То есть «переходное значение» становится доступным после того, как его процедура вычисления продумана или выполнена.

Содержание
  • 1 Предпосылки
  • 2 Приложения
    • 2.1 Функциональное программирование
    • 2.2 Объектно-ориентированное программирование
    • 2.3 Численные расчеты, требующие оценки в нескольких точках
    • 2.4 Функциональная совместимость
    • 2.5 Наложения и динамическое связывание
  • 3 См. Также
    • 3.1 Thunk-технологии
    • 3.2 Понятия, связанные с данным
  • 4 Примечания
  • 5 Ссылки
Предпосылки

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

Простая реализация «вызова по имени» может заменить код выражения аргумента для каждого появления соответствующего параметра в подпрограмме, но это может создать несколько версий подпрограммы и несколько копий кода выражения.. В качестве улучшения компилятор может сгенерировать вспомогательную подпрограмму, называемую преобразователем, которая вычисляет значение аргумента. Адрес и окружение этой вспомогательной подпрограммы затем передаются исходной подпрограмме вместо исходного аргумента, где ее можно вызывать столько раз, сколько необходимо. Питер Ингерман впервые описал преобразователи в отношении языка программирования ALGOL 60, который поддерживает вычисление по имени.

Приложения

Функциональное программирование

Хотя индустрия программного обеспечения в значительной степени стандартизировала оценку «вызов по значению» и вызов по ссылке, активное изучение вызова по имени продолжалось в сообществе функционального программирования. Это исследование произвело серию языков программирования с ленивым оцениванием, в которых некоторый вариант вызова по имени является стандартной стратегией оценки. Компиляторы для этих языков, такие как Glasgow Haskell Compiler, в значительной степени полагались на преобразователи с добавленной функцией, заключающейся в том, что преобразователи сохраняют свой первоначальный результат, чтобы избежать его повторного вычисления; это известно как мемоизация или вызов по необходимости.

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

Ниже приводится простая демонстрация на JavaScript (ES6):

// 'hypot' - это двоичная функция const hypot = (x, y) =>Math.sqrt (x * x + y * y); // thunk - это функция, которая не принимает аргументов и при вызове выполняет потенциально дорогостоящую // операцию (вычисление квадратного корня в этом примере) и / или вызывает некоторый побочный эффект const thunk = () =>гипотеза (3, 4); // преобразователь может быть передан без оценки... doSomethingWithThunk (thunk); //... или оценивается thunk (); // === 5

Объектно-ориентированное программирование

Преобразователи полезны в платформах объектно-ориентированного программирования, которые позволяют от класс до наследовать несколько интерфейсы, что приводит к ситуациям, когда один и тот же метод может быть вызван через любой из нескольких интерфейсов. Следующий код иллюстрирует такую ​​ситуацию в C ++.

class A {public: virtual int Access () const {return value_; } частный: int value_; }; class B {public: виртуальный int Access () const {возвращаемое значение_; } частный: int value_; }; класс C: публичный A, публичный B {публичный: int Access () const override {return better_value_; } частный: int better_value_; }; int Используйте (B * b) {return b->Access (); } int main () {//... B some_b; Используйте (some_b); C some_c; Используйте (some_c); }

В этом примере код, сгенерированный для каждого из классов A, B и C, будет включать таблицу диспетчеризации, которую можно использовать для вызова Accessдля объекта этого type через ссылку того же типа. Класс C будет иметь дополнительную таблицу диспетчеризации, используемую для вызова Accessдля объекта типа C через ссылку типа B. Выражение b->Access ()будет использовать собственное выражение B. таблица отправки или дополнительная таблица C, в зависимости от типа объекта, на который ссылается b. Если он ссылается на объект типа C, компилятор должен гарантировать, что реализация C Accessполучит адрес экземпляра для всего объекта C, а не унаследованной части B этого объекта.

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

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

Численные вычисления, требующие оценки в нескольких точках

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

Взаимодействие

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

Большая часть литературы по преобразователям совместимости относится к различным платформам Wintel, включая MS-DOS, OS / 2, Windows и .NET, а также переход от 16-битной к 32-битной адресации памяти. По мере того, как клиенты переходили с одной платформы на другую, переходники были необходимы для поддержки устаревшего программного обеспечения, написанного для старых платформ.

Переход от 32-битного кода к 64-битному на x86 также использует форму преобразования (WoW64). Однако, поскольку адресное пространство x86-64 больше, чем доступное для 32-битного кода, старый механизм «универсального преобразователя» не мог использоваться для вызова 64-битного кода из 32-битного кода. Единственный случай, когда 32-битный код вызывает 64-битный код, - это преобразование WoW64 API Windows в 32-битное.

Наложения и динамическое связывание

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

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

См. Также

Thunk-технологии

Связанные концепции

Примечания
Ссылки
Последняя правка сделана 2021-06-11 11:18:27
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте