Компания ALT Linux
Опубликован: 24.03.2015 | Доступ: свободный | Студентов: 553 / 138 | Длительность: 19:00:00
Лекция 4:

Численные методы и программирование с Maxima

4.1.5 Транслятор и компилятор в Maxima

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

4.1.5.1 Функция translate

Функция translate транслирует функцию Maxima на язык Lisp. Например, выражение: f(x) := 1+x+x^2+x^3+x^4+x^5+x^6+x^7 транслируется командой: translate(f);. После этого функция, как правило, начинает вычисляться быстрее.

Пример, иллюстрирующий выигрыш по времени после трансляции функции:

(%i1)	f(n):=block([sum,k],sum:0,
	for k:1 thru n do (sum:sum+k^2),sum)$

Функция f(n), организованная в виде блока, позволяет вычислить сумму \displaystyle\sum\limits_{k=1}^{k=n}k^2.

Для выполнения тестов использовался один и тот же ноутбук (ОС Linux, Maxima 5.24). При непосредственном обращении к функции f время вычисления f(1000000) составило 7,86 с, после трансляции — 3,19 с. Для оценки времени вычисления использована функция time.

(%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]

Функция time(\%o1, \%o2,...) возвращает список периодов времени в секундах, израсходованных для вычисления результатов \%o1, \%o2,... Аргументом функции time могут быть только номера строк вывода, для любых других переменных функция возвращает значение unknown.

4.1.5.2 Функция compile

Функция compile сначала транслирует функцию Maxima на язык Lisp, а затем компилирует эту функцию 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]

После этого функция (как правило) начинает считаться еще быстрее, чем после трансляции. Например, после компиляции функции f из последнего примера время вычисления f(1000000) составило 2.17 с.

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

Пример: Рассмотрим две функции, вычисляющие одно и то же выражение. В функции f2 явно указано, что функция возвращает действительные значения (в формате с плавающей точкой)

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)$

Время выполнения функции f1 при запуске f1(5, 10000) составило 1,8 с. После компиляции время выполнения составило 1,49 с, после трансляции — 1,39 с. Попытка обратиться к откомпилированной функции f1 командой f1(5.0, 10000.0) завершилась неудачей вследствие возникающей ошибки (плавающее переполнение).

При использовании функции с декларированным типом результата (f2) время выполнения f2(5, 10000) оказалось меньше, чем f1 (1,65 с вместо 1,8 с). Однако время выполнения той же функции после трансляции или компиляции превышает 10 с. Следует учесть, что в данном случае результат расчёта — рациональное число. Преобразование его к форме с плавающей точкой при вычислении очередного значения суммы требует дополнительных вычислительных затрат. При обращении к f2 с действительными аргументами f2(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)$

Время вычисления выражения f3(5, 2000) для неоткомпилированной и не оттранслированной функции составило 7,47 с., после трансляции время вычисления f3(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)$

Время вычисления выражения f4(5, 1000000) составило 10,89 с, время вычисления выражения f4(5.0, 1000000) составило 6,71 с. После трансляции f4 время вычисления выражения f4(5, 1000000) составило 9,1 с (выигрыш по времени практически отсутствует), а для f4(5.0, 1000000) — 2,49 с (выигрыш по времени за счёт выполнения вычислений с плавающей точкой примерно в 2,5 раза).