comparison src/sqlcache.sml @ 2268:bc1ef958d801

Thread state through addCaching more carefully.
author Ziv Scully <ziv@mit.edu>
date Wed, 14 Oct 2015 23:10:10 -0400
parents e5b7b066bf1b
children f7bc7c11a656
comparison
equal deleted inserted replaced
2267:e5b7b066bf1b 2268:bc1ef958d801
13 then raise Fail "Can't iterate function negative number of times." 13 then raise Fail "Can't iterate function negative number of times."
14 else if n = 0 14 else if n = 0
15 then x 15 then x
16 else iterate f (n-1) (f x) 16 else iterate f (n-1) (f x)
17 17
18 (* Filled in by [cacheWrap]. *) 18 (* Filled in by [addFlushing]. *)
19 val ffiInfo : {index : int, params : int} list ref = ref [] 19 val ffiInfoRef : {index : int, params : int} list ref = ref []
20 20
21 fun resetFfiInfo () = ffiInfo := [] 21 fun resetFfiInfo () = ffiInfoRef := []
22 22
23 fun getFfiInfo () = !ffiInfo 23 fun getFfiInfo () = !ffiInfoRef
24 24
25 (* Some FFIs have writing as their only effect, which the caching records. *) 25 (* Some FFIs have writing as their only effect, which the caching records. *)
26 val ffiEffectful = 26 val ffiEffectful =
27 (* ASK: how can this be less hard-coded? *) 27 (* ASK: how can this be less hard-coded? *)
28 let 28 let
59 (***********************) 59 (***********************)
60 (* General Combinators *) 60 (* General Combinators *)
61 (***********************) 61 (***********************)
62 62
63 (* From the MLton wiki. *) 63 (* From the MLton wiki. *)
64 infix 3 <\ fun x <\ f = fn y => f (x, y) (* Left section *)
65 infix 3 \> fun f \> y = f y (* Left application *)
66 infixr 3 /> fun f /> y = fn x => f (x, y) (* Right section *) 64 infixr 3 /> fun f /> y = fn x => f (x, y) (* Right section *)
67 infixr 3 </ fun x </ f = f x (* Right application *) 65 infixr 3 </ fun x </ f = f x (* Right application *)
68 66
69 (* Option monad. *) 67 (* Option monad. *)
70 fun obind (x, f) = Option.mapPartial f x 68 fun obind (x, f) = Option.mapPartial f x
71 fun oguard (b, x) = if b then x else NONE 69 fun oguard (b, x) = if b then x else NONE
70
71 fun mapFst f (x, y) = (f x, y)
72
72 73
73 (*******************) 74 (*******************)
74 (* Effect Analysis *) 75 (* Effect Analysis *)
75 (*******************) 76 (*******************)
76 77
697 * a map from free variable to cache arg number (per cache). 698 * a map from free variable to cache arg number (per cache).
698 Both queries and caches should have IDs. 699 Both queries and caches should have IDs.
699 700
700 *) 701 *)
701 702
702 fun cacheWrap (env, exp, resultTyp, args, i) = 703 fun cacheWrap (env, exp, resultTyp, args, state as (_, _, ffiInfo, index)) =
703 let 704 let
704 val loc = dummyLoc 705 val loc = dummyLoc
705 val rel0 = (ERel 0, loc) 706 val rel0 = (ERel 0, loc)
706 in 707 in
707 case MonoFooify.urlify env (rel0, resultTyp) of 708 case MonoFooify.urlify env (rel0, resultTyp) of
708 NONE => NONE 709 NONE => NONE
709 | SOME urlified => 710 | SOME urlified =>
710 let 711 let
711 val () = ffiInfo := {index = i, params = length args} :: !ffiInfo
712 (* We ensure before this step that all arguments aren't effectful. 712 (* We ensure before this step that all arguments aren't effectful.
713 by turning them into local variables as needed. *) 713 by turning them into local variables as needed. *)
714 val argsInc = map (incRels 1) args 714 val argsInc = map (incRels 1) args
715 val check = (check (i, args), loc) 715 val check = (check (index, args), loc)
716 val store = (store (i, argsInc, urlified), loc) 716 val store = (store (index, argsInc, urlified), loc)
717 in 717 in
718 SOME (ECase 718 SOME ((ECase
719 (check, 719 (check,
720 [((PNone stringTyp, loc), 720 [((PNone stringTyp, loc),
721 (ELet (varName "q", resultTyp, exp, (ESeq (store, rel0), loc)), loc)), 721 (ELet (varName "q", resultTyp, exp, (ESeq (store, rel0), loc)), loc)),
722 ((PSome (stringTyp, (PVar (varName "hit", stringTyp), loc)), loc), 722 ((PSome (stringTyp, (PVar (varName "hit", stringTyp), loc)), loc),
723 (* Boolean is false because we're not unurlifying from a cookie. *) 723 (* Boolean is false because we're not unurlifying from a cookie. *)
724 (EUnurlify (rel0, resultTyp, false), loc))], 724 (EUnurlify (rel0, resultTyp, false), loc))],
725 {disc = (TOption stringTyp, loc), result = resultTyp})) 725 {disc = (TOption stringTyp, loc), result = resultTyp})),
726 (#1 state,
727 #2 state,
728 {index = index, params = length args} :: ffiInfo,
729 index + 1))
726 end 730 end
727 end 731 end
728 732
729 val maxFreeVar = 733 val maxFreeVar =
730 MonoUtil.Exp.foldB 734 MonoUtil.Exp.foldB
746 0 750 0
747 IS.empty 751 IS.empty
748 752
749 val expSize = MonoUtil.Exp.fold {typ = #2, exp = fn (_, n) => n+1} 0 753 val expSize = MonoUtil.Exp.fold {typ = #2, exp = fn (_, n) => n+1} 0
750 754
751 datatype subexp = Cachable of unit -> exp | Impure of exp 755 type state = (SIMM.multimap
756 * (Sql.query * int) IntBinaryMap.map
757 * {index : int, params : int} list
758 * int)
759
760 datatype subexp = Cachable of state -> (exp * state) | Impure of exp
752 761
753 val isImpure = 762 val isImpure =
754 fn Cachable _ => false 763 fn Cachable _ => false
755 | Impure _ => true 764 | Impure _ => true
756 765
757 val expOfSubexp = 766 val runSubexp : subexp * state -> exp * state =
758 fn Cachable f => f () 767 fn (Cachable f, state) => f state
759 | Impure e => e 768 | (Impure e, state) => (e, state)
760 769
761 (* TODO: pick a number. *) 770 (* TODO: pick a number. *)
762 val sizeWorthCaching = 5 771 val sizeWorthCaching = 5
763 772
764 type state = (SIMM.multimap * (Sql.query * int) IntBinaryMap.map * int) 773 fun cacheQuery (effs, env, state, q) : (exp' * state) =
765 774 let
766 fun incIndex (x, y, index) = (x, y, index+1) 775 val (tableToIndices, indexToQueryNumArgs, ffiInfo, index) = state
767 776 val {query = queryText,
768 fun cacheQuery effs env (state as (tableToIndices, indexToQueryNumArgs, index)) = 777 state = resultTyp,
769 fn q as {query = queryText, 778 initial, body, tables, exps} = q
770 state = resultTyp,
771 initial, body, tables, exps} =>
772 let
773 val numArgs = maxFreeVar queryText + 1 779 val numArgs = maxFreeVar queryText + 1
774 val queryExp = (EQuery q, dummyLoc) 780 val queryExp = (EQuery q, dummyLoc)
775 (* DEBUG *) 781 (* DEBUG *)
776 (* val () = Print.preface ("sqlcache> ", MonoPrint.p_exp MonoEnv.empty queryText) *) 782 (* val () = Print.preface ("sqlcache> ", MonoPrint.p_exp MonoEnv.empty queryText) *)
777 val args = List.tabulate (numArgs, fn n => (ERel n, dummyLoc)) 783 val args = List.tabulate (numArgs, fn n => (ERel n, dummyLoc))
785 bound 791 bound
786 env) 792 env)
787 val attempt = 793 val attempt =
788 (* Ziv misses Haskell's do notation.... *) 794 (* Ziv misses Haskell's do notation.... *)
789 (safe 0 queryText andalso safe 0 initial andalso safe 2 body) 795 (safe 0 queryText andalso safe 0 initial andalso safe 2 body)
790 <\oguard\> 796 </oguard/>
791 Sql.parse Sql.query queryText 797 Sql.parse Sql.query queryText
792 <\obind\> 798 </obind/>
793 (fn queryParsed => 799 (fn queryParsed =>
794 (cacheWrap (env, queryExp, resultTyp, args, index)) 800 (cacheWrap (env, queryExp, resultTyp, args, state))
795 <\obind\> 801 </obind/>
796 (fn cachedExp => 802 (fn (cachedExp, state) =>
797 SOME (cachedExp, 803 SOME (cachedExp,
798 (SS.foldr (fn (tab, qi) => SIMM.insert (qi, tab, index)) 804 (SS.foldr (fn (tab, qi) => SIMM.insert (qi, tab, index))
799 tableToIndices 805 tableToIndices
800 (tablesQuery queryParsed), 806 (tablesQuery queryParsed),
801 IM.insert (indexToQueryNumArgs, index, (queryParsed, numArgs)), 807 IM.insert (indexToQueryNumArgs, index, (queryParsed, numArgs)),
802 index + 1)))) 808 #3 state,
809 #4 state))))
803 in 810 in
804 case attempt of 811 case attempt of
805 SOME pair => pair 812 SOME pair => pair
806 (* Even in this case, we have to increment index to avoid some bug, 813 | NONE => (EQuery q, state)
807 but I forget exactly what it is or why this helps. *) 814 end
808 (* TODO: just use a reference for current index.... *) 815
809 | NONE => (EQuery q, incIndex state) 816 fun cachePure (env, exp', state as (_, _, _, index)) =
810 end
811
812 fun cachePure (env, exp', (_, _, index)) =
813 case (expSize (exp', dummyLoc) > sizeWorthCaching) 817 case (expSize (exp', dummyLoc) > sizeWorthCaching)
814 </oguard/> 818 </oguard/>
815 typOfExp' env exp' of 819 typOfExp' env exp' of
816 NONE => NONE 820 NONE => NONE
817 | SOME (TFun _, _) => NONE 821 | SOME (TFun _, _) => NONE
823 (fn arg => SOME (arg :: args))) 827 (fn arg => SOME (arg :: args)))
824 (SOME []) 828 (SOME [])
825 (map (fn n => (n, #2 (MonoEnv.lookupERel env n))) 829 (map (fn n => (n, #2 (MonoEnv.lookupERel env n)))
826 (freeVars (exp', dummyLoc)))) 830 (freeVars (exp', dummyLoc))))
827 </obind/> 831 </obind/>
828 (fn args => cacheWrap (env, (exp', dummyLoc), typ, args, index)) 832 (fn args => cacheWrap (env, (exp', dummyLoc), typ, args, state))
829 833
830 fun cache (effs : IS.set) ((env, exp as (exp', loc)), state) : subexp * state = 834 fun cache (effs : IS.set) ((env, exp as (exp', loc)), state) =
831 let 835 let
832 fun wrapBindN f (args : (MonoEnv.env * exp) list) = 836 fun wrapBindN (f : exp list -> exp') (args : (MonoEnv.env * exp) list) =
833 let 837 let
834 val (subexps, state) = ListUtil.foldlMap (cache effs) state args 838 val (subexps, state) = ListUtil.foldlMap (cache effs) state args
835 fun mkExp () = (f (map expOfSubexp subexps), loc) 839 fun mkExp state = mapFst (fn exps => (f exps, loc))
840 (ListUtil.foldlMap runSubexp state subexps)
836 in 841 in
837 if List.exists isImpure subexps 842 if List.exists isImpure subexps
838 then (Impure (mkExp ()), state) 843 then mapFst Impure (mkExp state)
839 else (Cachable (fn () => case cachePure (env, f (map #2 args), state) of 844 else (Cachable (fn state =>
840 NONE => mkExp () 845 case cachePure (env, f (map #2 args), state) of
841 | SOME e' => (e', loc)), 846 NONE => mkExp state
842 (* Conservatively increment index. *) 847 | SOME (e', state) => ((e', loc), state)),
843 incIndex state) 848 state)
844 end 849 end
845 fun wrapBind1 f arg = 850 fun wrapBind1 f arg =
846 wrapBindN (fn [arg] => f arg | _ => raise Match) [arg] 851 wrapBindN (fn [arg] => f arg | _ => raise Match) [arg]
847 fun wrapBind2 f (arg1, arg2) = 852 fun wrapBind2 f (arg1, arg2) =
848 wrapBindN (fn [arg1, arg2] => f (arg1, arg2) | _ => raise Match) [arg1, arg2] 853 wrapBindN (fn [arg1, arg2] => f (arg1, arg2) | _ => raise Match) [arg1, arg2]
885 ((env, e1), (MonoEnv.pushERel env s t (SOME e1), e2)) 890 ((env, e1), (MonoEnv.pushERel env s t (SOME e1), e2))
886 (* ASK: | EClosure (n, es) => ? *) 891 (* ASK: | EClosure (n, es) => ? *)
887 | EUnurlify (e, t, b) => wrap1 (fn e => EUnurlify (e, t, b)) e 892 | EUnurlify (e, t, b) => wrap1 (fn e => EUnurlify (e, t, b)) e
888 | EQuery q => 893 | EQuery q =>
889 let 894 let
890 val (exp', state) = cacheQuery effs env state q 895 val (exp', state) = cacheQuery (effs, env, state, q)
891 in 896 in
892 (Impure (exp', loc), state) 897 (Impure (exp', loc), state)
893 end 898 end
894 | _ => if effectful effs env exp 899 | _ => if effectful effs env exp
895 then (Impure exp, state) 900 then (Impure exp, state)
896 else (Cachable (fn () => (case cachePure (env, exp', state) of 901 else (Cachable (fn state =>
897 NONE => exp' 902 case cachePure (env, exp', state) of
898 | SOME e' => e', 903 NONE => ((exp', loc), state)
899 loc)), 904 | SOME (exp', state) => ((exp', loc), state)),
900 incIndex state) 905 state)
901 end 906 end
902 907
903 fun addCaching file = 908 fun addCaching file =
904 let 909 let
905 val effs = effectfulDecls file 910 val effs = effectfulDecls file
906 fun doTopLevelExp env exp state = 911 fun doTopLevelExp env exp state = runSubexp (cache effs ((env, exp), state))
907 let 912 in
908 val (subexp, state) = cache effs ((env, exp), state) 913 ((fileTopLevelMapfoldB doTopLevelExp file (SIMM.empty, IM.empty, [], 0)), effs)
909 in
910 (expOfSubexp subexp, state)
911 end
912 in
913 ((fileTopLevelMapfoldB doTopLevelExp file (SIMM.empty, IM.empty, 0)), effs)
914 end 914 end
915 915
916 916
917 (************) 917 (************)
918 (* Flushing *) 918 (* Flushing *)
965 965
966 (* DEBUG *) 966 (* DEBUG *)
967 (* val gunk : ((Sql.query * int) * Sql.dml) list ref = ref [] *) 967 (* val gunk : ((Sql.query * int) * Sql.dml) list ref = ref [] *)
968 (* val gunk' : exp list ref = ref [] *) 968 (* val gunk' : exp list ref = ref [] *)
969 969
970 fun addFlushing ((file, (tableToIndices, indexToQueryNumArgs, index)), effs) = 970 fun addFlushing ((file, (tableToIndices, indexToQueryNumArgs, ffiInfo, index)), effs) =
971 let 971 let
972 val flushes = List.concat 972 val flushes = List.concat
973 o map (fn (i, argss) => map (fn args => flush (i, args)) argss) 973 o map (fn (i, argss) => map (fn args => flush (i, args)) argss)
974 val doExp = 974 val doExp =
975 fn dmlExp as EDml (dmlText, failureMode) => 975 fn dmlExp as EDml (dmlText, failureMode) =>
997 end 997 end
998 | e' => e' 998 | e' => e'
999 in 999 in
1000 (* DEBUG *) 1000 (* DEBUG *)
1001 (* gunk := []; *) 1001 (* gunk := []; *)
1002 ffiInfoRef := ffiInfo;
1002 fileMap doExp file 1003 fileMap doExp file
1003 end 1004 end
1004 1005
1005 1006
1006 (***************) 1007 (************************)
1007 (* Entry point *) 1008 (* Compiler Entry Point *)
1008 (***************) 1009 (************************)
1009 1010
1010 val inlineSql = 1011 val inlineSql =
1011 let 1012 let
1012 val doExp = 1013 val doExp =
1013 (* TODO: EQuery, too? *) 1014 (* TODO: EQuery, too? *)