1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
|
"""
This benchmark compares the installed library size between msgspec and pydantic
in a Python 3.10 x86 environment.
"""
import io
import zipfile
import requests
def get_latest_noarch_wheel_size(library):
"""Get the total uncompressed size of the latest noarch wheel"""
resp = requests.get(f"https://pypi.org/pypi/{library}/json").json()
version = resp["info"]["version"]
files = {}
for file_info in resp["releases"][version]:
name = file_info["filename"]
url = file_info["url"]
if name.endswith(".whl"):
files[name] = url
if len(files) != 1:
raise ValueError(
f"Expected to find only 1 matching file for {library}, got {list(files)}"
)
url = list(files.values())[0]
resp = requests.get(url)
fil = io.BytesIO(resp.content)
zfil = zipfile.ZipFile(fil)
size = sum(f.file_size for f in zfil.filelist)
return version, size
def get_latest_manylinux_wheel_size(library):
"""Get the total uncompressed size of the latest Python 3.10 manylinux
x86_64 wheel for the library"""
resp = requests.get(f"https://pypi.org/pypi/{library}/json").json()
version = resp["info"]["version"]
files = {}
for file_info in resp["releases"][version]:
name = file_info["filename"]
url = file_info["url"]
if "310" in name and "manylinux_2_17_x86_64" in name and "pp73" not in name:
files[name] = url
if len(files) != 1:
raise ValueError(
f"Expected to find only 1 matching file for {library}, got {list(files)}"
)
url = list(files.values())[0]
resp = requests.get(url)
fil = io.BytesIO(resp.content)
zfil = zipfile.ZipFile(fil)
size = sum(f.file_size for f in zfil.filelist)
return version, size
def main():
msgspec_version, msgspec_size = get_latest_manylinux_wheel_size("msgspec")
pydantic_version, pydantic_size = get_latest_noarch_wheel_size("pydantic")
_, pydantic_core_size = get_latest_manylinux_wheel_size("pydantic-core")
_, typing_extensions_size = get_latest_noarch_wheel_size("typing-extensions")
_, annotated_types_size = get_latest_noarch_wheel_size("annotated-types")
data = [
("msgspec", msgspec_version, msgspec_size),
(
"pydantic",
pydantic_version,
pydantic_size
+ pydantic_core_size
+ typing_extensions_size
+ annotated_types_size,
),
]
data.sort(key=lambda x: x[2])
msgspec_size = next(s for l, _, s in data if l == "msgspec")
columns = ("", "version", "size (MiB)", "vs. msgspec")
rows = [
(
f"**{lib}**",
version,
f"{size / (1024 * 1024):.2f}",
f"{size / msgspec_size:.2f}x",
)
for lib, version, size in data
]
widths = tuple(max(max(map(len, x)), len(c)) for x, c in zip(zip(*rows), columns))
row_template = ("|" + (" %%-%ds |" * len(columns))) % widths
header = row_template % tuple(columns)
bar_underline = "+%s+" % "+".join("=" * (w + 2) for w in widths)
bar = "+%s+" % "+".join("-" * (w + 2) for w in widths)
parts = [bar, header, bar_underline]
for r in rows:
parts.append(row_template % r)
parts.append(bar)
print("\n".join(parts))
if __name__ == "__main__":
main()
|