diff --git a/test_cache/.gitignore b/test_cache/.gitignore
index 33704e09912dbab71db4a8158fc11f530a62fec8..f6d03ced906aaef9b3587540781f969c23a2ecf4 100644
--- a/test_cache/.gitignore
+++ b/test_cache/.gitignore
@@ -2,3 +2,4 @@ __pycache__
 .dmypy.json
 .coverage*
 .cache
+.r-mount
diff --git a/test_cache/README.rst b/test_cache/README.rst
index 8d6ef478ede29a0bcd037bc8d0e3066b6dd5c7bf..bba25caef189c2b0d9062540ea1c6f5769158839 100644
--- a/test_cache/README.rst
+++ b/test_cache/README.rst
@@ -1,3 +1,9 @@
 ======================
 charmonium.determ_hash
 ======================
+
+::
+
+   nix develop --command poetry install
+   nix develop --command conda-shell -c conda-install
+   nix develop --command conda-shell -c 'conda env create --name exoplanet --file resources/exoplanet/environment.yaml'
diff --git a/test_cache/data/1/overheads.csv b/test_cache/data/1/overheads.csv
new file mode 100644
index 0000000000000000000000000000000000000000..a64fb61dc13a931b601230f84d42c600ffbf9fc7
--- /dev/null
+++ b/test_cache/data/1/overheads.csv
@@ -0,0 +1,10 @@
+name,function,outer_function,hash,misses,hits,inner_function,serialize,obj_store,obj_load,deserialize
+exoplanet,__main__.get_data,0.516634,0.037035,1,23,0.469482,0.000685,0.000345,0.001309,0.000278
+exoplanet,__main__.get_map_params,13.7643,2.704656,2,6,11.054364,0.0013440000000000001,0.000411,0.00047,0.00053
+exoplanet,__main__.get_model,28.193264,0.047777,8,0,27.811911000000002,0.362315,0.015686,0.0,0.0
+exoplanet,__main__.parse_data,0.047681,0.015999,24,0,0.01933,0.006297,0.002461,0.0,0.0
+exoplanet,__main__.plot_data,1.898237,0.021736000000000002,24,0,1.694293,0.17616199999999999,0.006036,0.0,0.0
+exoplanet,__main__.plot_params,1.478501,0.010706,8,0,1.309278,0.156314,0.004459,0.0,0.0
+exoplanet,__main__.plot_sample,2.372399,0.01383,8,0,1.8557830000000002,0.50007,0.015804,0.0,0.0
+exoplanet,__main__.run_model,226.717374,2.684368,8,0,223.306716,0.715352,0.186685,0.0,0.0
+exoplanet,__main__.summarize_model,6.104459,0.004854,8,0,5.231528,0.865816,0.022583,0.0,0.0
diff --git a/test_cache/data/1/results.csv b/test_cache/data/1/results.csv
new file mode 100644
index 0000000000000000000000000000000000000000..1631689b3ce6a62b23abbe018365e069655fab63
--- /dev/null
+++ b/test_cache/data/1/results.csv
@@ -0,0 +1,13 @@
+name,commit_no,commit,orig_time,orig_success,memo_time,memo_success,stdout_match,index_read,index_write,functions
+exoplanet,0,a61076b173a32fc90f286176dc5f194395854e02,270.496931,True,44.286457,True,True,0.000761,0.013449,0
+exoplanet,1,79a451778ce68c9ebf512d6df0bd0f317932b521,42.989056,True,39.951842,True,True,0.001413,0.013597999999999999,0
+exoplanet,2,5e633162c0f7ee375fe3b6cae1fedb53f0198c08,42.621853,True,40.053124,True,True,0.001459,0.013687000000000001,0
+exoplanet,3,b2d5ee484bb1c968a81a6f6a7b442254786f120c,6.212746,False,5.810497,False,True,0.001436,0.0023899999999999998,0
+exoplanet,4,9f7681301790276416c10f8139adb1b24f7a7d04,43.689584,True,40.197685,True,True,0.001376,0.013776,0
+exoplanet,5,7808e453cc3f689f5ad7cc7d03588bc35e1579ba,6.221139,False,5.769807,False,True,0.0013570000000000001,0.002493,0
+exoplanet,6,1760761f26815d72232800c90d8f49d1b6a2d65b,6.651908,False,5.815202,False,True,0.001349,0.002521,0
+exoplanet,7,db5e8775fdb51a7633941890dd59c7d17f201f45,6.158167,False,5.792711,False,True,0.0013410000000000002,0.002419,0
+exoplanet,8,5bd4e8464596bfb7ed32ae764cf498600b16ffdf,6.322904,False,5.894274,False,True,0.0013399999999999998,0.002423,0
+exoplanet,9,d3828e82812c741590846858974b65036d269c88,5.895946,False,5.326834,False,True,0.001406,0.0023369999999999997,0
+exoplanet,10,758bf1dd34270e8c9ab126335a00df275da6995d,5.89716,False,5.358514,False,True,0.001284,0.002309,0
+exoplanet,11,57d5787c70cfcb2c92d0825cb2559476dad1b498,5.751583,False,5.343576,False,True,0.001284,0.002229,0
diff --git a/test_cache/data/2/overheads.csv b/test_cache/data/2/overheads.csv
new file mode 100644
index 0000000000000000000000000000000000000000..78f0047b23a6e0ce2ec6f8a51d3878c30c60a8f8
--- /dev/null
+++ b/test_cache/data/2/overheads.csv
@@ -0,0 +1,10 @@
+name,function,outer_function,hash,misses,hits,inner_function,serialize,obj_store,obj_load,deserialize
+exoplanet,__main__.get_data,0.469636,0.022306,1,15,0.440343,0.000672,0.000334,0.000834,0.00017999999999999998
+exoplanet,__main__.get_map_params,6.160355,0.6795519999999999,1,1,5.479162,0.000732,0.000216,7.9e-05,8.8e-05
+exoplanet,__main__.get_model,7.034789,0.032155,2,0,6.941108,0.088953,0.0038789999999999996,0.0,0.0
+exoplanet,__main__.parse_data,0.033538,0.010083,16,0,0.01487,0.004409000000000001,0.001702,0.0,0.0
+exoplanet,__main__.plot_data,1.241321,0.014928,16,0,1.104913,0.11744399999999999,0.004001,0.0,0.0
+exoplanet,__main__.plot_params,0.37869600000000003,0.002757,2,0,0.336098,0.039313,0.001058,0.0,0.0
+exoplanet,__main__.plot_sample,0.727521,0.003434,2,0,0.660883,0.062505,0.003915,0.0,0.0
+exoplanet,__main__.run_model,56.783927000000006,0.675585,2,0,55.927837999999994,0.177818,0.04704,0.0,0.0
+exoplanet,__main__.summarize_model,1.5728179999999998,0.0012000000000000001,2,0,1.346722,0.224327,0.005851,0.0,0.0
diff --git a/test_cache/data/2/results.csv b/test_cache/data/2/results.csv
new file mode 100644
index 0000000000000000000000000000000000000000..d164c52cb1c40b03e6a2a40bdb64ea9f4d14cd4e
--- /dev/null
+++ b/test_cache/data/2/results.csv
@@ -0,0 +1,9 @@
+name,commit_no,commit,orig_time,orig_success,memo_time,memo_success,stdout_match,index_read,index_write,functions
+exoplanet,0,470b9be8911aa9b344d1e8f0c644d36b57e27c6d,5.731762,False,5.873491,False,True,0.000604,0.0018319999999999999,0
+exoplanet,1,c7d992bd58ed9033e2fd2d98ccaea9554b1b299e,5.840564,False,5.446461,False,True,0.0010479999999999999,0.002049,0
+exoplanet,2,a86b5c194479d7c6bee2e8122bf48440c4710d77,5.882226,False,5.391061,False,True,0.001064,0.002103,0
+exoplanet,3,d30a7b8e1ede786cec3032769632f7321b73bebf,6.416347,False,5.420914,False,True,0.001042,0.002031,0
+exoplanet,4,f4ef1a577c2d62c9c72bf06124caf4d3aefaba44,5.86483,False,5.410463,False,True,0.001037,0.002048,0
+exoplanet,5,6cf0bcf067d0651fad4043d1307532b954b99d5e,5.721428,False,5.395525,False,True,0.001038,0.002035,0
+exoplanet,6,57d5787c70cfcb2c92d0825cb2559476dad1b498,5.781274,False,5.402956,False,True,0.001034,0.0020369999999999997,0
+exoplanet,7,10b4ec3a99f07f35ffa9a7abfef083399af2a2d2,42.626809,True,43.207115,True,True,0.00132,0.013579,0
diff --git a/test_cache/flake.nix b/test_cache/flake.nix
index 377086b26ef4b717b9989e991c0411a248efd5e4..dd98962b6864b1ec78a29937b90a2643dc48e35e 100644
--- a/test_cache/flake.nix
+++ b/test_cache/flake.nix
@@ -19,6 +19,8 @@
         };
         packages.${name-shell} = pkgs.mkShell {
           buildInputs = alternative-pythons ++ [
+            default-python
+            pkgs.git
             pkgs.poetry
             pkgs.micromamba
             pkgs.conda
@@ -28,7 +30,9 @@
             #   python = default-python;
             # })
           ];
-          # TODO: write a check expression (`nix flake check`)
+          shellHook = ''
+            export LD_LIBRARY_PATH=${pkgs.stdenv.cc.cc.lib}/lib
+          '';
         };
         devShell = self.packages.${system}.${name-shell};
         defaultPackage = self.packages.${system}.${name};
diff --git a/test_cache/poetry.lock b/test_cache/poetry.lock
index 8db8c06138a139ee3dbd7cecd7883c0d79f31d85..ad39aa4024a65db5f4fcff98bc91231ee2bedefc 100644
--- a/test_cache/poetry.lock
+++ b/test_cache/poetry.lock
@@ -193,8 +193,10 @@ fasteners = "^0.16"
 xxhash = "^2.0.2"
 
 [package.source]
-type = "directory"
-url = "../../charmonium.cache"
+type = "git"
+url = "https://github.com/charmoniumQ/charmonium.cache.git"
+reference = "master"
+resolved_reference = "fff4fda1ba53fae98ffd31368e6d38be9cb1efcd"
 
 [[package]]
 name = "charset-normalizer"
@@ -533,6 +535,14 @@ category = "dev"
 optional = false
 python-versions = "*"
 
+[[package]]
+name = "numpy"
+version = "1.21.4"
+description = "NumPy is the fundamental package for array computing with Python."
+category = "main"
+optional = false
+python-versions = ">=3.7,<3.11"
+
 [[package]]
 name = "packaging"
 version = "21.2"
@@ -544,6 +554,26 @@ python-versions = ">=3.6"
 [package.dependencies]
 pyparsing = ">=2.0.2,<3"
 
