The Maple ?Compiler can compile a limited subset of Maple commands to native code. The result is substantially faster than running interpreted Maple code. This article shows how you can save a compiled procedure for reuse.
When a Maple procedure is compiled, a shared object library (dynamic-linked library for Windows) is created on the file system, as a temporary file. When the Maple session is ended, or restarted, this temporary file is deleted. To reuse it, this shared library must be copied to a permanent position. The Maple object returned by the compiler embeds the location of the shared library in the option field; that location must be updated.
Here is a compiled Maple procedure,
(**) sqr := proc(x) x^2 end proc):
(**) sqr := Compiler:-Compile(sqr);
sqr := proc()
option call_external, define_external(_m4fa7f349fa8b242dec87f5b731cbf23e,
MAPLE, LIB = "/tmp/joe-16236/_m4fa7f349fa8b242dec87f5b731cbf23e.so");
call_external(0, 140469208753856, true, false, args)
end proc
The location of the temporary file is the string "/tmp/joe-16236/_m4fa7f349fa8b242dec87f5b731cbf23e.so". One way to extract this is with an ?op command. For example,
(**) op([3,2,3,2], eval(sqr));
"/tmp/joe-16236/_m4fa7f349fa8b242dec87f5b731cbf23e.so"
A somewhat safer method is to convert the option field to an inert structure and extract the string from that.
Here is a small package that provides an export, Copy, that copies the shared library to a designated directory and updates the compiled procedure.
CompileTools := module()
option package;
export Copy
, GetTemporaryFile
;
GetTemporaryFile := proc( f :: procedure )
local inert, tmpfile;
inert := ToInert([attributes(eval(f))]);
if not has(inert, "define_external") then
error "procedure is not compiled";
end if;
tmpfile := op([1,1], indets(inert, '_Inert_STRING(string)'));
if not FileTools:-Exists(tmpfile) then
error "file %1 does not exist", tmpfile;
end if;
return tmpfile;
end proc:
Copy := proc( f :: procedure
, { dir :: string := sprintf("%s/maple/lib", kernelopts('homedir')) }
, { lib :: string := NULL }
)
local file
, filename
, tmpfile
;
# Get the temporary filename from the compiled procedure
tmpfile := GetTemporaryFile(f);
# Assign the new filename.
if lib <> NULL then
filename := lib;
else
filename := FileTools:-Filename(tmpfile);
end if;
# Create the full pathname.
file := StringTools:-Join([dir, filename], kernelopts('dirsep'));
# Copy the temporary file, overwriting if necessary
FileTools:-Copy(tmpfile, file, 'force');
# Return the updated procedure
return subs(tmpfile = file, eval(f))
end proc;
end module:
# Save this package
LibraryTools:-Save(CompileTools, "/home/joe/maple/lib/CompileTools.mla"):
Here is a simple demonstration of using it on the sqr procedure previously assigned and compiled.
(**) sqr := CompileTools:-Copy(sqr); # use default directory
sqr := proc()
option call_external, define_external(_m4fa7f349fa8b242dec87f5b731cbf23e,
MAPLE, LIB = "/home/joe/maple/lib/_m4fa7f349fa8b242dec87f5b731cbf23e.so");
call_external(0, 140469212120768, true, false, args)
end proc
Note that LIB now points to the new location of the shared object library. The LibraryTools:-Save command can now be used to save this procedure to a custom Maple archive,
(**) LibraryTools:-Save(sqr, sprintf("%s/maple/lib/mysqr.mla", kernelopts('homedir'))):
Now we can restart Maple and use the compiled procedure automatically
(**) restart;
(**) sqr(23);
529.
A similar technique can be used to to create a Maple package whose exports and locals are compiled procedures. After assigning the procedure in the module body, compile it, then use CompileTools to copy the shared object file and update the procedure.
Sample := module()
option package;
export sqr;
sqr := proc(x) x^2 end proc;
sqr := Compiler:-Compile(sqr);
sqr := CompileTools:-Copy(sqr);
end module:
# Save this package
LibraryTools:-Save(Sample, "/home/joe/maple/lib/Sample.mla");
Now restart Maple and verify that it works
(**) restart;
(**) Sample:-sqr(12);
144.