Server Call difference between two Anvil apps to return short dict is 2+ seconds

I am having issues with loading speed on a particular app.

I have hard coded a dict into the server module. Then the form calls the server call on load (init) to test this and confirm the speed issues logging the time difference with datetime.datetime.now().

Code and blank material design form is identical in both apps as follows:

Client form, init:

s = datetime.datetime.now()
# Any code you write here will run when the form opens.
r = anvil.server.call('sc')
print('dict')
print(r)

print('time')
print(datetime.datetime.now()-s)

Server module:

import anvil.server

@anvil.server.callable
def sc():
  return {'RowID': '[45818,49696544]', 'Name': 'Van (6-Sp)', 'ProjectName': '305554 - Humber bridge', 'Day': True, 'JobInfo': True, 'PackedCheck': True, 'Checked': True, 'Problem': False, 'KeySafe': None, 'RAMS': ''},` 

Empty new anvil project: 0:00:00.280735
Existing project with multiple forms and server modules and tables: 0:00:02.578720

What could be causing the 2+ seconds difference?

1 Like

Hi @stu, this might be the case

Does the below code fix the issue?

s = datetime.datetime.now()
# Any code you write here will run when the form opens.
r = anvil.server.call('sc')
print('time')
print(datetime.datetime.now()-s)

print('dict')
print(r)

Thanks Tony,

It does not alter the speed by much (2+ seconds longer in my existing app over the new project test):

Existing app:
0:00:02.618260

Test app:
0:00:00.172315

The issue seems to be with the existing app only.

Hi there,

My guess is that there are some slow imports in the Server Modules of your existing app. These have to be imported every time you do anvil.server.call, hence the difference between the apps. On the Business Plan and above, you can avoid this problem by enabling ā€œKeep the Server Runningā€.

I hope that helps!

Additionally, you may be using different versions of Python, server-side.

Thanks Ian,

To clarify, and to help me locate the problem; is it:

a) anvil.server.call() imports only referenced import(s) within that specific module?

or;

b) anvil.server.call() imports every referenced import within all server modules?

Hi @stu,

It’s (b) - until Anvil has loaded all your Server Modules, it doesn’t know where the function you’re trying to call is defined! That’s why persistent server modules can make such a big difference.

This looks like an opportunity for optimization. If 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?

Thanks @daviesian, I understand where you are coming from and I will now look at the entire imports of my project to optimise/remove unused imports.

Interesting point @p.colbert, is this something that would have to be done by the Anvil Team or is this achievable within a project? If so, can you point me in the direction as to how to save the @anvil.server.callable points?

Now that you ask, @stu, I can roughly imagine a way to do it ourselves, but it greatly distorts the way we would have to edit and save (and debug?) our server-side code … I’m not sure it would work, and I’m not sure the overhead would net any savings.

So as things stand, the Anvil Team would have to do it.

In fact, I was getting ready to post it in Feature Requests.

Note: the Anvil Team may have already tried it, and run into one ā€œgotchaā€ or another. Or, perhaps, the overhead outweighed the savings, on average. At this writing, there’s no way to know.

I’ve Liked your Feature Request @p.colbert.

In terms of finding the slow imports, am I correct in saying that multiple server modules importing the same set of imports (i.e. the standard anvil imports) only import that once, regardless of number server modules that import them?

i.e. in my optimisation here, can I safely ignore the following in all of my modules or do the multiple imports cause loading issues? :

import anvil.users
import anvil.email
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables
import anvil.google.auth, anvil.google.drive, anvil.google.mail
from anvil.google.drive import app_files
import anvil.server`

Yes, you can ignore them. Python caches all imported modules, as it loads them. So any other modules you import, will only load once, too.

They’re discarded when the (server-side) app closes, which typically happens shortly after the server call returns.

It may take some experimentation to see which import(s) are the slow ones. You may want to do that in a separate (throw-away) app.

1 Like

As you suggested, I created a list of all imports, removed duplicates and added them to the speed test app I had made to understand this issue.

Main culprit immediately found: from plotly import figure_factory as ff
plotly figure_factory causes a minimum 1.5 second slowdown on every server call.

Thanks for the help!

1 Like

This suggests collecting a list of imports and their timings, so that developers can be forewarned.

Timings may vary between Python 2 and 3.

Yes for sure that would be useful.

If it helps anyone, here’s a print out of my imports (as logged in Python 3 server code after import of anvil.server and datetime. Time starts before the first import and they are accumulative timings)

import anvil.users
0:00:00.000551
import anvil.email
0:00:00.000926
import anvil.tables
0:00:00.001307
import anvil.tables.query
0:00:00.001777
import anvil.tables
0:00:00.001810
import anvil.google.auth
0:00:00.003339
import anvil.google.drive
0:00:00.003390
import anvil.http
0:00:00.003782
import anvil.media
0:00:00.008764
import anvil.pdf
0:00:00.008793
import anvil.tz
0:00:00.009042
import anvil.pdf renderer
0:00:00.009071
import time
0:00:00.009095
import random
0:00:00.009116
import math/dataframes
0:00:00.632578
import email mimetypes
0:00:00.638363
import os
0:00:00.638395
import requests plus pdf packages
0:00:00.647813
import plotly ff
0:00:01.825181
1 Like

It’s really helpful, Would you mind sharing how do you measure the time of the import?

imported everything within the ā€˜sc’ server call with a print of import name and datetime.now() as below:

2 Likes

What about putting the slow import statements inside the function(s) that need them rather than at the top of the script.

This way you don’t import it with every server call - just when certain functions execute which use that particular module.

6 Likes

Yes, thats exactly what I have done with the plotly import that was causing the big slowdown.

3 Likes

I found your suggestion very useful today. I’m linking it to this related topic: Suggestions to optimize performance to help others find it.