Mercurial > urweb
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 |