comparison src/sqlcache.sml @ 2256:6f2ea4ed573a

Pure caching sort of works.
author Ziv Scully <ziv@mit.edu>
date Sun, 27 Sep 2015 03:52:14 -0400
parents 8428c534913a
children 28a541bd2d23
comparison
equal deleted inserted replaced
2255:8428c534913a 2256:6f2ea4ed573a
491 (fn ERel n => ERel (if n >= bound orelse n < 0 then n + inc else n) 491 (fn ERel n => ERel (if n >= bound orelse n < 0 then n + inc else n)
492 | e' => e'), 492 | e' => e'),
493 bind = fn (bound, MonoUtil.Exp.RelE _) => bound + 1 | (bound, _) => bound} 493 bind = fn (bound, MonoUtil.Exp.RelE _) => bound + 1 | (bound, _) => bound}
494 0 494 0
495 495
496 fun cacheWrap (env, query, i, resultTyp, args) = 496 fun cacheWrap (env, exp, resultTyp, args, i) =
497 let 497 let
498 val () = ffiInfo := {index = i, params = length args} :: !ffiInfo
499 val loc = dummyLoc 498 val loc = dummyLoc
500 val rel0 = (ERel 0, loc) 499 val rel0 = (ERel 0, loc)
501 (* We ensure before this step that all arguments aren't effectful. 500 in
502 by turning them into local variables as needed. *) 501 case MonoFooify.urlify env (rel0, resultTyp) of
503 val argsInc = map (incRels 1) args 502 NONE => NONE
504 val check = (check (i, args), dummyLoc) 503 | SOME urlified =>
505 val store = (store (i, argsInc, MonoFooify.urlify env (rel0, resultTyp)), dummyLoc) 504 let
506 in 505 val () = ffiInfo := {index = i, params = length args} :: !ffiInfo
507 ECase (check, 506 (* We ensure before this step that all arguments aren't effectful.
508 [((PNone stringTyp, loc), 507 by turning them into local variables as needed. *)
509 (ELet (varName "q", resultTyp, query, (ESeq (store, rel0), loc)), loc)), 508 val argsInc = map (incRels 1) args
510 ((PSome (stringTyp, (PVar (varName "hit", stringTyp), loc)), loc), 509 val check = (check (i, args), loc)
511 (* Boolean is false because we're not unurlifying from a cookie. *) 510 val store = (store (i, argsInc, urlified), loc)
512 (EUnurlify (rel0, resultTyp, false), loc))], 511 in
513 {disc = stringTyp, result = resultTyp}) 512 SOME (ECase
514 end 513 (check,
515 514 [((PNone stringTyp, loc),
516 fun fileMapfold doExp file start = 515 (ELet (varName "q", resultTyp, exp, (ESeq (store, rel0), loc)), loc)),
516 ((PSome (stringTyp, (PVar (varName "hit", stringTyp), loc)), loc),
517 (* Boolean is false because we're not unurlifying from a cookie. *)
518 (EUnurlify (rel0, resultTyp, false), loc))],
519 {disc = (TOption stringTyp, loc), result = resultTyp}))
520 end
521 end
522
523 fun fileMapfoldB doExp file start =
517 case MonoUtil.File.mapfoldB 524 case MonoUtil.File.mapfoldB
518 {typ = Search.return2, 525 {typ = Search.return2,
519 exp = fn env => fn e' => fn s => Search.Continue (doExp env e' s), 526 exp = fn env => fn e' => fn s => Search.Continue (doExp env e' s),
520 decl = fn _ => Search.return2, 527 decl = fn _ => Search.return2,
521 bind = doBind} 528 bind = doBind}
522 MonoEnv.empty file start of 529 MonoEnv.empty file start of
523 Search.Continue x => x 530 Search.Continue x => x
524 | Search.Return _ => raise Match 531 | Search.Return _ => raise Match
525 532
526 fun fileMap doExp file = #1 (fileMapfold (fn _ => fn e => fn _ => (doExp e, ())) file ()) 533 fun fileMap doExp file = #1 (fileMapfoldB (fn _ => fn e => fn _ => (doExp e, ())) file ())
527 534
528 fun factorOutNontrivial text = 535 fun factorOutNontrivial text =
529 let 536 let
530 val loc = dummyLoc 537 val loc = dummyLoc
531 fun strcat (e1, e2) = (EStrcat (e1, e2), loc) 538 fun strcat (e1, e2) = (EStrcat (e1, e2), loc)
559 (newText, wrapLets, numArgs) 566 (newText, wrapLets, numArgs)
560 end 567 end
561 568
562 fun addChecking file = 569 fun addChecking file =
563 let 570 let
571 val effs = effectfulDecls file
564 fun doExp env (queryInfo as (tableToIndices, indexToQueryNumArgs, index)) = 572 fun doExp env (queryInfo as (tableToIndices, indexToQueryNumArgs, index)) =
565 fn e' as EQuery {query = origQueryText, 573 fn e' as EQuery {query = origQueryText,
566 state = resultTyp, 574 state = resultTyp,
567 initial, body, tables, exps} => 575 initial, body, tables, exps} =>
568 let 576 let
580 (* DEBUG *) 588 (* DEBUG *)
581 val () = Print.preface ("sqlcache> ", (MonoPrint.p_exp MonoEnv.empty queryText)) 589 val () = Print.preface ("sqlcache> ", (MonoPrint.p_exp MonoEnv.empty queryText))
582 val args = List.tabulate (numArgs, fn n => (ERel n, dummyLoc)) 590 val args = List.tabulate (numArgs, fn n => (ERel n, dummyLoc))
583 fun bind x f = Option.mapPartial f x 591 fun bind x f = Option.mapPartial f x
584 fun guard b x = if b then x else NONE 592 fun guard b x = if b then x else NONE
585 val effs = effectfulDecls file
586 (* We use dummyTyp here. I think this is okay because databases 593 (* We use dummyTyp here. I think this is okay because databases
587 don't store (effectful) functions, but perhaps there's some 594 don't store (effectful) functions, but perhaps there's some
588 pathalogical corner case missing.... *) 595 pathalogical corner case missing.... *)
589 fun safe bound = 596 fun safe bound =
590 not 597 not
594 env) 601 env)
595 val attempt = 602 val attempt =
596 (* Ziv misses Haskell's do notation.... *) 603 (* Ziv misses Haskell's do notation.... *)
597 guard (safe 0 queryText andalso safe 0 initial andalso safe 2 body) ( 604 guard (safe 0 queryText andalso safe 0 initial andalso safe 2 body) (
598 bind (Sql.parse Sql.query queryText) (fn queryParsed => 605 bind (Sql.parse Sql.query queryText) (fn queryParsed =>
599 SOME (wrapLets (cacheWrap (env, queryExp, index, resultTyp, args)), 606 bind (cacheWrap (env, queryExp, resultTyp, args, index)) (fn cachedExp =>
607 SOME (wrapLets cachedExp,
600 (SS.foldr (fn (tab, qi) => SIMM.insert (qi, tab, index)) 608 (SS.foldr (fn (tab, qi) => SIMM.insert (qi, tab, index))
601 tableToIndices 609 tableToIndices
602 (tablesQuery queryParsed), 610 (tablesQuery queryParsed),
603 IM.insert (indexToQueryNumArgs, index, (queryParsed, numArgs)), 611 IM.insert (indexToQueryNumArgs, index, (queryParsed, numArgs)),
604 index + 1)))) 612 index + 1)))))
605 in 613 in
606 case attempt of 614 case attempt of
607 SOME pair => pair 615 SOME pair => pair
608 | NONE => (e', queryInfo) 616 | NONE => (e', queryInfo)
609 end 617 end
610 | e' => (e', queryInfo) 618 | e' => (e', queryInfo)
611 in 619 in
612 fileMapfold (fn env => fn exp => fn state => doExp env state exp) 620 (fileMapfoldB (fn env => fn exp => fn state => doExp env state exp)
613 file 621 file
614 (SIMM.empty, IM.empty, 0) 622 (SIMM.empty, IM.empty, 0),
623 effs)
615 end 624 end
616 625
617 structure Invalidations = struct 626 structure Invalidations = struct
618 627
619 val loc = dummyLoc 628 val loc = dummyLoc
660 val invalidations = Invalidations.invalidations 669 val invalidations = Invalidations.invalidations
661 670
662 (* DEBUG *) 671 (* DEBUG *)
663 val gunk : ((Sql.query * int) * Sql.dml) list ref = ref [] 672 val gunk : ((Sql.query * int) * Sql.dml) list ref = ref []
664 673
665 fun addFlushing (file, (tableToIndices, indexToQueryNumArgs, _)) = 674 fun addFlushing ((file, (tableToIndices, indexToQueryNumArgs, index)), effs) =
666 let 675 let
667 val flushes = List.concat o 676 val flushes = List.concat o
668 map (fn (i, argss) => map (fn args => flush (i, args)) argss) 677 map (fn (i, argss) => map (fn args => flush (i, args)) argss)
669 val doExp = 678 val doExp =
670 fn EDml (origDmlText, failureMode) => 679 fn EDml (origDmlText, failureMode) =>
692 end 701 end
693 | e' => e' 702 | e' => e'
694 in 703 in
695 (* DEBUG *) 704 (* DEBUG *)
696 gunk := []; 705 gunk := [];
697 fileMap doExp file 706 (fileMap doExp file, index, effs)
698 end 707 end
699 708
700 val inlineSql = 709 val inlineSql =
701 let 710 let
702 val doExp = 711 val doExp =
711 | e => e 720 | e => e
712 in 721 in
713 fileMap doExp 722 fileMap doExp
714 end 723 end
715 724
716 fun go file =
717 let
718 (* TODO: do something nicer than [Sql] being in one of two modes. *)
719 val () = (resetFfiInfo (); Sql.sqlcacheMode := true)
720 val file' = addFlushing (addChecking (inlineSql file))
721 val () = Sql.sqlcacheMode := false
722 in
723 file'
724 end
725
726 725
727 (**********************) 726 (**********************)
728 (* Mono Type Checking *) 727 (* Mono Type Checking *)
729 (**********************) 728 (**********************)
730
731 val typOfPrim =
732 fn Prim.Int _ => TFfi ("Basis", "int")
733 | Prim.Float _ => TFfi ("Basis", "int")
734 729
735 fun typOfExp' (env : MonoEnv.env) : exp' -> typ option = 730 fun typOfExp' (env : MonoEnv.env) : exp' -> typ option =
736 fn EPrim p => SOME (TFfi ("Basis", case p of 731 fn EPrim p => SOME (TFfi ("Basis", case p of
737 Prim.Int _ => "int" 732 Prim.Int _ => "int"
738 | Prim.Float _ => "double" 733 | Prim.Float _ => "double"
777 | EWrite _ => SOME (TRecord [], dummyLoc) 772 | EWrite _ => SOME (TRecord [], dummyLoc)
778 | ESeq (_, e) => typOfExp env e 773 | ESeq (_, e) => typOfExp env e
779 | ELet (s, t, e1, e2) => typOfExp (MonoEnv.pushERel env s t (SOME e1)) e2 774 | ELet (s, t, e1, e2) => typOfExp (MonoEnv.pushERel env s t (SOME e1)) e2
780 | EClosure _ => NONE 775 | EClosure _ => NONE
781 | EUnurlify (_, t, _) => SOME t 776 | EUnurlify (_, t, _) => SOME t
777 | _ => NONE
782 778
783 and typOfExp env (e', loc) = typOfExp' env e' 779 and typOfExp env (e', loc) = typOfExp' env e'
784 780
785 781
786 (*******************************) 782 (*******************************)
795 791
796 val expOfSubexp = 792 val expOfSubexp =
797 fn Pure f => f () 793 fn Pure f => f ()
798 | Impure e => e 794 | Impure e => e
799 795
800 val makeCache : MonoEnv.env -> exp' -> exp' = fn _ => fn _ => raise Fail "TODO" 796 fun makeCache (env, exp', index) =
801 797 case typOfExp' env exp' of
802 fun pureCache (effs : IS.set) (env : MonoEnv.env) (exp as (exp', loc)) : subexp = 798 NONE => NONE
799 | SOME (TFun _, _) => NONE
800 | SOME typ =>
801 case ListUtil.foldri (fn (_, _, NONE) => NONE
802 | (n, typ, SOME args) =>
803 case MonoFooify.urlify env ((ERel n, dummyLoc), typ) of
804 NONE => NONE
805 | SOME arg => SOME (arg :: args))
806 (SOME [])
807 (MonoEnv.typeContext env) of
808 NONE => NONE
809 | SOME args => cacheWrap (env, (exp', dummyLoc), typ, args, index)
810
811 fun pureCache (effs : IS.set) ((env, exp as (exp', loc)), index) : subexp * int =
803 let 812 let
804 fun wrapBindN f (args : (MonoEnv.env * exp) list) = 813 fun wrapBindN f (args : (MonoEnv.env * exp) list) =
805 let 814 let
806 val subexps = map (fn (env, exp) => pureCache effs env exp) args 815 val (subexps, index) = ListUtil.foldlMap (pureCache effs) index args
816 fun mkExp () = (f (map expOfSubexp subexps), loc)
807 in 817 in
808 if List.exists isImpure subexps 818 if List.exists isImpure subexps
809 then Impure (f (map expOfSubexp subexps), loc) 819 then (Impure (mkExp ()), index)
810 else Pure (fn () => (makeCache env (f (map #2 args)), loc)) 820 else (Pure (fn () => case makeCache (env, f (map #2 args), index) of
821 NONE => mkExp ()
822 | SOME e' => (e', loc)),
823 (* Conservatively increment index. *)
824 index + 1)
811 end 825 end
812 fun wrapBind1 f arg = 826 fun wrapBind1 f arg =
813 wrapBindN (fn [arg] => f arg | _ => raise Match) [arg] 827 wrapBindN (fn [arg] => f arg | _ => raise Match) [arg]
814 fun wrapBind2 f (arg1, arg2) = 828 fun wrapBind2 f (arg1, arg2) =
815 wrapBindN (fn [arg1, arg2] => f (arg1, arg2) | _ => raise Match) [arg1, arg2] 829 wrapBindN (fn [arg1, arg2] => f (arg1, arg2) | _ => raise Match) [arg1, arg2]
835 | EField (e, s) => wrap1 (fn e => EField (e, s)) e 849 | EField (e, s) => wrap1 (fn e => EField (e, s)) e
836 | ECase (e, cases, {disc, result}) => 850 | ECase (e, cases, {disc, result}) =>
837 wrapBindN (fn (e::es) => 851 wrapBindN (fn (e::es) =>
838 ECase (e, 852 ECase (e,
839 (ListPair.map (fn (e, (p, _)) => (p, e)) (es, cases)), 853 (ListPair.map (fn (e, (p, _)) => (p, e)) (es, cases)),
840 {disc = disc, result = result})) 854 {disc = disc, result = result})
855 | _ => raise Match)
841 ((env, e) :: map (fn (p, e) => (MonoEnv.patBinds env p, e)) cases) 856 ((env, e) :: map (fn (p, e) => (MonoEnv.patBinds env p, e)) cases)
842 | EStrcat (e1, e2) => wrap2 EStrcat (e1, e2) 857 | EStrcat (e1, e2) => wrap2 EStrcat (e1, e2)
843 (* We record page writes, so they're cachable. *) 858 (* We record page writes, so they're cachable. *)
844 | EWrite e => wrap1 EWrite e 859 | EWrite e => wrap1 EWrite e
845 | ESeq (e1, e2) => wrap2 ESeq (e1, e2) 860 | ESeq (e1, e2) => wrap2 ESeq (e1, e2)
847 wrapBind2 (fn (e1, e2) => ELet (s, t, e1, e2)) 862 wrapBind2 (fn (e1, e2) => ELet (s, t, e1, e2))
848 ((env, e1), (MonoEnv.pushERel env s t (SOME e1), e2)) 863 ((env, e1), (MonoEnv.pushERel env s t (SOME e1), e2))
849 (* ASK: | EClosure (n, es) => ? *) 864 (* ASK: | EClosure (n, es) => ? *)
850 | EUnurlify (e, t, b) => wrap1 (fn e => EUnurlify (e, t, b)) e 865 | EUnurlify (e, t, b) => wrap1 (fn e => EUnurlify (e, t, b)) e
851 | _ => if effectful effs env exp 866 | _ => if effectful effs env exp
852 then Impure exp 867 then (Impure exp, index)
853 else Pure (fn () => (makeCache env exp', loc)) 868 else (Pure (fn () => (case makeCache (env, exp', index) of
869 NONE => exp'
870 | SOME e' => e',
871 loc)),
872 index + 1)
873 end
874
875 fun addPure ((decls, sideInfo), index, effs) =
876 let
877 fun doVal ((x, n, t, exp, s), index) =
878 let
879 val (subexp, index) = pureCache effs ((MonoEnv.empty, exp), index)
880 in
881 ((x, n, t, expOfSubexp subexp, s), index)
882 end
883 fun doDecl' (decl', index) =
884 case decl' of
885 DVal v =>
886 let
887 val (v, index) = (doVal (v, index))
888 in
889 (DVal v, index)
890 end
891 | DValRec vs =>
892 let
893 val (vs, index) = ListUtil.foldlMap doVal index vs
894 in
895 (DValRec vs, index)
896 end
897 | _ => (decl', index)
898 fun doDecl ((decl', loc), index) =
899 let
900 val (decl', index) = doDecl' (decl', index)
901 in
902 ((decl', loc), index)
903 end
904 val decls = #1 (ListUtil.foldlMap doDecl index decls)
905 (* Important that this happens after the MonoFooify.urlify calls! *)
906 val fmDecls = MonoFooify.getNewFmDecls ()
907 in
908 print (Int.toString (length fmDecls));
909 (decls @ fmDecls, sideInfo)
910 end
911
912 val go' = addPure o addFlushing o addChecking o inlineSql
913
914 fun go file =
915 let
916 (* TODO: do something nicer than [Sql] being in one of two modes. *)
917 val () = (resetFfiInfo (); Sql.sqlcacheMode := true)
918 val file' = go' file
919 val () = Sql.sqlcacheMode := false
920 in
921 file'
854 end 922 end
855 923
856 end 924 end