Potential @anvil.server.call optimization

This suggestion was sparked by this topic.

As I understand it, to satisfy each @anvil.server.call, server-side Anvil loads the app’s entire set of Server Modules as a single, unified Python program.

For a “large” program, with scores of Server Modules, and the vast majority of the loaded code being irrelevant to the call (unreachable from the called function), this seems rather wasteful. If the called function is defined in module X, then only module X (and its imports) need be executed. The 99 other (unreachable) modules can be skipped, saving time and memory.

Yes, on a higher-tier Plan, for an app, one can make each session’s Server program persistent in server memory (see " Persistent Server Modules" under https://anvil.works/docs/server#advanced). This would alleviate load time, at the cost of occupying server memory for a much longer period. (Among other tradeoffs.)

For everyone else, this looks like an opportunity for optimization.

Suppose the app hasn’t changed since the last time a @anvil.server.callable function was run. Then the app’s set of @anvil.server.callable points has not changed, either. If this set is saved, for Anvil to consult on the next call, perhaps it could avoid loading the uninvolved modules.

Now, I don’t know if the overhead exceeds the savings. I may be missing something subtle (or obvious!). If there are unusual consequences, that we developers can program around, then it may be something that a developer should opt-in to, on an app-by-app basis.

But as the author of a fairly large app, I’d feel more comfortable if my own app was not taking server-side resources that other apps (including other invocations of my own apps!) could be using instead. If I have 120 server-side modules, but most of my server calls need only two of them loaded…

Edit: Revised trailing example to be more realistic.

5 Likes

I roughly profiled my server calls and managed to speed up my also rather large app by doing heavy imports INSIDE functions, so they don’t get called every time I do an unrelated call. For instance, calls that rely on pandas for data wrangling import pandas within the function. Ideal? No. Does it work? Yes. I managed to trim my calls from 1500 ms to ~ 200 ms!

4 Likes