-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Start implementing caching for unchanging code blocks #1
Comments
In order for this to be useful, we need to restore the state of variables after each code chunk. (or equivalently restore the a snapshot of the kernel as it was at the end of each code chunk) Relevant work: https://stackoverflow.com/questions/633127/viewing-all-defined-variables?noredirect=1&lq=1 |
Here's Brandon Willard talking about the approaches he was thinking about: |
@fgregg, looks like you're going down the same rabbit hole that I revisit every year! A lot of the possible (and worthwhile) functionality should exist in its own project. For instance, the type of caching that invalidates entries based on a variable/call dependency graph, efficient incremental bytecode storage and updating (good for interactive sessions), automatic caching based on instrumentation/runtimes, etc. As we discussed, there are necessarily low-level language implementation details, but a considerable amount of the logic surrounding those functions can be abstracted at a level sufficient for orchestration in Python (perhaps within the Jupyter framework). If you're interested, we could probably knock out a full fledged example for Python sessions/kernels and start the generalization from there. |
That sound great, @brandonwillard. Do you have a suggestion about how we should proceed? |
I was wondering if you are aware of the dill package and that it offers the ability to save a session. From https://pypi.python.org/pypi/dill:
I have not used this feature, but I have used other serialization features and I think it's a high quality package and the dev is very responsive. If storing the entire session is agreeable, then it's a pretty simple algorithm: restore session before chunk that has changed; evaluate chunk; save session after chunk. If any change in the saved session, re-evaluate next chunk. Premature optimization etc etc |
Thanks for sharing that, @piccolbo. It's helped me see that serializing and restoring sessions is not something that I want. Here's a common pattern I have.
I don't really want to serialize the connection or the cursor (neither of which can really be serialized anyway). Even if I could sanely serialize and hydrate the connections, I wouldn't want the rehydration of |
@piccolbo, yes, I was originally using dill's session pickling in my Pweave caching branch (it's commented-out in that commit, though), but — as @fgregg said — it was overkill. That's also what motivated my thinking about incremental caching. @fgregg, regarding first steps, here's a small example of AST-based "assign" statement caching. I left out any considerations for out-of-scope variable assigns within functions and classes, as well as variable-to-block dependencies (the kind that would invalidate caches for dependent blocks), etc. This is the kind of thing that might work for org-mode and Pweave, but I think it should exist closer to the Jupyter level (e.g. in a client or kernel). Anyway, from here, we should figure out exactly where this sort of logic should exist and start considering which other languages to support and whether or not we can easily tease-out assign statements. Either that or start on the aforementioned missing features. One idea I kept having involved Pygments. It has a wide array of lexers and they might be useful for obtaining assigns in a similar, and highly generic, way. That approach is extremely limited, but somewhat promising for a broad, slightly-less-than-naive caching similar to the example given here. Tools like this and, say, Antlr are a nice way to keep the work going in one language (e.g. Python) while covering more languages. Plus, much of what we've discussed doesn't directly rely on bytecode compilation and/or execution; for instance, the caller's frame and |
Thanks for sharing that @brandonwillard. It looks like you are hoping to implement a dependency graph to invalidate code the cached if a dependency changes. That's a very neat idea, but that would seem to assume that none of the dependencies were connections to external resources. In the example code, I posted above,
|
What I think is reasonable to implement in caching logic currently stops short at the bytecode level. Nonetheless, there are worthwhile, albeit less-than-automatic, ways around specific problems — like the one you mention — and I imagine most would involve some intervention by the user (e.g. specifying a form to evaluate that would determine whether or not remote content was changed). If you wanted to get really fancy, though, you could add caching logic with awareness specific to libraries like |
Okee doke. I think I'm going to start by implementing what knitr does by default. In pseudocode: old_globals = globals().copy()
for chunk in chunks:
if chunk in cache:
chunk.results = cache[chunk].results
globals().update(cache[chunk].objects)
else:
result = eval(chunk)
cache[chunk].results = result
cache[chunk].objects = globals() - old_globals # what's been changed in globals by this code chunk
old_globals = globals().copy() I think there's better things we can do in the future, but this seems pretty simple and from working with knitr, it seems acceptable. |
With source string hashing chunk validation, though, right? |
Yes. |
By the way, a great next step could involve AST-based hashing for cache and/or block validation, instead of string hashing. That way, inconsequential string changes in blocks wouldn't affect the cache (e.g. white spaces, comments, reorderings, var name changes, etc.) |
I agree! Although, from working with knitr, I've often found is useful to bust the cache by adding in meaningless white space. Haha. |
Ha, yeah, I've had to do that, as well. However, I think this functionality could be provided by a [temporary] chunk option. You know, this whole idea of AST-based caching really makes me wish I was working with JVM languages again. Seems like one could cover a whole lot of ground with a unified bytecode like Java's. |
No description provided.
The text was updated successfully, but these errors were encountered: