mirror of
https://github.com/codeflash-ai/codeflash-agent.git
synced 2026-05-04 18:25:19 +00:00
Use attrs fork with deferred inspect import
Point attrs dependency at local fork (KRRT7/attrs perf/defer-inspect-import) which defers the ~12ms inspect import until first class build. Temporary override until upstream merges python-attrs/attrs#1547. Also adds attrs optimization case study data (VM infra, status).
This commit is contained in:
parent
4f206be46b
commit
edfdd231e0
5 changed files with 316 additions and 6 deletions
93
.codeflash/krrt7/python-attrs/attrs/infra/cloud-init.yaml
Normal file
93
.codeflash/krrt7/python-attrs/attrs/infra/cloud-init.yaml
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#cloud-config
|
||||
# Benchmark VM for attrs import time optimization
|
||||
# Minimal setup: Python 3.13, uv, hyperfine, git
|
||||
|
||||
package_update: true
|
||||
|
||||
packages:
|
||||
- git
|
||||
- build-essential
|
||||
- curl
|
||||
- wget
|
||||
|
||||
runcmd:
|
||||
# Install hyperfine
|
||||
- wget -q https://github.com/sharkdp/hyperfine/releases/download/v1.19.0/hyperfine_1.19.0_amd64.deb -O /tmp/hyperfine.deb
|
||||
- dpkg -i /tmp/hyperfine.deb
|
||||
- rm /tmp/hyperfine.deb
|
||||
|
||||
# Install uv for azureuser
|
||||
- su - azureuser -c 'curl -LsSf https://astral.sh/uv/install.sh | sh'
|
||||
|
||||
# Clone attrs fork and set up both branches
|
||||
- su - azureuser -c 'export PATH=$HOME/.local/bin:$PATH && git clone https://github.com/KRRT7/attrs.git ~/attrs'
|
||||
- su - azureuser -c 'export PATH=$HOME/.local/bin:$PATH && cd ~/attrs && uv venv --python 3.13 && uv pip install -e "."'
|
||||
- su - azureuser -c 'export PATH=$HOME/.local/bin:$PATH && cd ~/attrs && uv pip install pytest hypothesis cloudpickle'
|
||||
|
||||
# Write benchmark script
|
||||
- |
|
||||
cat > /home/azureuser/bench.sh << 'BENCHEOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
export PATH=$HOME/.local/bin:$PATH
|
||||
cd ~/attrs
|
||||
|
||||
PYTHON=.venv/bin/python
|
||||
RUNS=100
|
||||
|
||||
echo "=== attrs import time benchmark ==="
|
||||
echo "Python: $($PYTHON --version)"
|
||||
echo "CPU: $(lscpu | grep 'Model name' | sed 's/.*: *//')"
|
||||
echo "Runs: $RUNS per branch"
|
||||
echo ""
|
||||
|
||||
# Optimized branch
|
||||
git checkout perf/defer-inspect-import -q
|
||||
uv pip install -e . -q
|
||||
echo "--- perf/defer-inspect-import ---"
|
||||
hyperfine --warmup 10 --runs $RUNS "$PYTHON -c 'import attrs'" --export-json /tmp/optimized.json
|
||||
echo ""
|
||||
echo "Without bytecode:"
|
||||
hyperfine --warmup 10 --runs $RUNS "PYTHONDONTWRITEBYTECODE=1 $PYTHON -c 'import attrs'" --export-json /tmp/optimized-nobc.json
|
||||
echo ""
|
||||
|
||||
# Baseline
|
||||
git checkout main -q
|
||||
uv pip install -e . -q
|
||||
echo "--- main (baseline) ---"
|
||||
hyperfine --warmup 10 --runs $RUNS "$PYTHON -c 'import attrs'" --export-json /tmp/baseline.json
|
||||
echo ""
|
||||
echo "Without bytecode:"
|
||||
hyperfine --warmup 10 --runs $RUNS "PYTHONDONTWRITEBYTECODE=1 $PYTHON -c 'import attrs'" --export-json /tmp/baseline-nobc.json
|
||||
echo ""
|
||||
|
||||
# importtime check
|
||||
echo "=== inspect deferred? ==="
|
||||
git checkout perf/defer-inspect-import -q
|
||||
uv pip install -e . -q
|
||||
echo -n "Optimized: "
|
||||
if $PYTHON -X importtime -c "import attr" 2>&1 | grep -q inspect; then
|
||||
echo "NO (inspect still eagerly imported)"
|
||||
else
|
||||
echo "YES (inspect deferred)"
|
||||
fi
|
||||
git checkout main -q
|
||||
uv pip install -e . -q
|
||||
echo -n "Baseline: "
|
||||
if $PYTHON -X importtime -c "import attr" 2>&1 | grep -q inspect; then
|
||||
echo "inspect eagerly imported (expected)"
|
||||
else
|
||||
echo "inspect NOT imported (unexpected)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Test suite (optimized branch) ==="
|
||||
git checkout perf/defer-inspect-import -q
|
||||
uv pip install -e . -q
|
||||
$PYTHON -m pytest tests/ -x -q 2>&1 | tail -5
|
||||
|
||||
echo ""
|
||||
echo "Done. JSON results in /tmp/{baseline,optimized,baseline-nobc,optimized-nobc}.json"
|
||||
BENCHEOF
|
||||
chown azureuser:azureuser /home/azureuser/bench.sh
|
||||
chmod +x /home/azureuser/bench.sh
|
||||
106
.codeflash/krrt7/python-attrs/attrs/infra/vm-manage.sh
Normal file
106
.codeflash/krrt7/python-attrs/attrs/infra/vm-manage.sh
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Azure benchmark VM for attrs import time optimization
|
||||
#
|
||||
# Usage:
|
||||
# bash infra/vm-manage.sh {create|start|stop|ip|ssh|bench|destroy}
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RG="attrs-BENCH-RG"
|
||||
VM="attrs-bench"
|
||||
REGION="westus2"
|
||||
SIZE="Standard_D2s_v5"
|
||||
IMAGE="Canonical:ubuntu-24_04-lts:server:latest"
|
||||
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519.pub}"
|
||||
|
||||
case "${1:-help}" in
|
||||
create)
|
||||
if [ ! -f "$SSH_KEY" ]; then
|
||||
echo "Error: SSH public key not found at $SSH_KEY"
|
||||
echo "Generate one: ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519"
|
||||
echo "Or set SSH_KEY=/path/to/key.pub"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Creating resource group..."
|
||||
az group create --name "$RG" --location "$REGION" --only-show-errors --output none
|
||||
|
||||
echo "Creating VM..."
|
||||
az vm create \
|
||||
--resource-group "$RG" \
|
||||
--name "$VM" \
|
||||
--image "$IMAGE" \
|
||||
--size "$SIZE" \
|
||||
--os-disk-size-gb 32 \
|
||||
--admin-username azureuser \
|
||||
--ssh-key-values "$SSH_KEY" \
|
||||
--authentication-type ssh \
|
||||
--security-type TrustedLaunch \
|
||||
--enable-secure-boot true \
|
||||
--enable-vtpm true \
|
||||
--nsg-rule NONE \
|
||||
--custom-data infra/cloud-init.yaml \
|
||||
--only-show-errors
|
||||
|
||||
MY_IP=$(curl -s ifconfig.me)
|
||||
echo "Restricting SSH to $MY_IP..."
|
||||
az network nsg rule create \
|
||||
--resource-group "$RG" \
|
||||
--nsg-name "${VM}NSG" \
|
||||
--name AllowSSHFromMyIP \
|
||||
--priority 1000 \
|
||||
--source-address-prefixes "$MY_IP/32" \
|
||||
--destination-port-ranges 22 \
|
||||
--access Allow \
|
||||
--protocol Tcp \
|
||||
--output none
|
||||
|
||||
echo "VM created. Get IP with: $0 ip"
|
||||
echo "Wait ~2 min for cloud-init, then: $0 bench"
|
||||
;;
|
||||
|
||||
start)
|
||||
echo "Starting VM..."
|
||||
az vm start --resource-group "$RG" --name "$VM"
|
||||
echo "Started. IP: $(az vm show -g "$RG" -n "$VM" -d --query publicIps -o tsv)"
|
||||
;;
|
||||
|
||||
stop)
|
||||
echo "Deallocating VM (stops billing)..."
|
||||
az vm deallocate --resource-group "$RG" --name "$VM"
|
||||
echo "Deallocated."
|
||||
;;
|
||||
|
||||
ip)
|
||||
az vm show -g "$RG" -n "$VM" -d --query publicIps -o tsv
|
||||
;;
|
||||
|
||||
ssh)
|
||||
IP=$(az vm show -g "$RG" -n "$VM" -d --query publicIps -o tsv)
|
||||
ssh -A azureuser@"$IP" "${@:2}"
|
||||
;;
|
||||
|
||||
bench)
|
||||
IP=$(az vm show -g "$RG" -n "$VM" -d --query publicIps -o tsv)
|
||||
ssh -A azureuser@"$IP" "bash ~/bench.sh"
|
||||
;;
|
||||
|
||||
destroy)
|
||||
echo "Destroying resource group (all resources)..."
|
||||
az group delete --name "$RG" --yes --no-wait
|
||||
echo "Deletion started."
|
||||
;;
|
||||
|
||||
help|*)
|
||||
echo "Usage: $0 {create|start|stop|ip|ssh|bench|destroy}"
|
||||
echo ""
|
||||
echo " create - Provision VM with cloud-init"
|
||||
echo " start - Start deallocated VM"
|
||||
echo " stop - Deallocate VM (stops billing)"
|
||||
echo " ip - Show VM public IP"
|
||||
echo " ssh - SSH into VM"
|
||||
echo " bench - Run A/B import benchmark"
|
||||
echo " destroy - Delete resource group and all resources"
|
||||
;;
|
||||
esac
|
||||
29
.codeflash/krrt7/python-attrs/attrs/status.md
Normal file
29
.codeflash/krrt7/python-attrs/attrs/status.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# attrs import time optimization
|
||||
|
||||
## Status: PR open upstream
|
||||
|
||||
## What was done
|
||||
|
||||
- Identified `inspect` (~12ms) as the primary import time bottleneck in attrs
|
||||
- Deferred `import inspect` from module level to first use in `_compat.py` and `_make.py`
|
||||
- Lazy-loaded `converters` and `validators` submodules from `attr/__init__.py` and `attrs/__init__.py`
|
||||
- Benchmarked on Azure VM (Standard_D2s_v5): **25-28% improvement** (42ms -> 31ms)
|
||||
- Opened PR on fork: https://github.com/KRRT7/attrs/pull/1
|
||||
- Opened upstream PR: https://github.com/python-attrs/attrs/pull/1547
|
||||
|
||||
## Branches
|
||||
|
||||
- `perf/defer-inspect-import` on `KRRT7/attrs` — single clean commit `4fbde9c`
|
||||
|
||||
## VM
|
||||
|
||||
- Resource group: `attrs-BENCH-RG`
|
||||
- VM: `attrs-bench` (Standard_D2s_v5, westus2)
|
||||
- State: **deallocated**
|
||||
- Last IP: 20.109.155.88
|
||||
|
||||
## Next
|
||||
|
||||
- Monitor upstream PR for maintainer feedback
|
||||
- Respond to any review comments
|
||||
- VM can be destroyed once PR is merged or closed: `bash infra/vm-manage.sh destroy`
|
||||
|
|
@ -41,6 +41,7 @@ test-optional = [
|
|||
codeflash-core = { workspace = true }
|
||||
codeflash-python = { workspace = true }
|
||||
codeflash-service = { workspace = true }
|
||||
attrs = { path = "/Users/krrt7/Desktop/work/attrs-opt", editable = true }
|
||||
|
||||
[tool.ruff]
|
||||
src = [
|
||||
|
|
|
|||
93
uv.lock
93
uv.lock
|
|
@ -80,11 +80,92 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "26.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" },
|
||||
source = { editable = "/Users/krrt7/Desktop/work/attrs-opt" }
|
||||
|
||||
[package.metadata]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
benchmark = [
|
||||
{ name = "cloudpickle", marker = "platform_python_implementation == 'CPython'" },
|
||||
{ name = "hypothesis" },
|
||||
{ name = "pympler" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-codspeed" },
|
||||
{ name = "pytest-xdist", extras = ["psutil"] },
|
||||
]
|
||||
cov = [
|
||||
{ name = "cloudpickle", marker = "platform_python_implementation == 'CPython'" },
|
||||
{ name = "coverage", extras = ["toml"] },
|
||||
{ name = "hypothesis" },
|
||||
{ name = "pympler" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-xdist", extras = ["psutil"] },
|
||||
]
|
||||
dev = [
|
||||
{ name = "cloudpickle", marker = "platform_python_implementation == 'CPython'" },
|
||||
{ name = "hypothesis" },
|
||||
{ name = "pympler" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-xdist", extras = ["psutil"] },
|
||||
{ name = "ruff" },
|
||||
]
|
||||
docs = [
|
||||
{ name = "cogapp" },
|
||||
{ name = "furo" },
|
||||
{ name = "myst-parser" },
|
||||
{ name = "sphinx" },
|
||||
{ name = "sphinx-notfound-page" },
|
||||
{ name = "sphinxcontrib-towncrier" },
|
||||
{ name = "towncrier" },
|
||||
]
|
||||
docs-watch = [
|
||||
{ name = "cogapp" },
|
||||
{ name = "furo" },
|
||||
{ name = "myst-parser" },
|
||||
{ name = "sphinx" },
|
||||
{ name = "sphinx-notfound-page" },
|
||||
{ name = "sphinxcontrib-towncrier" },
|
||||
{ name = "towncrier" },
|
||||
{ name = "watchfiles" },
|
||||
]
|
||||
mypy = [
|
||||
{ name = "cloudpickle", marker = "platform_python_implementation == 'CPython'" },
|
||||
{ name = "hypothesis" },
|
||||
{ name = "pympler" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-mypy-plugins", marker = "python_full_version >= '3.10' and platform_python_implementation == 'CPython'" },
|
||||
{ name = "pytest-xdist", extras = ["psutil"] },
|
||||
]
|
||||
pyrefly = [
|
||||
{ name = "cloudpickle", marker = "platform_python_implementation == 'CPython'" },
|
||||
{ name = "hypothesis" },
|
||||
{ name = "pympler" },
|
||||
{ name = "pyrefly" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-xdist", extras = ["psutil"] },
|
||||
]
|
||||
pyright = [
|
||||
{ name = "cloudpickle", marker = "platform_python_implementation == 'CPython'" },
|
||||
{ name = "hypothesis" },
|
||||
{ name = "pympler" },
|
||||
{ name = "pyright" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-xdist", extras = ["psutil"] },
|
||||
]
|
||||
tests = [
|
||||
{ name = "cloudpickle", marker = "platform_python_implementation == 'CPython'" },
|
||||
{ name = "hypothesis" },
|
||||
{ name = "pympler" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-xdist", extras = ["psutil"] },
|
||||
]
|
||||
ty = [
|
||||
{ name = "cloudpickle", marker = "platform_python_implementation == 'CPython'" },
|
||||
{ name = "hypothesis" },
|
||||
{ name = "pympler" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-xdist", extras = ["psutil"] },
|
||||
{ name = "ty" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -325,7 +406,7 @@ dependencies = [
|
|||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "attrs", specifier = ">=26.1.0" },
|
||||
{ name = "attrs", editable = "/Users/krrt7/Desktop/work/attrs-opt" },
|
||||
{ name = "gitpython", specifier = ">=3.1.0" },
|
||||
{ name = "platformdirs", specifier = ">=4.0.0" },
|
||||
{ name = "posthog", specifier = ">=3.0.0" },
|
||||
|
|
|
|||
Loading…
Reference in a new issue