Skip to content

Commit 0fcc529

Browse files
committed
Add initial benchmarks: djangocms + flaskblogging + mypy + pylint
1 parent 316ea69 commit 0fcc529

14 files changed

+3994
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,5 @@ dmypy.json
127127

128128
# Pyre type checker
129129
.pyre/
130+
131+
results

benchmarks/djangocms.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
"""
2+
Django-cms test
3+
Sets up a djangocms installation, and hits '/' a number of times.
4+
'/' is not super interesting, but it still exercises a little bit of
5+
functionality; looking at cms/templates/cms/welcome.html, it seems
6+
to do a decent amount of template logic, as well as do some basic
7+
user auth.
8+
We could probably improve the flow though, perhaps by logging in
9+
and browsing around.
10+
"""
11+
12+
import os
13+
import requests
14+
import socket
15+
import subprocess
16+
import sys
17+
import tempfile
18+
import time
19+
import json
20+
21+
def setup():
22+
"""
23+
Set up a djangocms installation.
24+
Runs the initial bootstrapping without the db migration,
25+
so that we can turn off sqlite synchronous and avoid fs time.
26+
Rough testing shows that setting synchronous=OFF is basically
27+
the same performance as running on /dev/shm
28+
"""
29+
30+
subprocess.check_call([exe.replace("python3", "djangocms"), "testsite", "--verbose", "--no-sync"])
31+
32+
with open("testsite/testsite/settings.py", "a") as f:
33+
f.write("""
34+
from django.db.backends.signals import connection_created
35+
def set_no_sychronous(sender, connection, **kwargs):
36+
if connection.vendor == 'sqlite':
37+
cursor = connection.cursor()
38+
cursor.execute('PRAGMA synchronous = OFF;')
39+
40+
connection_created.connect(set_no_sychronous)
41+
""")
42+
start = time.time()
43+
subprocess.check_call([exe, "manage.py", "migrate"], cwd="testsite")
44+
elapsed = time.time() - start
45+
print("%.2fs to initialize db" % (elapsed,))
46+
47+
def waitUntilUp(addr, timeout=10.0):
48+
start = time.time()
49+
while True:
50+
try:
51+
with socket.create_connection(addr) as sock:
52+
return
53+
except ConnectionRefusedError:
54+
if time.time() > start + timeout:
55+
raise Exception("Timeout reached when trying to connect")
56+
time.sleep(0.001)
57+
58+
def runbenchmark(n=800, out_file=None):
59+
p = subprocess.Popen([exe, "manage.py", "runserver", "--noreload"], cwd="testsite", stdout=open("/dev/null", "w"), stderr=subprocess.STDOUT)
60+
try:
61+
waitUntilUp(("127.0.0.1", 8000))
62+
63+
start = time.time()
64+
times = []
65+
for i in range(n):
66+
times.append(time.time())
67+
if i % 100 == 0:
68+
print(i, time.time() - start)
69+
requests.get("http://localhost:8000/").text
70+
times.append(time.time())
71+
elapsed = time.time() - start
72+
print("%.2fs (%.3freq/s)" % (elapsed, n / elapsed))
73+
74+
exitcode = p.poll()
75+
assert exitcode is None, exitcode
76+
77+
if out_file:
78+
json.dump(times, open(out_file, 'w'))
79+
80+
finally:
81+
p.terminate()
82+
83+
if __name__ == "__main__":
84+
exe = sys.executable
85+
# Hack: make sure this file gets run as "python3" so that perf will collate across different processes
86+
if not exe.endswith('3'):
87+
os.execv(exe + '3', [exe + '3'] + sys.argv)
88+
89+
os.environ["PATH"] = os.path.dirname(exe) + ":" + os.environ["PATH"]
90+
91+
"""
92+
Usage:
93+
python djangocms.py
94+
python djangocms.py --setup DIR
95+
python djangocms.py --serve DIR
96+
97+
The first form creates a temporary directory, sets up djangocms in it,
98+
serves out of it, and removes the directory.
99+
The second form sets up a djangocms installation in the given directory.
100+
The third form runs a benchmark out of an already-set-up directory
101+
The second and third forms are useful if you want to benchmark the
102+
initial migration phase separately from the second serving phase.
103+
"""
104+
if "--setup" in sys.argv:
105+
assert len(sys.argv) > 2
106+
dir = sys.argv[-1]
107+
os.makedirs(dir, exist_ok=True)
108+
os.chdir(dir)
109+
setup()
110+
elif "--serve" in sys.argv:
111+
assert len(sys.argv) > 2
112+
os.chdir(sys.argv[-1])
113+
runbenchmark()
114+
else:
115+
n = 800
116+
if len(sys.argv) > 1:
117+
n = int(sys.argv[1])
118+
out_file = None
119+
if len(sys.argv) > 2:
120+
out_file = os.path.abspath(sys.argv[2])
121+
122+
# It might be interesting to put the temporary directory in /dev/shm,
123+
# which makes the initial db migration about 20% faster.
124+
with tempfile.TemporaryDirectory(prefix="djangocms_test_") as d:
125+
os.chdir(d)
126+
127+
setup()
128+
runbenchmark(n, out_file)

