On my machine, static asset collection takes 57s to run. Here are the profiling stats, sorted by decreasing cumulative time, for the collectstatic command:
$ python -m cProfile -o static.prof ./manage.py lms --settings=tutor.production collectstatic --ignore "fixtures" --ignore "karma_*.js" --ignore "spec" --ignore "spec_helpers" --ignore "spec-helpers" --ignore "xmodule_js" --ignore "geoip" --ignore "sass" --noinput
I ran the following script in the static.prof
file:
import pstats
import io
s = io.StringIO()
ps = pstats.Stats("static.prof", stream=s).sort_stats(pstats.SortKey.CUMULATIVE)
s.seek(0)
open("static.prof.stats", "w).write(s.read())
Which yielded the following static.prof.stats file:
Sun May 2 08:49:09 2021 static.prof
12907115 function calls (12671003 primitive calls) in 57.219 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
5248/1 0.040 0.000 57.236 57.236 {built-in method builtins.exec}
1 0.000 0.000 57.236 57.236 ./manage.py:2(<module>)
1 0.000 0.000 51.817 51.817 /openedx/venv/lib/python3.8/site-packages/django/core/management/__init__.py:378(execute_from_command_line)
1 0.000 0.000 51.817 51.817 /openedx/venv/lib/python3.8/site-packages/django/core/management/__init__.py:301(execute)
1 0.000 0.000 51.787 51.787 /openedx/venv/lib/python3.8/site-packages/django/core/management/base.py:306(run_from_argv)
1 0.000 0.000 51.786 51.786 /openedx/venv/lib/python3.8/site-packages/django/core/management/base.py:342(execute)
1 0.000 0.000 51.785 51.785 /openedx/venv/lib/python3.8/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py:148(handle)
1 0.024 0.024 51.779 51.779 /openedx/venv/lib/python3.8/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py:86(collect)
5958 0.586 0.000 49.943 0.008 /openedx/venv/lib/python3.8/site-packages/require/storage.py:74(post_process)
1 0.000 0.000 38.758 38.758 /openedx/venv/lib/python3.8/site-packages/require/storage.py:35(run_optimizer)
1 0.000 0.000 38.757 38.757 /opt/pyenv/versions/3.8.6/lib/python3.8/subprocess.py:332(call)
120 0.001 0.000 38.750 0.323 /opt/pyenv/versions/3.8.6/lib/python3.8/subprocess.py:1074(wait)
120 0.001 0.000 38.750 0.323 /opt/pyenv/versions/3.8.6/lib/python3.8/subprocess.py:1772(_wait)
80 0.000 0.000 38.748 0.484 /opt/pyenv/versions/3.8.6/lib/python3.8/subprocess.py:1759(_try_wait)
80 38.748 0.484 38.748 0.484 {built-in method posix.waitpid}
5933 0.003 0.000 8.375 0.001 ./openedx/core/djangoapps/theming/storage.py:265(post_process)
5914 0.004 0.000 8.284 0.001 /openedx/venv/lib/python3.8/site-packages/pipeline/storage.py:20(post_process)
5828/1157 0.050 0.000 5.429 0.005 <frozen importlib._bootstrap>:986(_find_and_load)
5501/911 0.020 0.000 5.409 0.006 <frozen importlib._bootstrap>:956(_find_and_load_unlocked)
4609/517 0.019 0.000 5.336 0.010 <frozen importlib._bootstrap>:650(_load_unlocked)
4432/514 0.011 0.000 5.314 0.010 <frozen importlib._bootstrap_external>:777(exec_module)
5757/481 0.003 0.000 5.295 0.011 <frozen importlib._bootstrap>:211(_call_with_frames_removed)
57 0.001 0.000 4.726 0.083 /openedx/venv/lib/python3.8/site-packages/pipeline/packager.py:107(pack)
19 0.000 0.000 4.683 0.246 /openedx/venv/lib/python3.8/site-packages/pipeline/packager.py:121(pack_javascripts)
19 0.000 0.000 4.577 0.241 /openedx/venv/lib/python3.8/site-packages/pipeline/compressors/__init__.py:55(compress_js)
19 0.001 0.000 4.548 0.239 /openedx/venv/lib/python3.8/site-packages/pipeline/compressors/uglifyjs.py:6(compress_js)
19 0.001 0.000 4.544 0.239 /openedx/venv/lib/python3.8/site-packages/pipeline/compressors/__init__.py:235(execute_command)
39 0.001 0.000 4.348 0.111 /opt/pyenv/versions/3.8.6/lib/python3.8/subprocess.py:980(communicate)
38 0.007 0.000 4.347 0.114 /opt/pyenv/versions/3.8.6/lib/python3.8/subprocess.py:1813(_communicate)
1198 0.003 0.000 4.325 0.004 /opt/pyenv/versions/3.8.6/lib/python3.8/selectors.py:402(select)
1198 4.321 0.004 4.321 0.004 {method 'poll' of 'select.poll' objects}
2 0.000 0.000 4.313 2.156 /openedx/venv/lib/python3.8/site-packages/django/__init__.py:8(setup)
1 0.000 0.000 4.305 4.305 ./lms/startup.py:13(run)
2 0.001 0.001 4.302 2.151 /openedx/venv/lib/python3.8/site-packages/django/apps/registry.py:61(populate)
1367/1073 0.002 0.000 4.198 0.004 /opt/pyenv/versions/3.8.6/lib/python3.8/importlib/__init__.py:109(import_module)
1512/1073 0.001 0.000 4.196 0.004 <frozen importlib._bootstrap>:1002(_gcd_import)
1996/992 0.005 0.000 3.599 0.004 {built-in method builtins.__import__}
5876 0.003 0.000 3.547 0.001 /openedx/venv/lib/python3.8/site-packages/django/contrib/staticfiles/storage.py:401(post_process)
5876 0.003 0.000 3.537 0.001 /openedx/venv/lib/python3.8/site-packages/django/contrib/staticfiles/storage.py:209(post_process)
5878 0.085 0.000 3.520 0.001 /openedx/venv/lib/python3.8/site-packages/django/contrib/staticfiles/storage.py:257(_post_process)
8148/4881 0.009 0.000 2.806 0.001 <frozen importlib._bootstrap>:1017(_handle_fromlist)
179 0.001 0.000 1.913 0.011 /openedx/venv/lib/python3.8/site-packages/django/apps/config.py:204(import_models)
3288 0.752 0.000 1.703 0.001 {method 'sub' of 're.Pattern' objects}
179 0.001 0.000 1.418 0.008 /openedx/venv/lib/python3.8/site-packages/django/apps/config.py:81(create)
11145/10332 0.142 0.000 1.130 0.000 {built-in method builtins.__build_class__}
49526 0.030 0.000 0.981 0.000 /openedx/venv/lib/python3.8/site-packages/django/core/files/storage.py:322(path)
39 0.000 0.000 0.968 0.025 /openedx/venv/lib/python3.8/site-packages/Cryptodome/Util/_raw_api.py:86(load_lib)
49952 0.105 0.000 0.944 0.000 /openedx/venv/lib/python3.8/site-packages/django/utils/_os.py:24(safe_join)
19 0.001 0.000 0.936 0.049 /openedx/venv/lib/python3.8/site-packages/Cryptodome/Util/_raw_api.py:278(load_pycryptodome_raw_lib)
5880 0.030 0.000 0.915 0.000 ./openedx/core/djangoapps/theming/storage.py:189(converter)
5451 0.004 0.000 0.896 0.000 /openedx/venv/lib/python3.8/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py:334(copy_file)
5451 0.248 0.000 0.892 0.000 /openedx/venv/lib/python3.8/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py:245(delete_file)
6579 0.014 0.000 0.838 0.000 ./openedx/core/storage.py:22(hashed_name)
39 0.001 0.000 0.836 0.021 /openedx/venv/lib/python3.8/site-packages/cffi/api.py:137(dlopen)
21 0.002 0.000 0.836 0.040 /opt/pyenv/versions/3.8.6/lib/python3.8/ctypes/util.py:309(find_library)
39 0.001 0.000 0.835 0.021 /openedx/venv/lib/python3.8/site-packages/cffi/api.py:830(_make_ffi_library)
39 0.001 0.000 0.834 0.021 /openedx/venv/lib/python3.8/site-packages/cffi/api.py:804(_load_backend_lib)
6579 0.048 0.000 0.823 0.000 /openedx/venv/lib/python3.8/site-packages/django/contrib/staticfiles/storage.py:84(hashed_name)
80 0.004 0.000 0.822 0.010 /opt/pyenv/versions/3.8.6/lib/python3.8/subprocess.py:732(__init__)
79 0.000 0.000 0.821 0.010 /openedx/venv/lib/python3.8/site-packages/pkg_resources/__init__.py:2445(resolve)
4842 0.009 0.000 0.817 0.000 ./openedx/core/djangoapps/theming/storage.py:169(_url)
80 0.010 0.000 0.814 0.010 /opt/pyenv/versions/3.8.6/lib/python3.8/subprocess.py:1550(_execute_child)
1 0.000 0.000 0.767 0.767 /openedx/venv/lib/python3.8/site-packages/require/storage.py:57(__exit__)
2 0.000 0.000 0.767 0.384 /opt/pyenv/versions/3.8.6/lib/python3.8/shutil.py:679(rmtree)
2752/2 0.115 0.000 0.767 0.383 /opt/pyenv/versions/3.8.6/lib/python3.8/shutil.py:622(_rmtree_safe_fd)
20146 0.760 0.000 0.760 0.000 {method 'update' of '_hashlib.HASH' objects}
2 0.000 0.000 0.759 0.379 /openedx/venv/lib/python3.8/site-packages/stevedore/extension.py:92(__init__)
2 0.000 0.000 0.759 0.379 /openedx/venv/lib/python3.8/site-packages/stevedore/extension.py:185(_load_plugins)
28 0.000 0.000 0.738 0.026 /openedx/venv/lib/python3.8/site-packages/stevedore/extension.py:216(_load_one_plugin)
1 0.000 0.000 0.696 0.696 ./lms/djangoapps/bulk_email/models.py:1(<module>)
102380 0.081 0.000 0.693 0.000 /opt/pyenv/versions/3.8.6/lib/python3.8/posixpath.py:372(abspath)
1 0.000 0.000 0.688 0.688 ./openedx/core/djangoapps/course_groups/cohorts.py:1(<module>)
1 0.000 0.000 0.687 0.687 ./lms/djangoapps/courseware/courses.py:1(<module>)
1 0.000 0.000 0.675 0.675 /openedx/venv/lib/python3.8/site-packages/edx_proctoring/apps.py:113(ready)
1 0.000 0.000 0.670 0.670 /openedx/venv/lib/python3.8/site-packages/edx_proctoring/backends/software_secure.py:1(<module>)
4432 0.039 0.000 0.642 0.000 <frozen importlib._bootstrap_external>:849(get_code)
31752 0.033 0.000 0.626 0.000 /openedx/venv/lib/python3.8/site-packages/django/contrib/staticfiles/storage.py:42(path)
1 0.000 0.000 0.618 0.618 /openedx/venv/lib/python3.8/site-packages/Cryptodome/Cipher/__init__.py:25(<module>)
6471 0.021 0.000 0.607 0.000 /openedx/venv/lib/python3.8/site-packages/django/contrib/staticfiles/storage.py:73(file_hash)
1 0.000 0.000 0.601 0.601 /openedx/venv/src/edx-jsme/edx_jsme/__init__.py:1(<module>)
2682 0.002 0.000 0.587 0.000 /openedx/venv/lib/python3.8/site-packages/django/conf/__init__.py:76(__getattr__)
1 0.000 0.000 0.583 0.583 ./lms/startup.py:1(<module>)
1 0.000 0.000 0.583 0.583 /openedx/venv/lib/python3.8/site-packages/django/conf/__init__.py:51(_setup)
1 0.000 0.000 0.583 0.583 /openedx/venv/lib/python3.8/site-packages/django/conf/__init__.py:148(__init__)
1 0.000 0.000 0.580 0.580 ./lms/envs/tutor/production.py:2(<module>)
1 0.000 0.000 0.580 0.580 ./lms/envs/production.py:1(<module>)
895 0.001 0.000 0.540 0.001 /openedx/venv/lib/python3.8/site-packages/django/contrib/staticfiles/finders.py:281(get_finders)
5 0.000 0.000 0.539 0.108 /openedx/venv/lib/python3.8/site-packages/django/contrib/staticfiles/finders.py:286(get_finder)
26176 0.521 0.000 0.528 0.000 {built-in method io.open}
18057 0.021 0.000 0.522 0.000 /openedx/venv/lib/python3.8/site-packages/django/core/files/storage.py:309(exists)
1 0.000 0.000 0.521 0.521 ./openedx/core/lib/xblock_pipeline/finder.py:115(__init__)
1 0.000 0.000 0.521 0.521 ./openedx/core/lib/xblock_pipeline/finder.py:128(<setcomp>)
52 0.000 0.000 0.521 0.010 /openedx/venv/lib/python3.8/site-packages/xblock/plugin.py:119(load_classes)
51 0.000 0.000 0.520 0.010 /openedx/venv/lib/python3.8/site-packages/xblock/plugin.py:64(_load_class_entry_point)
51 0.000 0.000 0.520 0.010 /openedx/venv/lib/python3.8/site-packages/pkg_resources/__init__.py:2430(load)
107798 0.349 0.000 0.515 0.000 /opt/pyenv/versions/3.8.6/lib/python3.8/posixpath.py:334(normpath)
1 0.000 0.000 0.498 0.498 /openedx/edx-platform/common/lib/capa/capa/inputtypes.py:5(<module>)
11989 0.007 0.000 0.495 0.000 /openedx/venv/lib/python3.8/site-packages/django/core/files/storage.py:34(open)
11989 0.019 0.000 0.488 0.000 /openedx/venv/lib/python3.8/site-packages/django/core/files/storage.py:223(_open)
1 0.049 0.049 0.484 0.484 /openedx/venv/lib/python3.8/site-packages/chem/chemcalc.py:1(<module>)
4842 0.020 0.000 0.477 0.000 /openedx/venv/lib/python3.8/site-packages/django/contrib/staticfiles/storage.py:118(_url)
20461 0.472 0.000 0.472 0.000 {method 'read' of '_io.BufferedReader' objects}
9054 0.019 0.000 0.468 0.000 /opt/pyenv/versions/3.8.6/lib/python3.8/re.py:289(_compile)
5840 0.053 0.000 0.452 0.000 <frozen importlib._bootstrap>:890(_find_spec)
10915 0.443 0.000 0.443 0.000 {built-in method posix.unlink}
460/409 0.020 0.000 0.441 0.001 /openedx/venv/lib/python3.8/site-packages/django/db/models/base.py:69(__new__)
51 0.001 0.000 0.436 0.009 /openedx/venv/lib/python3.8/site-packages/pkg_resources/__init__.py:2455(require)
1077 0.006 0.000 0.435 0.000 /opt/pyenv/versions/3.8.6/lib/python3.8/sre_compile.py:759(compile)
52 0.006 0.000 0.431 0.008 /openedx/venv/lib/python3.8/site-packages/pkg_resources/__init__.py:715(resolve)
1 0.000 0.000 0.427 0.427 /openedx/venv/lib/python3.8/site-packages/nltk/__init__.py:9(<module>)
258 0.426 0.002 0.426 0.002 {built-in method posix.read}
1 0.000 0.000 0.407 0.407 ./lms/djangoapps/courseware/access.py:1(<module>)
1771 0.001 0.000 0.405 0.000 /opt/pyenv/versions/3.8.6/lib/python3.8/re.py:250(compile)
77109 0.399 0.000 0.399 0.000 {built-in method posix.stat}
905 0.001 0.000 0.396 0.000 /openedx/venv/lib/python3.8/site-packages/pkg_resources/__init__.py:2734(requires)
1 0.000 0.000 0.394 0.394 ./openedx/features/course_duration_limits/access.py:1(<module>)
828 0.001 0.000 0.387 0.000 /openedx/venv/lib/python3.8/site-packages/pkg_resources/__init__.py:3018(_dep_map)
89 0.001 0.000 0.386 0.004 /openedx/venv/lib/python3.8/site-packages/pkg_resources/__init__.py:3026(_compute_dependencies)
1 0.000 0.000 0.379 0.379 ./lms/djangoapps/courseware/utils.py:1(<module>)
1 0.000 0.000 0.378 0.378 ./lms/djangoapps/commerce/utils.py:1(<module>)
1 0.000 0.000 0.375 0.375 ./openedx/core/djangoapps/commerce/utils.py:1(<module>)
1 0.000 0.000 0.372 0.372 ./openedx/core/djangoapps/oauth_dispatch/jwt.py:1(<module>)
1 0.000 0.000 0.371 0.371 /openedx/venv/lib/python3.8/site-packages/edx_rbac/utils.py:1(<module>)
1 0.000 0.000 0.370 0.370 /openedx/venv/lib/python3.8/site-packages/edx_rest_framework_extensions/auth/jwt/authentication.py:1(<module>)
1 0.000 0.000 0.368 0.368 /openedx/venv/lib/python3.8/site-packages/edx_rest_framework_extensions/auth/jwt/decoder.py:1(<module>)
27909/5766 0.020 0.000 0.363 0.000 /openedx/venv/lib/python3.8/site-packages/django/contrib/staticfiles/utils.py:16(get_files)
...
We can see that most of the time is spent in a single subprocess.call. To understand what command is using most of the time, I modified the subprocess.call function to print its arguments. Here they are:
['node', '/openedx/venv/lib/python3.8/site-packages/require/resources/r.js', '-o', '/tmp/tmpo_wj6w66/lms/js/build.js', 'dir=/tmp/tmp482vi18h', 'appDir=/tmp/tmpo_wj6w66', 'baseUrl=./'],)'
38.7 out of 57 seconds are spent running that r.js script, which comes from here: GitHub - requirejs/r.js: Runs RequireJS in Node and Rhino, and used to run the RequireJS optimizer
r.js is being called by the OptimizedFilesMixin
static storage backend, from django-require. We use this backend because openedx.core.storage.ProductionStorage
inherits from OptimizedFilesMixin
and we have STATICFILES_STORAGE = 'openedx.core.storage.ProductionStorage'
.
I have no idea how to further improve this call to r.js
. There are some recommendations in the require.js docs but I fail to see how they apply to edx-platform: RequireJS Optimizer