+[[package]]
+name = "pandas"
+version = "1.3.4"
+description = "Powerful data structures for data analysis, time series, and statistics"
+category = "main"
+optional = false
+python-versions = ">=3.7.1"
+
+[package.dependencies]
+numpy = [
+    {version = ">=1.17.3", markers = "platform_machine != \"aarch64\" and platform_machine != \"arm64\" and python_version < \"3.10\""},
+    {version = ">=1.19.2", markers = "platform_machine == \"aarch64\" and python_version < \"3.10\""},
+    {version = ">=1.20.0", markers = "platform_machine == \"arm64\" and python_version < \"3.10\""},
+]
+python-dateutil = ">=2.7.3"
+pytz = ">=2017.3"
+
+[package.extras]
+test = ["hypothesis (>=3.58)", "pytest (>=6.0)", "pytest-xdist"]
+
 [[package]]
 name = "parso"
 version = "0.8.2"
@@ -771,11 +801,22 @@ python-versions = "*"
 pytest = ">=3.0.0"
 tblib = "*"
 
+[[package]]
+name = "python-dateutil"
+version = "2.8.2"
+description = "Extensions to the standard Python datetime module"
+category = "main"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+
+[package.dependencies]
+six = ">=1.5"
+
 [[package]]
 name = "pytz"
 version = "2021.3"
 description = "World timezone definitions, modern and historical"
-category = "dev"
+category = "main"
 optional = false
 python-versions = "*"
 
@@ -1236,7 +1277,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
 [metadata]
 lock-version = "1.1"
 python-versions = ">=3.8,<3.10"
