comparison src/sqlcache.sml @ 2250:c275bbc41194

Start work on pure expression caching.
author Ziv Scully <ziv@mit.edu>
date Sun, 13 Sep 2015 16:02:45 -0400
parents e09c3dc102ef
children 25874084bf1f
comparison
equal deleted inserted replaced
2249:c05851bf7861 2250:c275bbc41194
1 structure Sqlcache (* DEBUG: add back :> SQLCACHE. *) = struct 1 structure Sqlcache :> SQLCACHE = struct
2 2
3 open Mono 3 open Mono
4 4
5 structure IS = IntBinarySet 5 structure IS = IntBinarySet
6 structure IM = IntBinaryMap 6 structure IM = IntBinaryMap
7 structure SK = struct type ord_key = string val compare = String.compare end 7 structure SK = struct type ord_key = string val compare = String.compare end
8 structure SS = BinarySetFn(SK) 8 structure SS = BinarySetFn(SK)
9 structure SM = BinaryMapFn(SK) 9 structure SM = BinaryMapFn(SK)
10 structure SIMM = MultimapFn(structure KeyMap = SM structure ValSet = IS) 10 structure SIMM = MultimapFn(structure KeyMap = SM structure ValSet = IS)
11
12 fun iterate f n x = if n < 0
13 then raise Fail "Can't iterate function negative number of times."
14 else if n = 0
15 then x
16 else iterate f (n-1) (f x)
11 17
12 (* Filled in by [cacheWrap] during [Sqlcache]. *) 18 (* Filled in by [cacheWrap] during [Sqlcache]. *)
13 val ffiInfo : {index : int, params : int} list ref = ref [] 19 val ffiInfo : {index : int, params : int} list ref = ref []
14 20
15 fun resetFfiInfo () = ffiInfo := [] 21 fun resetFfiInfo () = ffiInfo := []
34 "urlifyString_w", 40 "urlifyString_w",
35 "urlifyBool_w", 41 "urlifyBool_w",
36 "urlifyChannel_w"] 42 "urlifyChannel_w"]
37 in 43 in
38 fn (m, f) => Settings.isEffectful (m, f) 44 fn (m, f) => Settings.isEffectful (m, f)
39 andalso not (m = "Basis" andalso SS.member (fs, f)) 45 orelse not (m = "Basis" andalso SS.member (fs, f))
40 end 46 end
41 47
42 val cache = ref LruCache.cache 48 val cache = ref LruCache.cache
43 fun setCache c = cache := c 49 fun setCache c = cache := c
44 fun getCache () = !cache 50 fun getCache () = !cache
45 51
46 (* Used to have type context for local variables in MonoUtil functions. *) 52 (* Used to have type context for local variables in MonoUtil functions. *)
47 val doBind = 53 val doBind =
48 fn (ctx, MonoUtil.Exp.RelE (_, t)) => t :: ctx 54 fn (env, MonoUtil.Exp.RelE (s, t)) => MonoEnv.pushERel env s t NONE
49 | (ctx, _) => ctx 55 | (env, _) => env
50 56
51 57
52 (*******************) 58 (*******************)
53 (* Effect Analysis *) 59 (* Effect Analysis *)
54 (*******************) 60 (*******************)
57 fun effectful (effs : IS.set) = 63 fun effectful (effs : IS.set) =
58 let 64 let
59 val isFunction = 65 val isFunction =
60 fn (TFun _, _) => true 66 fn (TFun _, _) => true
61 | _ => false 67 | _ => false
62 fun doExp (ctx, e) = 68 fun doExp (env, e) =
63 case e of 69 case e of
64 EPrim _ => false 70 EPrim _ => false
65 (* For now: variables of function type might be effectful, but 71 (* For now: variables of function type might be effectful, but
66 others are fully evaluated and are therefore not effectful. *) 72 others are fully evaluated and are therefore not effectful. *)
67 | ERel n => isFunction (List.nth (ctx, n)) 73 | ERel n => isFunction (#2 (MonoEnv.lookupERel env n))
68 | ENamed n => IS.member (effs, n) 74 | ENamed n => IS.member (effs, n)
69 | EFfi (m, f) => ffiEffectful (m, f) 75 | EFfi (m, f) => ffiEffectful (m, f)
70 | EFfiApp (m, f, _) => ffiEffectful (m, f) 76 | EFfiApp (m, f, _) => ffiEffectful (m, f)
71 (* These aren't effectful unless a subexpression is. *) 77 (* These aren't effectful unless a subexpression is. *)
72 | ECon _ => false 78 | ECon _ => false
82 | EStrcat _ => false 88 | EStrcat _ => false
83 (* EWrite is a special exception because we record writes when caching. *) 89 (* EWrite is a special exception because we record writes when caching. *)
84 | EWrite _ => false 90 | EWrite _ => false
85 | ESeq _ => false 91 | ESeq _ => false
86 | ELet _ => false 92 | ELet _ => false
93 | EUnurlify _ => false
87 (* ASK: what should we do about closures? *) 94 (* ASK: what should we do about closures? *)
88 | EClosure _ => false
89 | EUnurlify _ => false
90 (* Everything else is some sort of effect. We could flip this and 95 (* Everything else is some sort of effect. We could flip this and
91 explicitly list bits of Mono that are effectful, but this is 96 explicitly list bits of Mono that are effectful, but this is
92 conservatively robust to future changes (however unlikely). *) 97 conservatively robust to future changes (however unlikely). *)
93 | _ => true 98 | _ => true
94 in 99 in
97 102
98 (* TODO: test this. *) 103 (* TODO: test this. *)
99 fun effectfulDecls (decls, _) = 104 fun effectfulDecls (decls, _) =
100 let 105 let
101 fun doVal ((_, name, _, e, _), effs) = 106 fun doVal ((_, name, _, e, _), effs) =
102 if effectful effs [] e 107 if effectful effs MonoEnv.empty e
103 then IS.add (effs, name) 108 then IS.add (effs, name)
104 else effs 109 else effs
105 val doDecl = 110 val doDecl =
106 fn ((DVal v, _), effs) => doVal (v, effs) 111 fn ((DVal v, _), effs) => doVal (v, effs)
107 (* Repeat the list of declarations a number of times equal to its size, 112 (* Repeat the list of declarations a number of times equal to its size,
360 mapFormula (toAtomExps QueryArg) 365 mapFormula (toAtomExps QueryArg)
361 366
362 val markDml : (Sql.cmp * Sql.sqexp * Sql.sqexp) formula -> 367 val markDml : (Sql.cmp * Sql.sqexp * Sql.sqexp) formula ->
363 (Sql.cmp * atomExp option * atomExp option) formula = 368 (Sql.cmp * atomExp option * atomExp option) formula =
364 mapFormula (toAtomExps DmlRel) 369 mapFormula (toAtomExps DmlRel)
370
365 (* No eqs should have key conflicts because no variable is in two 371 (* No eqs should have key conflicts because no variable is in two
366 equivalence classes, so the [#1] could be [#2]. *) 372 equivalence classes, so the [#1] could be [#2]. *)
367
368 val mergeEqs : (atomExp IntBinaryMap.map option list 373 val mergeEqs : (atomExp IntBinaryMap.map option list
369 -> atomExp IntBinaryMap.map option) = 374 -> atomExp IntBinaryMap.map option) =
370 List.foldr (fn (SOME eqs, SOME acc) => SOME (IM.unionWith #1 (eqs, acc)) | _ => NONE) 375 List.foldr (fn (SOME eqs, SOME acc) => SOME (IM.unionWith #1 (eqs, acc)) | _ => NONE)
371 (SOME IM.empty) 376 (SOME IM.empty)
372 377
509 end 514 end
510 515
511 fun fileMapfold doExp file start = 516 fun fileMapfold doExp file start =
512 case MonoUtil.File.mapfoldB 517 case MonoUtil.File.mapfoldB
513 {typ = Search.return2, 518 {typ = Search.return2,
514 exp = fn ctx => fn e' => fn s => Search.Continue (doExp ctx e' s), 519 exp = fn env => fn e' => fn s => Search.Continue (doExp env e' s),
515 decl = fn _ => Search.return2, 520 decl = fn _ => Search.return2,
516 bind = doBind} 521 bind = doBind}
517 [] file start of 522 MonoEnv.empty file start of
518 Search.Continue x => x 523 Search.Continue x => x
519 | Search.Return _ => raise Match 524 | Search.Return _ => raise Match
520 525
521 fun fileMap doExp file = #1 (fileMapfold (fn _ => fn e => fn _ => (doExp e, ())) file ()) 526 fun fileMap doExp file = #1 (fileMapfold (fn _ => fn e => fn _ => (doExp e, ())) file ())
522 527
554 (newText, wrapLets, numArgs) 559 (newText, wrapLets, numArgs)
555 end 560 end
556 561
557 fun addChecking file = 562 fun addChecking file =
558 let 563 let
559 fun doExp ctx (queryInfo as (tableToIndices, indexToQueryNumArgs, index)) = 564 fun doExp env (queryInfo as (tableToIndices, indexToQueryNumArgs, index)) =
560 fn e' as EQuery {query = origQueryText, 565 fn e' as EQuery {query = origQueryText,
566 (* ASK: could this get messed up by inlining? *)
561 sqlcacheInfo = urlifiedRel0, 567 sqlcacheInfo = urlifiedRel0,
562 state = resultTyp, 568 state = resultTyp,
563 initial, body, tables, exps} => 569 initial, body, tables, exps} =>
564 let 570 let
565 val (newQueryText, wrapLets, numArgs) = factorOutNontrivial origQueryText 571 val (newQueryText, wrapLets, numArgs) = factorOutNontrivial origQueryText
579 val args = List.tabulate (numArgs, fn n => (ERel n, dummyLoc)) 585 val args = List.tabulate (numArgs, fn n => (ERel n, dummyLoc))
580 fun bind x f = Option.mapPartial f x 586 fun bind x f = Option.mapPartial f x
581 fun guard b x = if b then x else NONE 587 fun guard b x = if b then x else NONE
582 val effs = effectfulDecls file 588 val effs = effectfulDecls file
583 (* We use dummyTyp here. I think this is okay because databases 589 (* We use dummyTyp here. I think this is okay because databases
584 don't store (effectful) functions, but there could be some 590 don't store (effectful) functions, but perhaps there's some
585 corner case I missed. *) 591 pathalogical corner case missing.... *)
586 fun safe bound = 592 fun safe bound =
587 not o effectful effs (List.tabulate (bound, fn _ => dummyTyp) @ ctx) 593 not
594 o effectful effs
595 (iterate (fn env => MonoEnv.pushERel env "_" dummyTyp NONE)
596 bound
597 env)
588 val attempt = 598 val attempt =
589 (* Ziv misses Haskell's do notation.... *) 599 (* Ziv misses Haskell's do notation.... *)
590 guard (safe 0 queryText andalso safe 0 initial andalso safe 2 body) ( 600 guard (safe 0 queryText andalso safe 0 initial andalso safe 2 body) (
591 bind (Sql.parse Sql.query queryText) (fn queryParsed => 601 bind (Sql.parse Sql.query queryText) (fn queryParsed =>
592 SOME (wrapLets (cacheWrap (queryExp, index, urlifiedRel0, resultTyp, args)), 602 SOME (wrapLets (cacheWrap (queryExp, index, urlifiedRel0, resultTyp, args)),
600 SOME pair => pair 610 SOME pair => pair
601 | NONE => (e', queryInfo) 611 | NONE => (e', queryInfo)
602 end 612 end
603 | e' => (e', queryInfo) 613 | e' => (e', queryInfo)
604 in 614 in
605 fileMapfold (fn ctx => fn exp => fn state => doExp ctx state exp) 615 fileMapfold (fn env => fn exp => fn state => doExp env state exp)
606 file 616 file
607 (SIMM.empty, IM.empty, 0) 617 (SIMM.empty, IM.empty, 0)
608 end 618 end
609 619
610 structure Invalidations = struct 620 structure Invalidations = struct
714 val () = Sql.sqlcacheMode := false 724 val () = Sql.sqlcacheMode := false
715 in 725 in
716 file' 726 file'
717 end 727 end
718 728
729
730 (**********************)
731 (* Mono Type Checking *)
732 (**********************)
733
734 val typOfPrim =
735 fn Prim.Int _ => TFfi ("Basis", "int")
736 | Prim.Float _ => TFfi ("Basis", "int")
737
738 fun typOfExp' (env : MonoEnv.env) : exp' -> typ option =
739 fn EPrim p => SOME (TFfi ("Basis", case p of
740 Prim.Int _ => "int"
741 | Prim.Float _ => "double"
742 | Prim.String _ => "string"
743 | Prim.Char _ => "char"),
744 dummyLoc)
745 | ERel n => SOME (#2 (MonoEnv.lookupERel env n))
746 | ENamed n => SOME (#2 (MonoEnv.lookupENamed env n))
747 (* ASK: okay to make a new [ref] each time? *)
748 | ECon (dk, PConVar nCon, _) =>
749 let
750 val (_, _, nData) = MonoEnv.lookupConstructor env nCon
751 val (_, cs) = MonoEnv.lookupDatatype env nData
752 in
753 SOME (TDatatype (nData, ref (dk, cs)), dummyLoc)
754 end
755 | ECon (_, PConFfi {mod = s, datatyp, ...}, _) => SOME (TFfi (s, datatyp), dummyLoc)
756 | ENone t => SOME (TOption t, dummyLoc)
757 | ESome (t, _) => SOME (TOption t, dummyLoc)
758 | EFfi _ => NONE
759 | EFfiApp _ => NONE
760 | EApp (e1, e2) => (case typOfExp env e1 of
761 SOME (TFun (_, t), _) => SOME t
762 | _ => NONE)
763 | EAbs (_, t1, t2, _) => SOME (TFun (t1, t2), dummyLoc)
764 (* ASK: is this right? *)
765 | EUnop (unop, e) => (case unop of
766 "!" => SOME (TFfi ("Basis", "bool"), dummyLoc)
767 | "-" => typOfExp env e
768 | _ => NONE)
769 (* ASK: how should this (and other "=> NONE" cases) work? *)
770 | EBinop _ => NONE
771 | ERecord fields => SOME (TRecord (map (fn (s, _, t) => (s, t)) fields), dummyLoc)
772 | EField (e, s) => (case typOfExp env e of
773 SOME (TRecord fields, _) =>
774 (case List.find (fn (s', _) => s = s') fields of
775 SOME (_, t) => SOME t
776 | _ => NONE)
777 | _ => NONE)
778 | ECase (_, _, {result, ...}) => SOME result
779 | EStrcat _ => SOME (TFfi ("Basis", "string"), dummyLoc)
780 | EWrite _ => SOME (TRecord [], dummyLoc)
781 | ESeq (_, e) => typOfExp env e
782 | ELet (s, t, e1, e2) => typOfExp (MonoEnv.pushERel env s t (SOME e1)) e2
783 | EClosure _ => NONE
784 | EUnurlify (_, t, _) => SOME t
785
786 and typOfExp env (e', loc) = typOfExp' env e'
787
788
789 (*******************************)
790 (* Caching Pure Subexpressions *)
791 (*******************************)
792
793 datatype subexp = Pure of unit -> exp | Impure of exp
794
795 val isImpure =
796 fn Pure _ => false
797 | Impure _ => true
798
799 val expOfSubexp =
800 fn Pure f => f ()
801 | Impure e => e
802
803 val makeCache : MonoEnv.env -> exp' -> exp' = fn _ => fn _ => raise Fail "TODO"
804
805 fun pureCache (effs : IS.set) (env : MonoEnv.env) (exp as (exp', loc)) : subexp =
806 let
807 fun wrapBindN f (args : (MonoEnv.env * exp) list) =
808 let
809 val subexps = map (fn (env, exp) => pureCache effs env exp) args
810 in
811 if List.exists isImpure subexps
812 then Impure (f (map expOfSubexp subexps), loc)
813 else Pure (fn () => (makeCache env (f (map #2 args)), loc))
814 end
815 fun wrapBind1 f arg =
816 wrapBindN (fn [arg] => f arg | _ => raise Match) [arg]
817 fun wrapBind2 f (arg1, arg2) =
818 wrapBindN (fn [arg1, arg2] => f (arg1, arg2) | _ => raise Match) [arg1, arg2]
819 fun wrapN f es = wrapBindN f (map (fn e => (env, e)) es)
820 fun wrap1 f e = wrapBind1 f (env, e)
821 fun wrap2 f (e1, e2) = wrapBind2 f ((env, e1), (env, e2))
822 in
823 case exp' of
824 ECon (dk, pc, SOME e) => wrap1 (fn e => ECon (dk, pc, SOME e)) e
825 | ESome (t, e) => wrap1 (fn e => ESome (t, e)) e
826 | EFfiApp (s1, s2, args) =>
827 wrapN (fn es => EFfiApp (s1, s2, ListPair.map (fn (e, (_, t)) => (e, t)) (es, args)))
828 (map #1 args)
829 | EApp (e1, e2) => wrap2 EApp (e1, e2)
830 | EAbs (s, t1, t2, e) =>
831 wrapBind1 (fn e => EAbs (s, t1, t2, e))
832 (MonoEnv.pushERel env s t1 NONE, e)
833 | EUnop (s, e) => wrap1 (fn e => EUnop (s, e)) e
834 | EBinop (bi, s, e1, e2) => wrap2 (fn (e1, e2) => EBinop (bi, s, e1, e2)) (e1, e2)
835 | ERecord fields =>
836 wrapN (fn es => ERecord (ListPair.map (fn (e, (s, _, t)) => (s, e, t)) (es, fields)))
837 (map #2 fields)
838 | EField (e, s) => wrap1 (fn e => EField (e, s)) e
839 | ECase (e, cases, {disc, result}) =>
840 wrapBindN (fn (e::es) =>
841 ECase (e,
842 (ListPair.map (fn (e, (p, _)) => (p, e)) (es, cases)),
843 {disc = disc, result = result}))
844 ((env, e) :: map (fn (p, e) => (MonoEnv.patBinds env p, e)) cases)
845 | EStrcat (e1, e2) => wrap2 EStrcat (e1, e2)
846 (* We record page writes, so they're cachable. *)
847 | EWrite e => wrap1 EWrite e
848 | ESeq (e1, e2) => wrap2 ESeq (e1, e2)
849 | ELet (s, t, e1, e2) =>
850 wrapBind2 (fn (e1, e2) => ELet (s, t, e1, e2))
851 ((env, e1), (MonoEnv.pushERel env s t (SOME e1), e2))
852 (* ASK: | EClosure (n, es) => ? *)
853 | EUnurlify (e, t, b) => wrap1 (fn e => EUnurlify (e, t, b)) e
854 | _ => if effectful effs env exp
855 then Impure exp
856 else Pure (fn () => (makeCache env exp', loc))
857 end
858
719 end 859 end