routines which redefine themselves

acer's picture

There are some routines in Maple's library which, when called the first time, redefine themselves.

One plausible explanation for this is that the new versions are session dependent (external calls, say) while also more efficient to call (repeatedly).

For example, consider StringTools:-Join which seems typical of that package. First, consider it before it's been called at all.

> restart:
> showstat(StringTools:-Join);
 
StringTools:-Join := proc()
   1   passign(procname,defun(convert(join,'string')));
   2   procname(args)
end proc

Now call it a first time. And examine it again.

 

> StringTools:-Join(["hi","bye"]):
> showstat(StringTools:-Join);
 
StringTools:-Join := proc()
   1   call_external(0,182946067904,true,args)
end proc

So the magic was in those calls to `passign` and `defun`, which can also be examined.

> kernelopts(opaquemodules=false):

StringTools:-defun := proc(nom)
local extsym, solib;
   1   solib := ExternalCalling:-ExternalLibraryName("mstring",'HWFloat');
   2   extsym := nprintf("mstring_%s",nom);
   3   userinfo(5,StringTools,nprintf("Defining %a",extsym));
   4   define_external(extsym,'MAPLE',('LIB') = solib)
end proc
 
> showstat(StringTools:-passign);
 
StringTools:-passign := proc(n::name, v)
   1   unprotect(n);
   2   assign(n,v);
   3   protect(n);
   4   NULL
end proc

A neat little technique.

Comments

JacquesC's picture

A better method

This 'trick' is very old, and used to be pervasive back in the days where readlib was necessary.  Luckily, readlib was made obsolete, and yet now this trick pops up again!

I thought that that was exactly why ModuleLoad was created for. This kind of initialization really ought to be done at load-time of the StringTools module, rather than through evil side-effects. There is a downside to ModuleLoad: all routines, whether they are used or not, would get loaded if any are. But that should be fixeable too. The 'persistent store' mechanism should be able to deal with that too! Creating a thunk that is only resolved upon first read (for the 2nd parameter to the call_external) should be relatively simple.

acer's picture

small modules

The one-time cost of defining all the StringTools exports as their session-dependent call_externals may be negligible compared to the cost of the initial dlopen of the mstring dynamic library.

Here is a crude illustration, done in order in a fresh TTY session.

> st:=time():
> try StringTools:-Join(): catch: end try:
> time()-st;
                                     0.002
 
> st:=time():
> try
> for j in exports(StringTools) do
>   StringTools[j]():
> end do:
> catch: end try:
> time()-st;
                                     0.001

> st:=time():
> try
> for j in exports(StringTools) do
>   StringTools[j]():
> end do:
> catch: end try:
> time()-st;
                                      0.

Ignoring effects due to try..catch overhead and the cost of raising errors, and assuming that the timer is accurate at such small granularity, it looks like the initial dlopen costs 2/3's as much as initializing/redefining all of the (approximately 200) StringTools exports.

It may only be for packages with many exports (which each need redefining) that having ModuleLoad deal with them all at once on the first access might be undesirable. So it may be reasonable to have StringTools:-ModuleLoad initialize them all, without any change to the persistent store mechanism.

acer

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
}