-content-hash = "72a9f94e4512a2911040608b553a6f1826aa40c1c3956aafa67468c27717d575"
+content-hash = "8169a2d736b25e5478db4923450322f00986686f81d7b95b61c0cf88f31c24c7"
 
 [metadata.files]
 alabaster = [
@@ -1631,10 +1672,68 @@ mypy-extensions = [
     {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
     {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
 ]
+numpy = [
+    {file = "numpy-1.21.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8890b3360f345e8360133bc078d2dacc2843b6ee6059b568781b15b97acbe39f"},
+    {file = "numpy-1.21.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:69077388c5a4b997442b843dbdc3a85b420fb693ec8e33020bb24d647c164fa5"},
+    {file = "numpy-1.21.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e89717274b41ebd568cd7943fc9418eeb49b1785b66031bc8a7f6300463c5898"},
+    {file = "numpy-1.21.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b78ecfa070460104934e2caf51694ccd00f37d5e5dbe76f021b1b0b0d221823"},
+    {file = "numpy-1.21.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:615d4e328af7204c13ae3d4df7615a13ff60a49cb0d9106fde07f541207883ca"},
+    {file = "numpy-1.21.4-cp310-cp310-win_amd64.whl", hash = "sha256:1403b4e2181fc72664737d848b60e65150f272fe5a1c1cbc16145ed43884065a"},
+    {file = "numpy-1.21.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:74b85a17528ca60cf98381a5e779fc0264b4a88b46025e6bcbe9621f46bb3e63"},
+    {file = "numpy-1.21.4-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92aafa03da8658609f59f18722b88f0a73a249101169e28415b4fa148caf7e41"},
+    {file = "numpy-1.21.4-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5d95668e727c75b3f5088ec7700e260f90ec83f488e4c0aaccb941148b2cd377"},
+    {file = "numpy-1.21.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5162ec777ba7138906c9c274353ece5603646c6965570d82905546579573f73"},
+    {file = "numpy-1.21.4-cp37-cp37m-win32.whl", hash = "sha256:81225e58ef5fce7f1d80399575576fc5febec79a8a2742e8ef86d7b03beef49f"},
+    {file = "numpy-1.21.4-cp37-cp37m-win_amd64.whl", hash = "sha256:32fe5b12061f6446adcbb32cf4060a14741f9c21e15aaee59a207b6ce6423469"},
+    {file = "numpy-1.21.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c449eb870616a7b62e097982c622d2577b3dbc800aaf8689254ec6e0197cbf1e"},
+    {file = "numpy-1.21.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2e4ed57f45f0aa38beca2a03b6532e70e548faf2debbeb3291cfc9b315d9be8f"},
+    {file = "numpy-1.21.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1247ef28387b7bb7f21caf2dbe4767f4f4175df44d30604d42ad9bd701ebb31f"},
+    {file = "numpy-1.21.4-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:34f3456f530ae8b44231c63082c8899fe9c983fd9b108c997c4b1c8c2d435333"},
+    {file = "numpy-1.21.4-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4c9c23158b87ed0e70d9a50c67e5c0b3f75bcf2581a8e34668d4e9d7474d76c6"},
+    {file = "numpy-1.21.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4799be6a2d7d3c33699a6f77201836ac975b2e1b98c2a07f66a38f499cb50ce"},
+    {file = "numpy-1.21.4-cp38-cp38-win32.whl", hash = "sha256:bc988afcea53e6156546e5b2885b7efab089570783d9d82caf1cfd323b0bb3dd"},
+    {file = "numpy-1.21.4-cp38-cp38-win_amd64.whl", hash = "sha256:170b2a0805c6891ca78c1d96ee72e4c3ed1ae0a992c75444b6ab20ff038ba2cd"},
+    {file = "numpy-1.21.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fde96af889262e85aa033f8ee1d3241e32bf36228318a61f1ace579df4e8170d"},
+    {file = "numpy-1.21.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c885bfc07f77e8fee3dc879152ba993732601f1f11de248d4f357f0ffea6a6d4"},
+    {file = "numpy-1.21.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9e6f5f50d1eff2f2f752b3089a118aee1ea0da63d56c44f3865681009b0af162"},
+    {file = "numpy-1.21.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ad010846cdffe7ec27e3f933397f8a8d6c801a48634f419e3d075db27acf5880"},
+    {file = "numpy-1.21.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c74c699b122918a6c4611285cc2cad4a3aafdb135c22a16ec483340ef97d573c"},
+    {file = "numpy-1.21.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9864424631775b0c052f3bd98bc2712d131b3e2cd95d1c0c68b91709170890b0"},
+    {file = "numpy-1.21.4-cp39-cp39-win32.whl", hash = "sha256:b1e2312f5b8843a3e4e8224b2b48fe16119617b8fc0a54df8f50098721b5bed2"},
+    {file = "numpy-1.21.4-cp39-cp39-win_amd64.whl", hash = "sha256:e3c3e990274444031482a31280bf48674441e0a5b55ddb168f3a6db3e0c38ec8"},
+    {file = "numpy-1.21.4-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a3deb31bc84f2b42584b8c4001c85d1934dbfb4030827110bc36bfd11509b7bf"},
+    {file = "numpy-1.21.4.zip", hash = "sha256:e6c76a87633aa3fa16614b61ccedfae45b91df2767cf097aa9c933932a7ed1e0"},
+]
 packaging = [
     {file = "packaging-21.2-py3-none-any.whl", hash = "sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0"},
     {file = "packaging-21.2.tar.gz", hash = "sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966"},
 ]
+pandas = [
+    {file = "pandas-1.3.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9707bdc1ea9639c886b4d3be6e2a45812c1ac0c2080f94c31b71c9fa35556f9b"},
+    {file = "pandas-1.3.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c2f44425594ae85e119459bb5abb0748d76ef01d9c08583a667e3339e134218e"},
+    {file = "pandas-1.3.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:372d72a3d8a5f2dbaf566a5fa5fa7f230842ac80f29a931fb4b071502cf86b9a"},
+    {file = "pandas-1.3.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99d2350adb7b6c3f7f8f0e5dfb7d34ff8dd4bc0a53e62c445b7e43e163fce63"},
+    {file = "pandas-1.3.4-cp310-cp310-win_amd64.whl", hash = "sha256:4acc28364863127bca1029fb72228e6f473bb50c32e77155e80b410e2068eeac"},
+    {file = "pandas-1.3.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c2646458e1dce44df9f71a01dc65f7e8fa4307f29e5c0f2f92c97f47a5bf22f5"},
+    {file = "pandas-1.3.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5298a733e5bfbb761181fd4672c36d0c627320eb999c59c65156c6a90c7e1b4f"},
+    {file = "pandas-1.3.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22808afb8f96e2269dcc5b846decacb2f526dd0b47baebc63d913bf847317c8f"},
+    {file = "pandas-1.3.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b528e126c13816a4374e56b7b18bfe91f7a7f6576d1aadba5dee6a87a7f479ae"},
+    {file = "pandas-1.3.4-cp37-cp37m-win32.whl", hash = "sha256:fe48e4925455c964db914b958f6e7032d285848b7538a5e1b19aeb26ffaea3ec"},
+    {file = "pandas-1.3.4-cp37-cp37m-win_amd64.whl", hash = "sha256:eaca36a80acaacb8183930e2e5ad7f71539a66805d6204ea88736570b2876a7b"},
+    {file = "pandas-1.3.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:42493f8ae67918bf129869abea8204df899902287a7f5eaf596c8e54e0ac7ff4"},
+    {file = "pandas-1.3.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a388960f979665b447f0847626e40f99af8cf191bce9dc571d716433130cb3a7"},
+    {file = "pandas-1.3.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba0aac1397e1d7b654fccf263a4798a9e84ef749866060d19e577e927d66e1b"},
+    {file = "pandas-1.3.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f567e972dce3bbc3a8076e0b675273b4a9e8576ac629149cf8286ee13c259ae5"},
+    {file = "pandas-1.3.4-cp38-cp38-win32.whl", hash = "sha256:c1aa4de4919358c5ef119f6377bc5964b3a7023c23e845d9db7d9016fa0c5b1c"},
+    {file = "pandas-1.3.4-cp38-cp38-win_amd64.whl", hash = "sha256:dd324f8ee05925ee85de0ea3f0d66e1362e8c80799eb4eb04927d32335a3e44a"},
+    {file = "pandas-1.3.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d47750cf07dee6b55d8423471be70d627314277976ff2edd1381f02d52dbadf9"},
+    {file = "pandas-1.3.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d1dc09c0013d8faa7474574d61b575f9af6257ab95c93dcf33a14fd8d2c1bab"},
+    {file = "pandas-1.3.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10e10a2527db79af6e830c3d5842a4d60383b162885270f8cffc15abca4ba4a9"},
+    {file = "pandas-1.3.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35c77609acd2e4d517da41bae0c11c70d31c87aae8dd1aabd2670906c6d2c143"},
+    {file = "pandas-1.3.4-cp39-cp39-win32.whl", hash = "sha256:003ba92db58b71a5f8add604a17a059f3068ef4e8c0c365b088468d0d64935fd"},
+    {file = "pandas-1.3.4-cp39-cp39-win_amd64.whl", hash = "sha256:a51528192755f7429c5bcc9e80832c517340317c861318fea9cea081b57c9afd"},
+    {file = "pandas-1.3.4.tar.gz", hash = "sha256:a2aa18d3f0b7d538e21932f637fbfe8518d085238b429e4790a35e1e44a96ffc"},
+]
 parso = [
     {file = "parso-0.8.2-py2.py3-none-any.whl", hash = "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"},
     {file = "parso-0.8.2.tar.gz", hash = "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398"},
@@ -1721,6 +1820,10 @@ pytest-parallel = [
     {file = "pytest-parallel-0.1.1.tar.gz", hash = "sha256:9aac3fc199a168c0a8559b60249d9eb254de7af58c12cee0310b54d4affdbfab"},
     {file = "pytest_parallel-0.1.1-py3-none-any.whl", hash = "sha256:9e3703015b0eda52be9e07d2ba3498f09340a56d5c79a39b50f22fc5c38212fe"},
 ]
+python-dateutil = [
+    {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
+    {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
+]
 pytz = [
     {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"},
     {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"},
diff --git a/test_cache/pyproject.toml b/test_cache/pyproject.toml
index 02918907e3e01775e81923dbddb6f1b2ddd1c6a5..e024fee9b344962cb3fe8de4057c5fed0ab743fb 100644
--- a/test_cache/pyproject.toml
+++ b/test_cache/pyproject.toml
@@ -12,7 +12,8 @@ python = ">=3.8,<3.10"
 typer = "^0.4.0"
 "charmonium.async-subprocess" = "^0.1.7"
 tqdm = "^4.62.3"
-"charmonium.cache" = {path = "../../charmonium.cache"}
+"charmonium.cache" = {git = "https://github.com/charmoniumQ/charmonium.cache.git"}
+pandas = "^1.3.4"
 
 [tool.poetry.dev-dependencies]
 mypy = "^0.910"
diff --git a/test_cache/resources/exoplanet/environment.yaml b/test_cache/resources/exoplanet/environment.yaml
index 3494b2d4ef9b37561b9ae7554d1ba72933f0ed40..1fbcaf5128510aad5dfb581eb33b92f5ada34800 100644
--- a/test_cache/resources/exoplanet/environment.yaml
+++ b/test_cache/resources/exoplanet/environment.yaml
@@ -114,10 +114,11 @@ dependencies:
     - astropy==4.3.1
     - attrs==20.3.0
     - bitmath==1.3.3.1
+    - charmonium-cache==1.2.3
+    - charmonium-time-block==0.2.1
     - corner==2.2.1
-    - exoplanet==0.5.1
-    - exoplanet-core==0.1.2
     - fasteners==0.16.3
+    - psutil==5.8.0
     - pyerfa==2.0.0.1
     - pymc3-ext==0.1.1
     - xxhash==2.0.2
diff --git a/test_cache/resources/exoplanet/script.py b/test_cache/resources/exoplanet/script.py
index f1cd3716acfd5bb44e2ee68454ee798563f5da92..a0bfbec30d91e9ab6a607783d941eb50b876ceab 100644
--- a/test_cache/resources/exoplanet/script.py
+++ b/test_cache/resources/exoplanet/script.py
@@ -10,10 +10,12 @@ if os.environ.get("CHARMONIUM_CACHE", "") == "enable":
     import logging
     perf_logger = logging.getLogger("charmonium.cache.perf")
     perf_logger.setLevel(logging.DEBUG)
-    perf_logger.addHandler(logging.FileHandler(os.environ["CHARMONIUM_CACHE_PERF_LOG"]))
-    size_logger = logging.getLogger("charmonium.cache.size")
-    size_logger.setLevel(logging.DEBUG)
-    size_logger.addHandler(logging.FileHandler(os.environ["CHARMONIUM_CACHE_SIZE_LOG"]))
+    perf_logger.addHandler(logging.FileHandler(os.environ.get("CHARMONIUM_CACHE_PERF_LOG", "/tmp/perf.log")))
+    perf_logger.propagate = False
+    # hash_logger = logging.getLogger("charmonium.cache.determ_hash")
+    # hash_logger.setLevel(logging.DEBUG)
+    # hash_logger.addHandler(logging.FileHandler("/tmp/hash.log"))
+    # hash_logger.propagate = False
     from charmonium.cache import memoize
 else:
     memoize = lambda: lambda x: x
diff --git a/test_cache/test_cache/__main__.py b/test_cache/test_cache/__main__.py
index 26ce363528b3b07ce39cc33d04ab9aa8449ee5a7..f0e6fafb693bb5cd8e92e6f38c92afe43385254c 100644
--- a/test_cache/test_cache/__main__.py
+++ b/test_cache/test_cache/__main__.py
@@ -1,4 +1,47 @@
 import asyncio
+import typer
+import pandas as pd
+from pathlib import Path
+from dataclasses import asdict
 from .test_cache import test_cache
 
-asyncio.run(test_cache())
+
+ROOT = Path(__file__).parent.parent
+
+
+def main():
+    repo_results = asyncio.run(test_cache())
+    pd.DataFrame.from_records(
+        [
+            {
+                "commit": commit_result.commit,
+                "orig_time": commit_result.orig_time,
+                "orig_success": commit_result.orig_success,
+                "memo_time": commit_result.memo_time,
+                "memo_success": commit_result.memo_success,
+                "stdout_match": commit_result.stdout_match,
+                "index_read": commit_result.index_read,
+                "index_write": commit_result.index_write,
+                "name": repo.name,
+                "commit_no": commit_no,
+                "functions": 0,
+            }
+            for repo, commit_results in repo_results
+            for commit_no, commit_result in enumerate(commit_results)
+        ],
+        index=("name", "commit_no")
+    ).to_csv(ROOT / "results.csv")
+    pd.DataFrame.from_records(
+        [
+            {
+                **asdict(function_data),
+                "name": repo.name,
+                "function": function,
+            }
+            for repo, commit_results in repo_results
+            for commit_result in commit_results
+            for function, function_data in commit_result.functions.items()
+        ],
+    ).groupby(by=["name", "function"]).sum().to_csv(ROOT / "overheads.csv")
+
+typer.run(main)
diff --git a/test_cache/test_cache/data.py b/test_cache/test_cache/data.py
index 2e0ca06ab46e992a639edfc07906f0cba5768e98..ff6b5a5b49bb7e872d26fc24389da40609181da2 100644
--- a/test_cache/test_cache/data.py
+++ b/test_cache/test_cache/data.py
@@ -36,21 +36,27 @@ class Action(Protocol):
 class GitRepo(Repo):
     def __init__(
         self,
+        *,
+        name: str,
         url: str,
         start_commit: Optional[str] = None,
         stop_commit: Optional[str] = None,
+        all_commits: Optional[List[str]] = None,
     ) -> None:
         super().__init__()
         self.url = url
-        self.name = urlparse(self.url).path.replace("/", "_")
+        self.name = name
         self.dir = CACHE_PATH / self.name
         self.start_commit = start_commit
         self.stop_commit = stop_commit
+        self.all_commits = all_commits
 
     async def setup(self) -> None:
         self.dir.mkdir(exist_ok=True, parents=True)
         if not list(self.dir.iterdir()):
-            await async_run(["git", "clone", self.url, self.dir], check=True)
+            await async_run(["git", "clone", self.url, "."], cwd=self.dir, check=True)
+        else:
+            await async_run(["git", "clean", "-fdx", "."], cwd=self.dir, check=True)
         proc = await async_run(
             ["git", "symbolic-ref", "refs/remotes/origin/HEAD"],
             cwd=self.dir,
@@ -62,50 +68,47 @@ class GitRepo(Repo):
         await async_run(["git", "checkout", branch], cwd=self.dir, check=True)
 
     def get_commits(self) -> List[str]:
-        proc = subprocess.run(
-            [
-                "git",
-                "log",
-                "--pretty=format:%H",
-                self.stop_commit if self.stop_commit else "HEAD",
-                *(["^" + self.start_commit] if self.start_commit else []),
-            ],
-            cwd=self.dir,
-            check=True,
-            capture_output=True,
-            text=True,
-        )
-        return proc.stdout.split("\n")
+        if self.all_commits is not None:
+            assert self.start_commit is None and self.stop_commit is None
+            return self.all_commits
+        else:
+            proc = subprocess.run(
+                [
+                    "git",
+                    "log",
+                    "--pretty=format:%H",
+                    self.stop_commit if self.stop_commit else "HEAD",
+                    *(["^" + self.start_commit] if self.start_commit else []),
+                ],
+                cwd=self.dir,
+                check=True,
+                capture_output=True,
+                text=True,
+            )
+            return proc.stdout.split("\n")
 
     def checkout(self, commit: str) -> None:
         subprocess.run(["git", "checkout", commit], cwd=self.dir, check=True, capture_output=True)
 
 
 class CondaAction(Action):
-    def __init__(self, name: str, install_repo: bool = False) -> None:
+    def __init__(self, *, name: str, install_repo: bool = False) -> None:
         super().__init__()
         self.name = name
         self.environment = RESOURCE_PATH / self.name / "environment.yaml"
         self.script = RESOURCE_PATH / self.name / "script.py"
-        self.install_repo = False
+        self.install_repo = install_repo
 
     async def setup(self, repo: Repo) -> None:
-        await async_run(
-            [
-                "conda-shell",
-                "-c",
-                f"conda create --name {self.name} --file {self.environment}",
-            ],
-            check=True,
-        )
-        await async_run(
-            [
-                "conda-shell",
-                "-c",
-                f"conda run --name {self.name} pip install {ROOT / '../../charmonium.cache'}",
-            ],
-            check=True,
-        )
+        # await async_run(
+        #     [
+        #         "conda-shell",
+        #         "-c",
+        #         f"conda env create --name {self.name} --file {self.environment}",
+        #     ],
+        #     check=True,
+        #     capture_output=True,
+        # )
         if self.install_repo:
             await async_run(
                 [
@@ -120,6 +123,7 @@ class CondaAction(Action):
         proc = subprocess.run(
             ["conda-shell", "-c", f"conda run --name {self.name} python {self.script}"],
             check=False,
+            cwd=repo.dir,
             env={
                 **os.environ,
                 **(env_override if env_override else {}),
@@ -131,9 +135,18 @@ class CondaAction(Action):
 repos = [
     (
         GitRepo(
+            name="exoplanet",
             url="https://github.com/exoplanet-dev/exoplanet.git",
-            stop_commit="a61076b173a32fc90f286176dc5f194395854e02",
-            start_commit="de30d91597ac39c0a81bbabefdefc7ce4b9dae5a",
+            all_commits=[
+                "470b9be8911aa9b344d1e8f0c644d36b57e27c6d",
+                "c7d992bd58ed9033e2fd2d98ccaea9554b1b299e",
+                "a86b5c194479d7c6bee2e8122bf48440c4710d77",
+                "d30a7b8e1ede786cec3032769632f7321b73bebf",
+                "f4ef1a577c2d62c9c72bf06124caf4d3aefaba44",
+                "6cf0bcf067d0651fad4043d1307532b954b99d5e",
+                "57d5787c70cfcb2c92d0825cb2559476dad1b498",
+                "10b4ec3a99f07f35ffa9a7abfef083399af2a2d2",
+            ],
         ),
         CondaAction(
             name="exoplanet",
diff --git a/test_cache/test_cache/test_cache.py b/test_cache/test_cache/test_cache.py
index cbb308e13cca57148388d9692dd82f0e00d88cd5..2ac305b63c7c10261f9e91f05a849c4d934f6f4e 100644
--- a/test_cache/test_cache/test_cache.py
+++ b/test_cache/test_cache/test_cache.py
@@ -1,9 +1,11 @@
 import asyncio
+import collections
 import datetime
-from dataclasses import dataclass
+from dataclasses import dataclass, field
+import json
 from pathlib import Path
-from typing import Callable, List, Tuple, TypeVar
-from tqdm import tqdm
+import shutil
+from typing import Callable, List, Tuple, TypeVar, Mapping
 
 from .data import Action, Repo, repos
 
@@ -17,7 +19,25 @@ ROOT = Path(__file__).parent.parent
 Return = TypeVar("Return")
 
 
-charmonium.cache.DEFAULT_MEMOIZED_GROUP = MemoizedGroup(obj_store=DirObjStore(ROOT / ".cache/functions"))
+group = MemoizedGroup(obj_store=DirObjStore(ROOT / ".cache/functions"))
+
+import logging
+hash_logger = logging.getLogger("charmonium.cache.determ_hash")
+hash_logger.setLevel(logging.DEBUG)
+hash_logger.addHandler(logging.FileHandler("/tmp/hash.log"))
+hash_logger.propagate = False
+
+@dataclass
+class CachedFunctionPerf:
+    outer_function: float = 0
+    hash: float = 0
+    misses: int = 0
+    hits: int = 0
+    inner_function: float = 0
+    serialize: float = 0
+    obj_store: float = 0
+    obj_load: float = 0
+    deserialize: float = 0
 
 @dataclass
 class CommitResult:
@@ -26,8 +46,13 @@ class CommitResult:
     orig_success: bool
     memo_time: float
     memo_success: bool
+    memo2_time: float
+    memo2_success: bool
     stdout_match: bool
-
+    stdout_match2: bool
+    index_read: float = 0
+    index_write: float = 0
+    functions: Mapping[str, CachedFunctionPerf] = field(default_factory=lambda: collections.defaultdict(CachedFunctionPerf))
 
 def timeit(thunk: Callable[[], Return]) -> Tuple[datetime.timedelta, Return]:
     start = datetime.datetime.now()
@@ -36,38 +61,63 @@ def timeit(thunk: Callable[[], Return]) -> Tuple[datetime.timedelta, Return]:
     return (stop - start, ret)
 
 
-@memoize()
+@memoize(group=group)
 def get_commit_result(commit: str, repo: Repo, action: Action) -> CommitResult:
+    from tqdm import tqdm
     repo.checkout(commit)
     log_path = Path("/tmp/logs")
-    log_path.mkdir(exist_ok=True)
+    if log_path.exists():
+        shutil.rmtree(log_path)
+    log_path.mkdir()
     orig_time, (orig_stdout, orig_success) = timeit(
+        lambda: action.run(repo=repo)
+    )
+    memo_time, (memo_stdout, memo_success) = timeit(
         lambda: action.run(repo=repo, env_override={
             "CHARMONIUM_CACHE": "enable",
             "CHARMONIUM_CACHE_PERF_LOG": str(log_path / "perf.log"),
-            "CHARMONIUM_CACHE_SIZE_LOG": str(log_path / "size.log"),
         })
     )
-    memo_time, (memo_stdout, memo_success) = timeit(
-        lambda: action.run(repo=repo)
+    memo2_time, (memo2_stdout, memo2_success) = timeit(
+        lambda: action.run(repo=repo, env_override={
+            "CHARMONIUM_CACHE": "enable",
+            "CHARMONIUM_CACHE_PERF_LOG": str(log_path / "perf.log"),
+        })
     )
-    return CommitResult(
+    result = CommitResult(
         commit=commit,
         orig_time=orig_time.total_seconds(),
         orig_success=orig_success,
         memo_time=memo_time.total_seconds(),
         memo_success=memo_success,
+        memo2_time=memo_time.total_seconds(),
+        memo2_success=memo_success,
         stdout_match=orig_stdout == memo_stdout,
+        stdout_match2=orig_stdout == memo2_stdout,
     )
+    with (log_path / "perf.log").open() as perf_log:
+        for line in perf_log:
+            record = json.loads(line)
+            if "name" in record:
+                result.functions[record["name"]].__dict__[record["event"]] += record["duration"]
+                if record["event"] == "outer_function":
+                    result.functions[record["name"]].hits += int(record["hit"])
+                    result.functions[record["name"]].misses += int(not record["hit"])
+            else:
+                result.__dict__[record["event"]] += record["duration"]
+
+    return result
 
 
-@memoize()
+@memoize(group=group)
 def get_repo_result(repo: Repo, action: Action) -> List[CommitResult]:
+    from tqdm import tqdm
     commits = repo.get_commits()
     return [get_commit_result(commit, repo, action) for commit in tqdm(commits, total=len(commits), desc="Testing commit")]
 
 
-async def test_cache() -> List[List[CommitResult]]:
+async def test_cache() -> List[Tuple[Repo, List[CommitResult]]]:
+    from tqdm import tqdm
     await asyncio.gather(*[repo.setup() for repo, _ in tqdm(repos, total=len(repos), desc="Repo setup")])
     await asyncio.gather(*[action.setup(repo) for repo, action in tqdm(repos, total=len(repos), desc="Action setup")])
-    return [get_repo_result(repo, action) for repo, action in tqdm(repos, total=len(repos), desc="Testing repo")]
+    return [(repo, get_repo_result(repo, action)) for repo, action in tqdm(repos, total=len(repos), desc="Testing repo")]