Mercurial > urweb
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? *) |