Численные методы и программирование с Maxima
4.1.5 Транслятор и компилятор в Maxima
Определив ту или иную функцию, можно заметно ускорить ее выполнение, если ее оттранслировать или откомпилировать. Это происходит потому, что если Вы не оттранслировали и не откомпилировали определенную Вами функцию, то при каждом очередном ее вызове Maxima каждый раз заново выполняет те действия, которые входят в определение функции, т.е. фактически разбирает соответствующее выражение на уровне синтаксиса Maxima.
4.1.5.1 Функция translate
Функция транслирует функцию Maxima на язык Lisp. Например, выражение: транслируется командой: translate(f);. После этого функция, как правило, начинает вычисляться быстрее.
Пример, иллюстрирующий выигрыш по времени после трансляции функции:
(%i1) f(n):=block([sum,k],sum:0, for k:1 thru n do (sum:sum+k^2),sum)$
Функция , организованная в виде блока, позволяет вычислить сумму .
Для выполнения тестов использовался один и тот же ноутбук (ОС Linux, Maxima 5.24). При непосредственном обращении к функции время вычисления (1000000) составило 7,86 с, после трансляции — 3,19 с. Для оценки времени вычисления использована функция .
(%i2) f(1000000); (%o2) 333333833333500000 (%i3) time(%o2); (%o3) [7.86] (%i4) translate(f); (%o4) [f] (%i5) f(1000000); (%o5) 333333833333500000 (%i6) time(%o5); (%o6) [3.19]
Функция возвращает список периодов времени в секундах, израсходованных для вычисления результатов Аргументом функции могут быть только номера строк вывода, для любых других переменных функция возвращает значение .
4.1.5.2 Функция compile
Функция сначала транслирует функцию Maxima на язык Lisp, а затем компилирует эту функцию до двоичных кодов и загружает их в память.
(%i9) compile(f); Compiling /tmp/gazonk_1636_0.lsp. End of Pass 1. End of Pass 2. OPTIMIZE levels: Safety=2, Space=3, Speed=3 Finished compiling /tmp/gazonk_1636_0.lsp. (%o92) [f]
После этого функция (как правило) начинает считаться еще быстрее, чем после трансляции. Например, после компиляции функции из последнего примера время вычисления (1000000) составило 2.17 с.
Следует иметь в виду, что как при трансляции, так и при компиляции Maxima старается оптимизировать функцию по скорости. Однако Maxima работает преимущественно с целыми числами произвольной длины либо текстовыми выражениями. Поэтому при работе с большими по объёму функциями могут возникнуть проблемы, связанные с преобразованием типов данных. В этом случае следует отказаться от трансляции или компиляции, либо переписать функцию, упорядочив использование типов.
Пример: Рассмотрим две функции, вычисляющие одно и то же выражение. В функции явно указано, что функция возвращает действительные значения (в формате с плавающей точкой)
f1(x,n):=block([sum,k], sum:1, for k:1 thru n do (sum:sum+1/x^k),sum)$ f2(x,n):=block([sum,k], mode_declare ([function (f2),x], float), sum:1, for k:1 thru n do (sum:sum+1/x^k),sum)$
Время выполнения функции при запуске (5, 10000) составило 1,8 с. После компиляции время выполнения составило 1,49 с, после трансляции — 1,39 с. Попытка обратиться к откомпилированной функции командой (5.0, 10000.0) завершилась неудачей вследствие возникающей ошибки (плавающее переполнение).
При использовании функции с декларированным типом результата (f2) время выполнения f2(5, 10000) оказалось меньше, чем (1,65 с вместо 1,8 с). Однако время выполнения той же функции после трансляции или компиляции превышает 10 с. Следует учесть, что в данном случае результат расчёта — рациональное число. Преобразование его к форме с плавающей точкой при вычислении очередного значения суммы требует дополнительных вычислительных затрат. При обращении к с действительными аргументами (5.0, 10000.0) время счёта составило всего 0,16 с.
Для функции, возвращающей результат, который представляется в виде числа с плавающей точкой, компиляция или трансляция может дать уменьшение времени счёта в несколько раз.
Пример: Рассмотрим функции, вычисляющую действительное выражение (в данном случае суммируются иррациональные числа)
f3(x,n):=block([sum,k], mode_declare ([function (f3),x], float), sum:1, for k:1 thru n do (sum:sum+sqrt(x^k)),sum)$
Время вычисления выражения (5, 2000) для неоткомпилированной и не оттранслированной функции составило 7,47 с., после трансляции время вычисления (5, 2000) составило 0,03 с, после компиляции — 0,02 с.
Рассмотрим ещё один пример:
f4(x,n):=block([sum,k], sum:1, for k:1 thru n do (sum:sum+k/x),sum)$
Время вычисления выражения (5, 1000000) составило 10,89 с, время вычисления выражения (5.0, 1000000) составило 6,71 с. После трансляции время вычисления выражения (5, 1000000) составило 9,1 с (выигрыш по времени практически отсутствует), а для f4(5.0, 1000000) — 2,49 с (выигрыш по времени за счёт выполнения вычислений с плавающей точкой примерно в 2,5 раза).