λ-калькуляция: что является наиболее эффективным в представлении функций в памяти?


9

Я хотел бы сравнить производительность структур данных, закодированных функцией (Черча / Скотта), и классически закодированных (ассемблер / C).

Но прежде чем сделать это, мне нужно знать, насколько эффективно / может быть представление функции в памяти. Эта функция, конечно, может быть применена частично (иначе говоря, замыкание).

Я заинтересован как в текущем алгоритме кодирования, используемом в популярных функциональных языках (Haskell, ML), так и в наиболее эффективном, который может быть достигнут.


Бонус: есть ли такая кодировка, которая отображает функционально закодированные целые числа в собственные целые ( shortи intт. Д. В C). Это вообще возможно?


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


Все мои попытки Google провалились, может быть, я не знаю правильных ключевых слов.
Ford O.

Можете ли вы отредактировать вопрос, чтобы уточнить, что вы подразумеваете под «эффективным»? Эффективно для чего? Когда вы запрашиваете эффективную структуру данных, вам нужно указать, какие операции вы хотите выполнять над структурой данных, так как это влияет на выбор структуры данных. Или вы имеете в виду, что кодирование максимально эффективно использует пространство?
DW

1
Это довольно широко. Существует множество абстрактных машин для лямбда-исчисления, которые стремятся эффективно его выполнять (см., Например, SECD, CAM, Krivine's, STG). Кроме того, вам необходимо рассмотреть закодированные данные Черча / Скотта, что создает больше проблем. Например, в списках, закодированных Церковью, операция хвоста должна быть O (n) вместо O (1). Я думаю, что где-то читал, что существование кодировки для списков в Системе F с O (1) операциями head и tail все еще оставалось открытым вопросом.
Чи

@ DW Я говорю о производительности / накладных расходах. Например, с эффективным отображением кодирования по списку церкви и списку Хаскелла должно потребоваться одно и то же время.
Ford O.

Производительность для какой операции? Что вы хотите сделать с функциями? Вы хотите оценить эти функции по некоторой ценности? Один раз или оценить одну и ту же функцию по многим значениям? Делай что-нибудь еще с ними? Вы просто спрашиваете, как скомпилировать функцию (написанную на функциональном языке), чтобы она могла выполняться максимально эффективно?
DW

Ответы:


11

Дело в том, что на самом деле не так много возможностей для кодирования функций. Вот основные варианты:

  • Переписывание термина: вы сохраняете функции как их абстрактные синтаксические деревья (или некоторую их кодировку. Когда вы вызываете функцию, вы вручную пересекаете синтаксическое дерево, чтобы заменить его параметры аргументом. Это просто, но ужасно неэффективно с точки зрения времени и пространства ,

  • Замыкания: у вас есть некоторый способ представления функции, возможно, синтаксическое дерево, более вероятно, машинный код. И в этих функциях вы ссылаетесь на свои аргументы по ссылке в некотором роде. Это может быть указатель-смещение, это может быть целое число или индекс Де Брюина, это может быть имя. Затем вы представляете функцию как замыкание : функция «инструкции» (дерево, код и т. Д.) Соединяется со структурой данных, содержащей все свободные переменные функции. Когда функция фактически применяется, она каким-то образом знает, как искать свободные переменные в своей структуре данных, используя среды, арифметику указателей и т. Д.

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

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

Я заинтересован в использовании обоих популярных алгоритмов кодирования популярных функциональных языков (Haskell, ML)

Я не эксперт, но на 99% большинство ML-версий используют некоторые варианты замыканий, которые я описываю, хотя и с некоторой вероятностью оптимизации. Смотрите это для (возможно, устаревшей) перспективы.

Haskell делает что-то более сложное из-за ленивых вычислений: он использует переписывание графа без тегов .

а также в самом эффективном, который может быть достигнут.

Что наиболее эффективно? Не существует реализации, которая была бы наиболее эффективной для всех входов, поэтому вы получаете реализации, которые в среднем эффективны, но каждая из них будет лучше в разных сценариях. Так что нет определенного рейтинга наиболее или наименее эффективных.

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

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

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

Бонус: существует ли такое кодирование, которое отображает целые числа, закодированные в функции, на собственные целые числа (short, int и т. Д. В C). Это вообще возможно?

Нет, это невозможно. Проблема в том, что лямбда-исчисление не позволяет вам анализировать термины. Когда функция принимает аргумент того же типа, что и церковная цифра, она должна иметь возможность вызывать его, не изучая точное определение этой цифры. Это то же самое, что и с церковными кодировками: единственное, что вы можете с ними сделать, это позвонить им, и вы можете имитировать все полезное с этим, но не без затрат.

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

РЕДАКТИРОВАТЬ: С момента написания этого, мне стало известно о третьем варианте для реализации функций более высокого порядка: дефункциональность . Здесь каждый вызов функции превращается в большой switchоператор, в зависимости от того, какая лямбда-абстракция была задана как функция. Компромисс здесь заключается в том, что это целое преобразование программы: вы не можете скомпилировать части по отдельности, а затем связать их вместе таким образом, поскольку вам нужно заранее иметь полный набор лямбда-абстракций.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.