20010623 This was added sometime prior to 20001025.
Try out MLDBM here and document it if it works.
timethis(main, { MEMOIZED => [ suba, subb ] })
What would this do? It would time main
three times, once with suba
and subb
unmemoized, twice with them memoized.
Why would you want to do this? By the third set of runs, the memo tables would be fully populated, so all calls by main
to suba
and subb
would return immediately. You would be able to see how much of main
's running time was due to time spent computing in suba
and subb
. If that was just a little time, you would know that optimizing or improving suba
and subb
would not have a large effect on the performance of main
. But if there was a big difference, you would know that suba
or subb
was a good candidate for optimization if you needed to make main
go faster.
Done.
memoize
should return a reference to the original function as well as one to the memoized version? But the programmer could always construct such a reference themselves, so perhaps it's not necessary. We save such a reference anyway, so a new package method could return it on demand even if it wasn't provided by memoize
. We could even bless the new function reference so that it could have accessor methods for getting to the original function, the options, the memo table, etc.
Naah.
Done!
You have a long note to Mike Cariaso that outlines a good approach that you sent on 9 April 1999.
What's the timeout stuff going to look like?
EXPIRE_TIME => time_in_sec EXPIRE_USES => num_uses MAXENTRIES => n
perhaps? Is EXPIRE_USES actually useful?
19990916: Memoize::Expire does EXPIRE_TIME and EXPIRE_USES. MAXENTRIES can come later as a separate module.
fibo
. Show an example of a nonrecursive function that simply takes a long time to run. getpwuid
for example? But this exposes the bug that you can't say memoize('getpwuid')
, so perhaps it's not a very good example.
Well, I did add the ColorToRGB example, but it's still not so good. These examples need a lot of work. factorial
might be a better example than fibo
.
Does it need more now?
Fixed, I think.
What for?
Maybe the tied hash interface taskes care of this anyway?
A prototype of Memoize::Storable is finished. Test it and add to the test suite.
Done.
FETCH { if (it's in the memory hash) { return it } elsif (it's in the readonly disk hash) { return it } else { not-there } } STORE { put it into the in-memory hash }
Maybe `save' and `restore' methods?
It isn't working right because the destructor doesn't get called at the right time.
This is fixed. `use strict vars' would have caught it immediately. Duh.
20010627 It would appear that you put this into 0.51.
SCALAR_CACHE => [TIE, Memoize::SDBM_File, $filename, O_RDWR|O_CREAT, 0666], LIST_CACHE => MERGE
my $wrapper = eval "sub { unshift \@_, qq{$cref}; goto &_memoizer; }";
I think becayse @_
is lexically scoped in threadperl, the effect of unshift
never makes it into _memoizer
. That's probably a bug in Perl, but maybe I should work around it. Can anyone provide more information here, or lend me a machine with threaded Perl where I can test this theory? Line 59, currently commented out, may fix the problem.
20010623 Working around this in 0.65, but it still blows.
($)
, there's no reason to use `join'. If it's (\@)
then it can use join $;,@$_[0];
instead of join $;,@_;
.$sig{INT} = sub { unmemoize ... };
$sig{INT} = sub { for $f (Memoize->all_memoized) { unmemoize $f; } }
Right, if that's what you want. If you have EXISTS return false, it'll throw away the old cached item and replace it in the cache with a new item. But if you want the cache to actually get smaller, you have to do that yourself. I was planning to build an Expire module that implemented an LRU queue and kept the cache at a constant fixed size, but I didn't get to it yet. It's not clear to me that the automatic exptynig-out behavior is very useful anyway. The whole point of a cache is to trade space for time, so why bother going through the cache to throw away old items before you need to?
Randal then pointed out that it could discard expired items at DESTRoY or TIEHASH time, which seemed like a good idea, because if the cache is on disk you might like to keep it as small as possible.
return join $;, (stat($_[0])[9]), $_[0];
Now when the modification date changes, the true key returned by the normalizer is different, so you get a cache miss and it loads the new contents. Disadvantage: The old contents are still in the cache. I think it makes more sense to have a special expiration manager for this. Make one up and bundle it.
19991220 I have one written: Memoize::ExpireFile. But how can you make this work when the function might have several arguments, of which some are filenames and some aren't?
19991220 Philip Gwyn contributed a patch for this.
20001231 You should really put this in. Jonathan Roy uncovered a problem that it will be needed to solve. Here's the problem: He has:
memoize "get_items", LIST_CACHE => ["TIE", "Memoize::Expire", LIFETIME => 86400, TIE => ["DB_File", "debug.db", O_CREAT|O_RDWR, 0666] ];
This won't work, because memoize is trying to store listrefs in a DB_File. He owuld have gotten a fatal error if he had done this:
memoize "get_items", LIST_CACHE => ["TIE", "DB_File", "debug.db", O_CREAT|O_RDWR, 0666]'
But in this case, he tied the cache to Memoize::Expire, which is *not* scalar-only, and the check for scalar-only ties is missing from Memoize::Expire. The inheritable method can take care of this.
20010623 I decided not to put it in. Instead, we avoid the problem by getting rid of TIE. The HASH option does the same thing, and HASH is so simple to support that a module is superfluous.