Из Википедии, бесплатной энциклопедии
  (Перенаправлено из Thunking )
Перейти к навигации Перейти к поиску

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

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

Фон [ править ]

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

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

Приложения [ править ]

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

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

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

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

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

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

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

класс  A  {  общедоступный :  виртуальный  int  Access ()  const  {  возвращаемое  значение_ ;  } частный :  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объекта этого типа через ссылку того же типа. Класс C будет иметь дополнительную таблицу диспетчеризации, используемую для вызова Accessобъекта типа C через ссылку типа B. Выражение b->Access()будет использовать собственную таблицу диспетчеризации B или дополнительную таблицу C, в зависимости от типа объекта b, на который ссылается. Если он ссылается на объект типа C, компилятор должен гарантировать, что Accessреализация C получит адрес экземпляра для всего объекта C, а не унаследованной части B этого объекта. [7]

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

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

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

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

Совместимость [ править ]

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

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

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

Оверлеи и динамические ссылки [ править ]

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

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

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

Thunk технологии [ править ]

  • Интерфейс защищенного режима DOS (DPMI)
  • Службы защищенного режима DOS (DPMS)
  • J / Direct
  • Microsoft Layer для Unicode
  • Сервисы вызова платформы
  • Win32s
  • Windows в Windows
  • WoW64
  • libffi

Понятия, связанные с данным [ править ]

  • Анонимная функция
  • Будущее и обещания
  • Удаленный вызов процедур
  • Прокладка (вычисление)
  • Батут (компьютерный)
  • Приводимое выражение

Заметки [ править ]

  1. ^ Преобразователь - это ранний ограниченный тип закрытия . Среда, переданная преобразователю, - это среда выражения, а не вызываемая подпрограмма. [2]

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

  1. Эрик Раймонд отвергает «пару звукоподражательных мифов о происхождении этого термина» и цитирует изобретателей преобразователя, напоминая, что термин «был придуман после того, как они поняли (в первые часы после нескольких часов обсуждения), что тип аргумент в Алголе-60 можно было выяснить заранее, немного подумав во время компиляции [...] Другими словами, он «уже был продуман»; поэтому он был назван thunk , что является «прошедшим временем «думай» в два часа ночи ». См .: Raymond, Eric S. (1996). Raymond, Eric S. (ed.). The New Hacker's Dictionary . MIT Press. p. 445. ISBN 9780262680929. Проверено 25 мая 2015 .
  2. ^ ET Утюги (1961-01-01). «Комментарии к реализации рекурсивных процедур и блоков в Алголе». Коммуникации ACM . Ассоциация вычислительной техники (ACM). 4 (1): 65–69. DOI : 10.1145 / 366062.366084 . ISSN 0001-0782 . 
  3. ^ Ингерман, PZ (1961-01-01). «Thunks: способ составления операторов процедур с некоторыми комментариями к объявлениям процедур». Коммуникации ACM . Ассоциация вычислительной техники (ACM). 4 (1): 55–58. DOI : 10.1145 / 366062.366084 . ISSN 0001-0782 . 
  4. ^ Скотт, Майкл (2009). Прагматика языка программирования . п. 395.
  5. ^ Марлоу, Саймон (2013). Параллельное и параллельное программирование в Haskell . п. 10.
  6. ^ Queinnec, Кристиан (2003). Лисп мелкими кусочками . п. 176.
  7. ^ Страуструп, Бьярне (осень 1989). «Множественное наследование для C ++» (PDF) . Вычислительные системы . USENIX . 1 (4) . Проверено 4 августа 2014 .
  8. ^ Дризен, Карел; Хёльцле, Урс (1996). «Прямая стоимость вызовов виртуальных функций в C ++» (PDF) . УПСЛА . Проверено 24 февраля 2011 . Цитировать журнал требует |journal=( помощь )
  9. ^ Calcote, Джон (май 1995). «Преобразование: использование 16-битных библиотек в OS / 2 2.0» . Журнал для разработчиков OS / 2 . 7 (3).
  10. ^ Король, Адриан (1994). Внутри Microsoft Windows 95 (2-е изд.). Редмонд, Вашингтон, США: Microsoft Press . ISBN 1-55615-626-X.
  11. ^ Руководство программиста по Microsoft Windows 95: Основные темы по программированию для Windows от группы разработчиков Microsoft Windows . Технический справочник (1-е изд.). Редмонд, Вашингтон, США: Microsoft Press . 1995-07-01. ISBN 1-55615-834-3. Проверено 26 мая 2016 .
  12. ^ Hazzah, Карен (1997). Написание Windows VxD и драйверов устройств - секреты программирования для драйверов виртуальных устройств (2-е издание, 2-е изд.). Лоуренс, Канзас, США: R&D Books / Miller Freeman, Inc. ISBN  0-87930-438-3.
  13. ^ Kauler, Барри (август 1997). Язык ассемблера Windows и системное программирование - 16- и 32-разрядное низкоуровневое программирование для ПК и Windows (2-е изд.). Лоуренс, Канзас, США: R&D Books / Miller Freeman, Inc. ISBN  0-87930-474-X.
  14. ^ "Почему вы не можете переключаться между 32-битной и 64-битной Windows?" . Старая новая вещь . 2008-10-20.
  15. ^ Брайт, Уолтер (1990-07-01). «Виртуальная память для 640К DOS» . Журнал доктора Добба . Проверено 6 марта 2014 .
  16. ^ Левин, Джон Р. (2000) [октябрь 1999]. Линкеры и загрузчики . Серия Морган Кауфманн в программной инженерии и программировании (1-е изд.). Сан-Франциско, США: Морган Кауфманн . ISBN 1-55860-496-0. OCLC  42413382 . ISBN 978-1-55860-496-4 . Архивировано 05 декабря 2012 года . Проверено 12 января 2020 . Код: [1] [2] Ошибки: [3]