A generic way to define function that cache their values. spad )abbrev domain CACHEDF CachedFunction ++ Author: Ralf Hemmecke ++ Date Created: 2009 ++ Date Last Updated: Oct 27, spad Compiling FriCAS source code from file
/var/lib/zope2.10/instance/axiom-wiki/var/LatexWiki/8303643491408128431-25px001.spad
using old system compiler.
CACHEDF abbreviates domain CachedFunction
------------------------------------------------------------------------
initializing NRLIB CACHEDF for CachedFunction
compiling into NRLIB CACHEDF
compiling exported function : $ -> A -> B
CACHEDF;function;$M;1 is replaced by QCDR
Time: 0 SEC.fricas )set message time on
Type: Type
fricas Time: 0.02 (OT) = 0.02 sec f(n:I): I ==1 Type: Void
fricas Time: 0 sec fib: CachedFunction(I, fricas Compiling function f with type Integer -> Integer Type: CachedFunction?(Integer,
fricas Time: 0.01 (OT) = 0.01 sec recursiveDefine(fib, Type: CachedFunction?(Integer,
fricas Time: 0.01 (IN) = 0.01 sec fib 40
Type: PositiveInteger?
fricas Time: 0.01 (IN) = 0.01 sec g(n:I): I == if n<2 then 1 else g(n-1)+g(n-2) Type: Void
fricas Time: 0 sec g 40 fricas Compiling function g with type Integer -> Integer
Type: PositiveInteger?
fricas Time: 3.32 (EV) = 3.32 sec h: I -> I := function fib
Type: (Integer -> Integer)
fricas Time: 0 sec h 40
Type: PositiveInteger?
fricas Time: 0 sec fricas )set message time off Caching a function with more than one argument We need to combine more than one domain into a single domain
in a universal manner like a cross-product. There are seveal ways
to do this in Axiom including fricas -- combine two domain in one using Record Pair:=Record(x1:Integer,
Type: Type
fricas pair(x:Integer, Type: Void
fricas -- gcd2(arg:Pair):Integer == gcd(arg.x1, Type: Void
fricas gcd2cached:CachedFunction(Pair, fricas Compiling function gcd2 with type Record(x1: Integer, fricas gcdCached(x1:Integer, Type: Void
fricas )set message time on Type: Void
fricas Time: 0.33 (EV) = 0.33 sec for i in 1..100 repeat for j in 1..100 repeat gcdCached(i, fricas Compiling function pair with type (Integer, fricas Compiling function gcdCached with type (Integer, Type: Void
fricas Time: 0.01 (IN) + 7.09 (EV) = 7.10 sec fricas )set message time off fricas testhash() ==
for i in 1..100 repeat for j in 1..100 repeat
if gcdCached(i,
Type: Void
fricas testhash() fricas Compiling function testhash with type () -> String
Type: String
I was rather disappointed with the performance of the cache!
So I took a look at the source code for
Table
and it seems that fricas hashable(Integer)$Lisp
Type: SExpression?
fricas hashable(String)$Lisp
Type: SExpression?
fricas hashable(Record(x1:Integer,
Type: SExpression?
fricas hashable(List Integer)$Lisp
Type: SExpression?
But as far as I can see the implementation of
++ This creates a \spadtype{HashTable} if equal for the Key
++ domain is consistent with Lisp EQUAL otherwise an
++ \spadtype{AssociationList}
if their component domains do. So I think it would be a good
idea to make the
(defun |knownEqualPred| (dom)
(let ((fun (|compiledLookup| '= '((|Boolean|) $ $) dom)))
(if fun (get (bpiname (car fun)) '|SPADreplace|)
nil)))
(defun |hashable| (dom)
(memq (|knownEqualPred| dom)
#-Lucid '(EQ EQL EQUAL)
#+Lucid '(EQ EQL EQUAL EQUALP)
))
a little smarter. This test only seems to check if the compiler
was able to optimize equality to a lisp operator. I suppose we
could improve the compiler but even failing that, there should
be a way to recognize it at this level. I think it's something
like having the fricas )clear completely We can test this for now by using HashTable? explicitly and just
assuming that that domain spad )abbrev domain CACHEDF CachedFunction CachedFunction(A: SetCategory, spad Compiling FriCAS source code from file
/var/lib/zope2.10/instance/axiom-wiki/var/LatexWiki/6894297888104578709-25px008.spad
using old system compiler.
CACHEDF abbreviates domain CachedFunction
------------------------------------------------------------------------
initializing NRLIB CACHEDF for CachedFunction
compiling into NRLIB CACHEDF
Local variable Rep type redefined: (Join (SetCategory) (CATEGORY domain (SIGNATURE construct ((Record (: cache (HashTable A B UEQUAL)) (: fun (Mapping B A))) (HashTable A B UEQUAL) (Mapping B A))) (SIGNATURE ~= ((Boolean) (Record (: cache (HashTable A B UEQUAL)) (: fun (Mapping B A))) (Record (: cache (HashTable A B UEQUAL)) (: fun (Mapping B A))))) (SIGNATURE coerce ((OutputForm) (Record (: cache (HashTable A B UEQUAL)) (: fun (Mapping B A))))) (SIGNATURE elt ((HashTable A B UEQUAL) (Record (: cache (HashTable A B UEQUAL)) (: fun (Mapping B A))) cache)) (SIGNATURE elt ((Mapping B A) (Record (: cache (HashTable A B UEQUAL)) (: fun (Mapping B A))) fun)) (SIGNATURE setelt! ((HashTable A B UEQUAL) (Record (: cache (HashTable A B UEQUAL)) (: fun (Mapping B A))) cache (HashTable A B UEQUAL))) (SIGNATURE setelt! ((Mapping B A) (Record (: cache (HashTable A B UEQUAL)) (: fun (Mapping B A))) fun (Mapping B A))) (SIGNATURE copy ((Record (: cache (HashTable A B UEQUAL)) (: fun (Mapping B A))) (Record (: cache (HashTable A B UEQUAL)) (: fun (Mapping B A))))))) to (Join (SetCategory) (CATEGORY domain (SIGNATURE construct ((Record (: cache (Table A B)) (: fun (Mapping B A))) (Table A B) (Mapping B A))) (SIGNATURE ~= ((Boolean) (Record (: cache (Table A B)) (: fun (Mapping B A))) (Record (: cache (Table A B)) (: fun (Mapping B A))))) (SIGNATURE coerce ((OutputForm) (Record (: cache (Table A B)) (: fun (Mapping B A))))) (SIGNATURE elt ((Table A B) (Record (: cache (Table A B)) (: fun (Mapping B A))) cache)) (SIGNATURE elt ((Mapping B A) (Record (: cache (Table A B)) (: fun (Mapping B A))) fun)) (SIGNATURE setelt! ((Table A B) (Record (: cache (Table A B)) (: fun (Mapping B A))) cache (Table A B))) (SIGNATURE setelt! ((Mapping B A) (Record (: cache (Table A B)) (: fun (Mapping B A))) fun (Mapping B A))) (SIGNATURE copy ((Record (: cache (Table A B)) (: fun (Mapping B A))) (Record (: cache (Table A B)) (: fun (Mapping B A)))))))
compiling exported function : $ -> A -> B
CACHEDF;function;$M;1 is replaced by QCDR Now let's try that again. fricas -- combine two domain in one Pair:=Record(x1:Integer,
Type: Type
fricas pair(x:Integer, Type: Void
fricas -- gcd2(arg:Pair):Integer == gcd(arg.x1, Type: Void
fricas gcd2cached:CachedFunction(Pair, fricas Compiling function gcd2 with type Record(x1: Integer, fricas gcdCached(x1:Integer, Type: Void
fricas )set message time on Type: Void
fricas Time: 0.28 (EV) = 0.28 sec for i in 1..100 repeat for j in 1..100 repeat gcdCached(i, fricas Compiling function pair with type (Integer, fricas Compiling function gcdCached with type (Integer, Type: Void
fricas Time: 0.01 (IN) + 0.22 (EV) = 0.23 sec fricas )set message time off fricas testhash() ==
for i in 1..100 repeat for j in 1..100 repeat
if gcdCached(i,
Type: Void
fricas testhash() fricas Compiling function testhash with type () -> String
Type: String
perhaps recursiveDefine not necessary --Bill Page, Wed, 28 Oct 2009 22:06:18 -0700 reply fricas )set message time on fricas )lib CACHEDF Type: CachedFunction?(Integer,
fricas Time: 0.01 (OT) = 0.01 sec fib2 40
Type: PositiveInteger?
fricas Time: 0.01 (IN) = 0.01 sec Looks like the time difference (if any) is not significant. |