benchmarks/djangocms_requirements.txt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
asgiref==3.3.0
2+
certifi==2020.6.20
3+
chardet==3.0.4
4+
dj-database-url==0.5.0
5+
Django==3.0.10
6+
django-classy-tags==2.0.0
7+
django-cms==3.7.4
8+
django-filer==2.0.2
9+
django-formtools==2.2
10+
django-js-asset==1.2.2
11+
django-mptt==0.11.0
12+
django-polymorphic==3.0.0
13+
django-sekizai==2.0.0
14+
django-treebeard==4.3.1
15+
djangocms-admin-style==1.5.0
16+
djangocms-attributes-field==2.0.0
17+
djangocms-bootstrap4==1.6.0
18+
djangocms-file==2.4.0
19+
djangocms-googlemap==1.4.0
20+
djangocms-icon==1.5.0
21+
djangocms-installer==1.2.3
22+
djangocms-link==2.6.1
23+
djangocms-picture==2.4.0
24+
djangocms-snippet==2.3.0
25+
djangocms-style==2.3.0
26+
djangocms-text-ckeditor==3.10.0
27+
djangocms-video==2.3.0
28+
easy-thumbnails==2.7
29+
html5lib==1.1
30+
idna==2.10
31+
Pillow==8.0.0
32+
pytz==2020.1
33+
requests==2.24.0
34+
six==1.15.0
35+
sqlparse==0.4.1
36+
tzlocal==2.1
37+
Unidecode==1.1.1
38+
urllib3==1.25.11
39+
webencodings==0.5.1

benchmarks/flaskblogging.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import json
2+
import os
3+
import requests
4+
import subprocess
5+
import sys
6+
import threading
7+
import time
8+
9+
from djangocms import waitUntilUp
10+
11+
if __name__ == "__main__":
12+
exe = sys.executable
13+
14+
times = []
15+
16+
p = subprocess.Popen([exe, "../data/flaskblogging_serve.py"], stdout=open("/dev/null", "w"), stderr=subprocess.STDOUT, cwd=os.path.dirname(__file__))
17+
try:
18+
waitUntilUp(("127.0.0.1", 8000))
19+
20+
n = 1800
21+
if len(sys.argv) > 1:
22+
n = int(sys.argv[1])
23+
24+
start = time.time()
25+
for i in range(n):
26+
times.append(time.time())
27+
if i % 100 == 0:
28+
print(i, time.time() - start)
29+
requests.get("http://localhost:8000/blog/").text
30+
times.append(time.time())
31+
elapsed = time.time() - start
32+
print("%.2fs (%.3freq/s)" % (elapsed, n / elapsed))
33+
34+
assert p.poll() is None, p.poll()
35+
36+
finally:
37+
p.terminate()
38+
39+
if len(sys.argv) > 2:
40+
json.dump(times, open(sys.argv[2], 'w'))
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
blinker==1.4
2+
certifi==2020.6.20
3+
chardet==3.0.4
4+
click==7.1.2
5+
Flask==1.1.2
6+
Flask-Blogging==1.2.2
7+
Flask-Caching==1.9.0
8+
Flask-FileUpload==0.5.0
9+
Flask-Login==0.5.0
10+
Flask-Principal==0.4.0
11+
Flask-WTF==0.14.3
12+
idna==2.10
13+
itsdangerous==1.1.0
14+
Jinja2==2.11.2
15+
Markdown==3.3.2
16+
MarkupSafe==1.1.1
17+
python-slugify==4.0.1
18+
requests==2.24.0
19+
shortuuid==1.0.1
20+
SQLAlchemy==1.3.20
21+
text-unidecode==1.3
22+
urllib3==1.25.11
23+
Werkzeug==0.16.0
24+
WTForms==2.3.3

benchmarks/mypy_bench.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import json
2+
import os
3+
import sys
4+
import time
5+
6+
"""
7+
I tested it, and it looks like we get the same performance conclusions
8+
when we run on the same file multiple times as if we run on a set of files once.
9+
10+
So for convenience run on a single file multiple times.
11+
"""
12+
13+
if __name__ == "__main__":
14+
from mypy.main import main
15+
16+
n = 20
17+
if len(sys.argv) > 1:
18+
n = int(sys.argv[1])
19+
target = os.path.join(os.path.dirname(__file__), "../data/mypy_target.py")
20+
21+
times = []
22+
devnull = open("/dev/null", "w")
23+
for i in range(n):
24+
times.append(time.time())
25+
print(i)
26+
try:
27+
main(None, devnull, devnull, [target])
28+
except SystemExit:
29+
pass
30+
times.append(time.time())
31+
32+
if len(sys.argv) > 2:
33+
json.dump(times, open(sys.argv[2], 'w'))
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
mypy==0.790
2+
mypy-extensions==0.4.3
3+
typed-ast==1.4.1
4+
typing-extensions==3.7.4.3

benchmarks/pylint_bench.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import json
2+
import os
3+
import subprocess
4+
import sys
5+
import time
6+
7+
from pylint import epylint as lint
8+
from pylint.lint import Run
9+
10+
"""
11+
pylint benchmark
12+
13+
pylint seems to speed up considerably as it progresses, and this
14+
benchmark includes that
15+
"""
16+
17+
if __name__ == "__main__":
18+
def noop(*args, **kw):
19+
pass
20+
class NullReporter:
21+
path_strip_prefix = "/"
22+
def __getattr__(self, attr):
23+
return noop
24+
25+
n = 10
26+
if len(sys.argv) > 1:
27+
n = int(sys.argv[1])
28+
29+
times = []
30+
for i in range(n):
31+
times.append(time.time())
32+
print(i)
33+
Run([os.path.join(os.path.dirname(__file__), "../data/pylint_target/dist.py")], exit=False, reporter=NullReporter())
34+
times.append(time.time())
35+
36+
if len(sys.argv) > 2:
37+
json.dump(times, open(sys.argv[2], 'w'))
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
astroid==2.4.2
2+
isort==5.6.4
3+
lazy-object-proxy==1.4.3
4+
mccabe==0.6.1
5+
pylint==2.6.0
6+
six==1.15.0
7+
toml==0.10.1
8+
wrapt==1.12.1

0 commit comments

Comments
 (0)