Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Язык программирования FPTL и средства работы с ним.pdf
Скачиваний:
30
Добавлен:
28.06.2014
Размер:
226.48 Кб
Скачать

Для поиска мемоизуемых ФП интерпретатор имеет режим работы, в котором, одновременно с вычислением значения программы, ведется накопление истории вызовов всех ФП в программе. По окончании работы программы строится таблица, в которой для каждой ФП сохраняется общее кол-во ее вычислений и кол-во различных аргументов, с которыми вызывалась данная ФП. Чем больше отношение первого числа ко второму, тем выше оценка, получаемая ФП. Также на окончательное решение мемоизовать ФП или нет влияет оценка ее структурной сложности. Чем выше эта оценка, тем больше шансов у ФП быть мемоизуемой. В данный момент интерпретатор записывает предлагаемые для мемоизации названия ФП в файл настроек funcs.xml.

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

Как мы поддерживаем любое кол-во машин в кластере и процессоров на машине?

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

Вреальности кол-во одновременно работающих на разных узлах (как компьютерах, так и процессорах) интерпретаторов на данный момент ограничивается алгоритмом поиска пересылаемых ФП и свойствами самой программы (закон Амдала). Задача нахождения оптимального порога оценки стоимости структурной сложности ФП как функции от имеющегося кластера пока стоит открытой.

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

Массивы в языке FPTL

На данный момент у нас есть реализация встроенной в язык поддержки работы с массивами на одномашинном одноядерном интерпретаторе. Мы планируем перенести эту возможность и в сетевой интерпретатор.

Компилятор программ языка FPTL

Основной целью проекта по созданию компилятора с языка FPTL является поиск оптимизаций для интерпретатора языка. Компилятор переводит программы с языка FPTL на исходный код на языке Java. Затем он вызывает компилятор, поставляемый в комплекте JDK для окончательной обработки программы.

В рамках проекта компилятора были разработаны (или все еще разрабатываются) следующие подпроекты:

Оптимизирующий компилятор на язык Java (строит по данным языка FPTL классы Java, делает наиболее очевидные оптимизации кода)

Преобразователь функций, имеющих хвостовую рекурсию (tail recursive functions) из рекурсивного вида в итеративный

Преобразователь программ FPTL, подставляющий inline все простые ФП в программе (с очень низкой вычислительной сложностью)

Поиск переносимых и мемоизуемых ФП программы за счет анализа структурной сложности программы

Механизм компиляции, переноса на удаленные машины и загрузки и выполнения там откомпилированных классов

Механизм автоматического вывода типов программы

«Клей» между компилированными и интерпретируемыми программами (генерация кода, трансформирующего данные в виде, используемом интерпретатором, в вид, используемый в данной компилированной программе) (частично реализован)

Добавление возможности генерации многопоточного кода в компилятор (не реализовано)

Добавление функционала по работе с массивами в компилятор (не реализовано)

Поддержка мемо-функций в компилируемом коде (не реализовано)

Генерация программ на языке FPTL на основе математических формул. Планируется, что форматом формул будет являться формат набора TeX.

Разработка компилятора имеет перед собой по крайней мере две цели:

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

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

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

Характеристики компилятора

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

private int ACK(int arg0, int arg1)

{

if ((arg0 == 0))

{

return arg1 + 1;

}

else

{

if ((arg1 == 0))

{

return ACK(arg0 - 1, 1);

}

else

{

return ACK(arg0 - 1, ACK(arg0, arg1 - 1));

}

}

}

Вкомпиляторе реализованы следующие оптимизации:

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

Вычисление константных выражений (реализовано для большинства встроенных функций). Все константные выражения в программе вычисляются на этапе компиляции. К константным относятся и арифметические тождества типа “a / a = 1,

если a != 0”

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

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

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

Делаются следующие простые преобразования частных случаев условного оператора: “if(condition)var=true else var=false => var=condition”; “if(condition)var=false else var=true => var=!condition”; “if (condition) branch else branch => branch”. В последнем случае требуется полное совпадение обоих ветвей условного оператора.

Если функция condition условного оператора является одной из двух булевских констант, то генерируется лишь соответствующая ветвь условного оператора.

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

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