已合并
feat: Add python performance scripts and results #3249
feat: Add python performance scripts and results #3249
已合并
Tian-Fantasea创建于 10 天前
16 个文件变更+1643-0
Atests/python/python_test.sh+160-0
@@ -0,0 +1,160 @@
1+#!/bin/bash
2+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
3+SOFTWARE_NAME="python"
4+SOFTWARE_VERSION="${SOFTWARE_VERSION:-3.14.7}"
5+export SOFTWARE_VERSION
6+BUILD_METHOD="source_build"
7+TARGET_OS="${TARGET_OS:-openEuler 24.03 SP3}"
8+TARGET_MODEL="${TARGET_MODEL:-Kunpeng-920}"
9+RESULTS_DIR="${SCRIPT_DIR}/results/${SOFTWARE_VERSION}"
10+mkdir -p "${RESULTS_DIR}"
11+LOG_FILE="${RESULTS_DIR}/results.log"
12+JSON_HELPER="${SCRIPT_DIR}/scripts/json_helper.py"
13+BUILD_TMPDIR=""
14+SHUNIT2_PATH=""
15+PYTHON_BIN=""
16+ITERATIONS="${ITERATIONS:-1}"
17+MINIMUM_OPS_PER_SEC="${MINIMUM_OPS_PER_SEC:-100}"
18+log() { local tag="$1"; shift; printf '[%s] %s\n' "$tag" "$*" | tee -a "${LOG_FILE}"; }
19+json_get() { python3 "${JSON_HELPER}" "$1" get "${@:2}"; }
20+json_field_exists() { python3 "${JSON_HELPER}" "$1" field_exists "$2"; }
21+json_count_results() { python3 "${JSON_HELPER}" "$1" count_results; }
22+json_throughput_ge() { python3 "${JSON_HELPER}" "$1" throughput_ge "$2" "${@:3}"; }
23+json_avg_throughput() { python3 "${JSON_HELPER}" "$1" avg_throughput "${@:2}"; }
24+json_version() { python3 "${JSON_HELPER}" "$1" version; }
25+json_contains() { python3 "${JSON_HELPER}" "$1" contains "$2"; }
26+detect_os_id() { if [ -f /etc/os-release ]; then . /etc/os-release; echo "${ID}"; else echo "unknown"; fi; }
27+detect_os_name() { echo "${TARGET_OS}"; }
28+create_build_tmpdir() { BUILD_TMPDIR="$(mktemp -d /tmp/python_build_XXXXXX)"; log "BUILD" "Created temp dir: ${BUILD_TMPDIR}"; }
29+cleanup_build_tmpdir() { if [ -n "${BUILD_TMPDIR}" ] && [ -d "${BUILD_TMPDIR}" ]; then rm -rf "${BUILD_TMPDIR}"; BUILD_TMPDIR=""; fi; }
30+download_shunit2() {
31+ local d; d="$(mktemp -d /tmp/shunit2_XXXXXX)"; SHUNIT2_PATH="${d}/shunit2"
32+ log "SETUP" "Downloading shUnit2 to ${d}..."
33+ local mirrors=("https://raw.githubusercontent.com/kward/shunit2/master/shunit2" "https://mirrors.aliyun.com/github-raw/kward/shunit2/master/shunit2" "https://raw.gitmirror.com/kward/shunit2/master/shunit2")
34+ local ok=0
35+ for u in "${mirrors[@]}"; do curl --connect-timeout 30 --max-time 60 -sL -o "${SHUNIT2_PATH}" "${u}" && { chmod +x "${SHUNIT2_PATH}"; grep -q "^SHUNIT_VERSION=" "${SHUNIT2_PATH}" && { ok=1; break; }; }; rm -f "${SHUNIT2_PATH}"; done
36+ if [ "${ok}" -eq 0 ]; then for u in "${mirrors[@]}"; do wget --timeout=30 --tries=2 -q -O "${SHUNIT2_PATH}" "${u}" 2>/dev/null && { chmod +x "${SHUNIT2_PATH}"; grep -q "^SHUNIT_VERSION=" "${SHUNIT2_PATH}" && { ok=1; break; }; }; rm -f "${SHUNIT2_PATH}"; done; fi
37+ if [ "${ok}" -eq 0 ]; then log "ERROR" "Failed to download shUnit2"; rm -rf "${d}"; return 1; fi
38+}
39+check_prerequisites() {
40+ local err=0
41+ command -v python3 >/dev/null 2>&1 && log "CHECK" "Python3 OK: $(python3 --version 2>&1)" || { log "ERROR" "python3 missing"; err=$((err+1)); }
42+ command -v gcc >/dev/null 2>&1 && log "CHECK" "GCC OK: $(gcc --version 2>&1 | head -1)" || log "WARN" "gcc not found"
43+ command -v make >/dev/null 2>&1 && log "CHECK" "Make OK" || log "WARN" "make not found"
44+ command -v git >/dev/null 2>&1 && log "CHECK" "Git OK: $(git --version 2>&1)" || log "WARN" "git not found"
45+ [ -f "${JSON_HELPER}" ] && log "CHECK" "json_helper.py OK" || { log "ERROR" "json_helper.py not found"; err=$((err+1)); }
46+ local os_id; os_id="$(detect_os_id)"
47+ local os_id_lower; os_id_lower="$(echo "${os_id}" | tr '[:upper:]' '[:lower:]')"
48+ log "CHECK" "OS: $(detect_os_name) (${os_id})"
49+ log "CHECK" "Architecture: $(uname -m)"
50+ log "CHECK" "Build method: ${BUILD_METHOD} (configure+make, CPython source)"
51+ case "${os_id_lower}" in
52+ ubuntu|debian) sudo apt-get update -qq >/dev/null 2>&1; sudo apt-get install -y -qq build-essential gcc make git wget curl libssl-dev zlib1g-dev libffi-dev >/dev/null 2>&1 ;;
53+ openeuler) sudo dnf install -y gcc make git wget curl openssl-devel zlib-devel libffi-devel >/dev/null 2>&1 ;;
54+ centos|rhel|fedora) sudo dnf install -y gcc make git wget curl openssl-devel zlib-devel libffi-devel >/dev/null 2>&1 ;;
55+ *) log "WARN" "Unknown OS: ${os_id}" ;;
56+ esac
57+ return ${err}
58+}
59+phase1_build() {
60+ log "PHASE1" "=== Phase 1: Source Build CPython v${SOFTWARE_VERSION} ==="
61+ create_build_tmpdir
62+ local SRC="${BUILD_TMPDIR}/cpython_src"
63+ local INSTALL="${BUILD_TMPDIR}/install"
64+ local ver_tag="v${SOFTWARE_VERSION}"
65+ [ "${SOFTWARE_VERSION:0:1}" = "v" ] && ver_tag="${SOFTWARE_VERSION}"
66+ log "PHASE1" "Cloning CPython tag ${ver_tag}..."
67+ git clone --branch "${ver_tag}" --depth 1 https://github.com/python/cpython.git "${SRC}" 2>&1 | tee -a "${LOG_FILE}" || { log "ERROR" "Failed to clone CPython"; return 1; }
68+ log "PHASE1" "Configuring..."
69+ (cd "${SRC}" && ./configure --prefix="${INSTALL}" --enable-optimizations=no 2>&1 | tee -a "${LOG_FILE}") || { log "ERROR" "configure failed"; return 1; }
70+ log "PHASE1" "Compiling (this may take 5-15 minutes)..."
71+ (cd "${SRC}" && make -j$(nproc) 2>&1 | tee -a "${LOG_FILE}") || { log "ERROR" "make failed"; return 1; }
72+ log "PHASE1" "Installing..."
73+ (cd "${SRC}" && make install 2>&1 | tee -a "${LOG_FILE}") || log "WARN" "make install had issues"
74+ PYTHON_BIN="${INSTALL}/bin/python3"
75+ if [ ! -x "${PYTHON_BIN}" ]; then PYTHON_BIN="$(find "${INSTALL}" -name python3 -type f -executable 2>/dev/null | head -1)"; fi
76+ if [ ! -x "${PYTHON_BIN}" ]; then log "ERROR" "python3 binary not found after build"; return 1; fi
77+ log "PHASE1" "Verifying..."
78+ "${PYTHON_BIN}" --version 2>&1 | tee -a "${LOG_FILE}" | head -1 || log "WARN" "version check failed"
79+ log "PHASE1" "Build phase complete"
80+}
81+phase2_verify() {
82+ log "PHASE2" "=== Phase 2: Collect Version Info ==="
83+ local timestamp model arch kernel os_name cpu_model cores python_ver gcc_ver
84+ timestamp="$(date -u '+%Y-%m-%dT%H:%M:%SZ' | tr -d '\n\t')"
85+ model="${TARGET_MODEL}"; arch="$(uname -m | tr -d '\n\t')"; kernel="$(uname -r | tr -d '\n\t')"
86+ os_name="$(detect_os_name | tr -d '\n\t')"
87+ cpu_model="$(grep 'model name' /proc/cpuinfo 2>/dev/null | head -1 | cut -d: -f2 | xargs | tr -d '\n\t')"
88+ if [ -z "${cpu_model}" ]; then local np; np="$(grep -c 'processor' /proc/cpuinfo 2>/dev/null || echo 0)"; cpu_model="ARM64 CPU (${np} cores)"; fi
89+ cores="$(nproc 2>/dev/null | tr -d '\n\t' || echo '4')"
90+ python_ver="$(python3 --version 2>&1 | tr -d '\n\t')"
91+ gcc_ver="$(gcc --version 2>/dev/null | head -1 | cut -d' ' -f3 | tr -d '\n\t' || echo 'unknown')"
92+ python3 "${JSON_HELPER}" "${RESULTS_DIR}/version_info.json" write_version_info \
93+ "${timestamp}" "${model}" "${arch}" "${kernel}" "${os_name}" "${cpu_model}" \
94+ "${cores}" "${SOFTWARE_NAME}" "${SOFTWARE_VERSION}" "${python_ver}" "${gcc_ver}"
95+ log "PHASE2" "Version info saved (Python: ${SOFTWARE_VERSION}, GCC: ${gcc_ver})"
96+}
97+phase3_run_benchmarks() {
98+ log "PHASE3" "=== Phase 3: Run Benchmarks ==="
99+ mkdir -p "${RESULTS_DIR}"
100+ log "PHASE3A" "Running pyperformance benchmark..."
101+ python3 "${SCRIPT_DIR}/scripts/benchmark_py.py" "${PYTHON_BIN}" "${RESULTS_DIR}/benchmark_py.json" "${ITERATIONS}" 2>&1 | tee -a "${LOG_FILE}" || log "WARN" "pyperformance had issues"
102+ log "PHASE3B" "Running micro benchmark..."
103+ python3 "${SCRIPT_DIR}/scripts/micro_benchmark.py" "${PYTHON_BIN}" "${RESULTS_DIR}/micro_benchmark.json" "${ITERATIONS}" 2>&1 | tee -a "${LOG_FILE}" || log "WARN" "Micro benchmark had issues"
104+}
105+phase4_results() {
106+ log "PHASE4" "=== Phase 4: Aggregate and Report ==="
107+ python3 "${SCRIPT_DIR}/scripts/aggregate_results.py" "${RESULTS_DIR}" "${RESULTS_DIR}/results.json"
108+ python3 "${SCRIPT_DIR}/scripts/generate_summary.py" "${RESULTS_DIR}/results.json" "${RESULTS_DIR}/results.txt"
109+ log "PHASE4" "Reports generated:"
110+ log "PHASE4" " JSON: ${RESULTS_DIR}/results.json"
111+ log "PHASE4" " TXT: ${RESULTS_DIR}/results.txt"
112+ log "PHASE4" " LOG: ${RESULTS_DIR}/results.log"
113+}
114+oneTimeSetUp() {
115+ mkdir -p "${RESULTS_DIR}"
116+ log "START" "${SOFTWARE_NAME} Performance Benchmark - v${SOFTWARE_VERSION} (${BUILD_METHOD})"
117+ check_prerequisites || log "WARN" "Some prerequisites missing"
118+ phase1_build || log "FATAL" "Phase 1 failed"
119+ phase2_verify || log "WARN" "Phase 2 had issues"
120+ phase3_run_benchmarks || log "WARN" "Phase 3 had issues"
121+ phase4_results || log "WARN" "Phase 4 had issues"
122+}
123+oneTimeTearDown() { cleanup_build_tmpdir; if [ -n "${SHUNIT2_PATH}" ]; then rm -rf "$(dirname "${SHUNIT2_PATH}")"; SHUNIT2_PATH=""; fi; }
124+setUp() { rm -f "${RESULTS_DIR}/test_temp_*.json"; }
125+tearDown() { rm -f "${RESULTS_DIR}/test_temp_*.json"; }
126+testArchitectureIsARM64() { local a; a="$(uname -m)"; assertTrue "Arch aarch64/arm64, got ${a}" "[ '${a}' = 'aarch64' ] || [ '${a}' = 'arm64' ]"; }
127+testSoftwareIsInstalled() { local f=0; [ -n "${PYTHON_BIN}" ] && [ -x "${PYTHON_BIN}" ] && f=1; if [ "${f}" -eq 0 ]; then startSkipping; return; fi; assertTrue "python3 binary should exist" "[ ${f} -eq 1 ]"; }
128+testSoftwareVersionMatches() { assertNotNull "Version not empty" "${SOFTWARE_VERSION}"; }
129+testVersionInfoExists() { assertTrue "version_info.json exists" "[ -f '${RESULTS_DIR}/version_info.json' ]"; }
130+testVersionInfoHasArchitecture() { local vf="${RESULTS_DIR}/version_info.json"; [ -f "${vf}" ] || { startSkipping; return; }; assertTrue "has architecture" "[ $(json_field_exists "${vf}" architecture) -eq 1 ]"; }
131+testVersionInfoHasSoftwareVersion() { local vf="${RESULTS_DIR}/version_info.json"; [ -f "${vf}" ] || { startSkipping; return; }; assertTrue "has software_version" "[ $(json_field_exists "${vf}" software_version) -eq 1 ]"; }
132+testBenchmarkPrimaryProducesResults() { assertTrue "benchmark_py.json exists" "[ -f '${RESULTS_DIR}/benchmark_py.json' ]"; }
133+testBenchmarkPrimaryHasRequiredFields() { local bf="${RESULTS_DIR}/benchmark_py.json"; [ -f "${bf}" ] || { startSkipping; return; }; assertTrue "has benchmark" "[ $(json_contains "${bf}" benchmark) -eq 1 ]"; assertTrue "has performance_metrics" "[ $(json_contains "${bf}" performance_metrics) -eq 1 ]"; assertTrue "has results_summary" "[ $(json_contains "${bf}" results_summary) -eq 1 ]"; }
134+testBenchmarkPrimaryOpsAboveThreshold() { local bf="${RESULTS_DIR}/benchmark_py.json"; [ -f "${bf}" ] || { startSkipping; return; }; local ops; ops="$(json_avg_throughput "${bf}" results_summary ops_per_sec)"; if [ -z "${ops}" ] || [ "${ops}" = "0" ]; then startSkipping; return; fi; echo "[DIAG] Avg ops/sec: ${ops} (min: ${MINIMUM_OPS_PER_SEC})"; assertTrue "Avg ops/sec >= ${MINIMUM_OPS_PER_SEC}" "[ $(echo "${ops} >= ${MINIMUM_OPS_PER_SEC}" | bc -l) -eq 1 ]"; }
135+testBenchmarkPrimaryIsPyperformance() { local bf="${RESULTS_DIR}/benchmark_py.json"; [ -f "${bf}" ] || { startSkipping; return; }; assertEquals "benchmark is pyperformance" "pyperformance" "$(json_get "${bf}" benchmark)"; }
136+testBenchmarkMicroProducesResults() { assertTrue "micro_benchmark.json exists" "[ -f '${RESULTS_DIR}/micro_benchmark.json' ]"; }
137+testBenchmarkMicroThreadScaling() { local bf="${RESULTS_DIR}/micro_benchmark.json"; [ -f "${bf}" ] || { startSkipping; return; }; assertTrue "has thread_scaling" "[ $(json_contains "${bf}" thread_scaling) -eq 1 ]"; }
138+testAggregatedResultsExist() { assertTrue "results.json exists" "[ -f '${RESULTS_DIR}/results.json' ]"; }
139+testSummaryReportGenerated() { assertTrue "results.txt exists" "[ -f '${RESULTS_DIR}/results.txt' ]"; }
140+testLogFileGenerated() { assertTrue "results.log exists" "[ -f '${RESULTS_DIR}/results.log' ]"; }
141+testAggregatedResultsContainsAllBenchmarks() { local af="${RESULTS_DIR}/results.json"; [ -f "${af}" ] || { startSkipping; return; }; assertTrue "has primary" "[ $(json_contains "${af}" primary) -eq 1 ]"; assertTrue "has micro" "[ $(json_contains "${af}" micro) -eq 1 ]"; }
142+usage() {
143+ echo "Usage: $0 [OPTIONS]"
144+ echo "CPython Performance Benchmark (source build + pyperformance)"
145+ echo "Options: --check (prerequisites), -h|--help"
146+ echo "Env: SOFTWARE_VERSION (default: 3.14.7, tag v prefix), ITERATIONS (default: 1)"
147+ echo " MINIMUM_OPS_PER_SEC (default: 100)"
148+ echo "Note: Builds CPython from source (5-15 min), then runs pyperformance suite"
149+}
150+main() {
151+ local check_only=0
152+ while [ $# -gt 0 ]; do case "$1" in --check) check_only=1; shift ;; -h|--help) usage; exit 0 ;; *) log "ERROR" "Unknown: $1"; usage; exit 1 ;; esac; done
153+ log "START" "${SOFTWARE_NAME} Performance Benchmark v${SOFTWARE_VERSION}"
154+ if [ "${check_only}" -eq 1 ]; then check_prerequisites; exit $?; fi
155+ check_prerequisites || { log "FATAL" "Prerequisites not met"; exit 1; }
156+ download_shunit2 || { log "FATAL" "Failed to download shUnit2"; exit 1; }
157+ SHUNIT_PARENT="${SCRIPT_DIR}/${SOFTWARE_NAME}_test.sh"
158+ . "${SHUNIT2_PATH}"
159+}
160+if [ "${1:-}" != "--shunit2-run" ]; then main "$@"; fi
Atests/python/results/3.13.15/benchmark_py.json+24-0
@@ -0,0 +1,24 @@
1+{
2+ "benchmark": "pyperformance",
3+ "description": "CPython pyperformance benchmark suite (json_dumps,json_loads,nbody,telco,fannkuch,regex_v8,meteor_contest) on ARM64",
4+ "reference": "https://github.com/python/pyperformance",
5+ "software": "python",
6+ "version": "3.13.15",
7+ "architecture": "arm64",
8+ "timestamp": "2026-08-10T06:34:19Z",
9+ "performance_metrics": {
10+ "mean_ms": {
11+ "unit": "ms",
12+ "description": "Mean execution time"
13+ },
14+ "ops_per_sec": {
15+ "unit": "ops/sec",
16+ "description": "Operations per second (1/mean)"
17+ }
18+ },
19+ "parameters": {
20+ "bench_tests": "json_dumps,json_loads,nbody,telco,fannkuch,regex_v8,meteor_contest",
21+ "iterations": 1
22+ },
23+ "results_summary": {}
24+}
Atests/python/results/3.13.15/micro_benchmark.json+114-0
@@ -0,0 +1,114 @@
1+{
2+ "benchmark": "micro_operations",
3+ "description": "CPython micro: timeit operations + GIL thread scaling on ARM64",
4+ "reference": "https://github.com/python/cpython",
5+ "software": "python",
6+ "version": "3.13.15",
7+ "architecture": "arm64",
8+ "timestamp": "2026-08-10T06:35:10Z",
9+ "performance_metrics": {
10+ "ops_per_sec": {
11+ "unit": "ops/sec",
12+ "description": "Operations per second"
13+ },
14+ "seconds_per_op": {
15+ "unit": "s",
16+ "description": "Seconds per operation"
17+ }
18+ },
19+ "parameters": {
20+ "micro_ops": [
21+ "list_comprehension",
22+ "dict_get",
23+ "string_concat",
24+ "json_parse"
25+ ],
26+ "thread_counts": [
27+ "1",
28+ "2",
29+ "4",
30+ "8",
31+ "all"
32+ ],
33+ "max_threads": 32,
34+ "iterations": 1
35+ },
36+ "results": {
37+ "micro_ops": {
38+ "list_comprehension": {
39+ "size_1000": {
40+ "seconds_per_op": 5.708e-05,
41+ "ops_per_sec": 17520.37
42+ },
43+ "size_10000": {
44+ "seconds_per_op": 0.00061735,
45+ "ops_per_sec": 1619.83
46+ },
47+ "size_100000": {
48+ "seconds_per_op": 0.00747901,
49+ "ops_per_sec": 133.71
50+ }
51+ },
52+ "dict_get": {
53+ "size_1000": {
54+ "seconds_per_op": 5e-08,
55+ "ops_per_sec": 20867995.18
56+ },
57+ "size_10000": {
58+ "seconds_per_op": 5e-08,
59+ "ops_per_sec": 20479140.66
60+ },
61+ "size_100000": {
62+ "seconds_per_op": 5e-08,
63+ "ops_per_sec": 19550304.5
64+ }
65+ },
66+ "string_concat": {
67+ "size_100": {
68+ "seconds_per_op": 0.0,
69+ "ops_per_sec": 0
70+ },
71+ "size_1000": {
72+ "seconds_per_op": 0.0,
73+ "ops_per_sec": 0
74+ },
75+ "size_10000": {
76+ "seconds_per_op": 0.0,
77+ "ops_per_sec": 0
78+ }
79+ },
80+ "json_parse": {
81+ "size_1000": {
82+ "seconds_per_op": 0.0,
83+ "ops_per_sec": 0
84+ },
85+ "size_10000": {
86+ "seconds_per_op": 0.0,
87+ "ops_per_sec": 0
88+ }
89+ }
90+ },
91+ "thread_scaling": {
92+ "threads_1": {
93+ "seconds_per_op": 0.00063066,
94+ "ops_per_sec": 1585.64
95+ },
96+ "threads_2": {
97+ "seconds_per_op": 0.00063307,
98+ "ops_per_sec": 1579.61
99+ },
100+ "threads_4": {
101+ "seconds_per_op": 0.00063262,
102+ "ops_per_sec": 1580.72
103+ },
104+ "threads_8": {
105+ "seconds_per_op": 0.00063907,
106+ "ops_per_sec": 1564.77
107+ },
108+ "threads_all": {
109+ "seconds_per_op": 0.00063963,
110+ "ops_per_sec": 1563.41
111+ }
112+ }
113+ }
114+}
Atests/python/results/3.13.15/results.json+159-0
@@ -0,0 +1,159 @@
1+{
2+ "test_time": "2026-08-10T06:34:16Z",
3+ "environment": {
4+ "test_time": "2026-08-10T06:34:16Z",
5+ "Model": "Kunpeng-920",
6+ "architecture": "aarch64",
7+ "kernel": "6.6.0-132.0.0.111.oe2403sp3.aarch64",
8+ "os": "openEuler 24.03 SP3",
9+ "cpu_model": "ARM64 CPU (32 cores)",
10+ "cpu_cores": 32,
11+ "software_name": "python",
12+ "software_version": "3.13.15",
13+ "python_version": "Python 3.11.6",
14+ "gcc_version": "12.3.1"
15+ },
16+ "benchmarks": {
17+ "primary": {
18+ "benchmark": "pyperformance",
19+ "description": "CPython pyperformance benchmark suite (json_dumps,json_loads,nbody,telco,fannkuch,regex_v8,meteor_contest) on ARM64",
20+ "reference": "https://github.com/python/pyperformance",
21+ "software": "python",
22+ "version": "3.13.15",
23+ "architecture": "arm64",
24+ "timestamp": "2026-08-10T06:34:19Z",
25+ "performance_metrics": {
26+ "mean_ms": {
27+ "unit": "ms",
28+ "description": "Mean execution time"
29+ },
30+ "ops_per_sec": {
31+ "unit": "ops/sec",
32+ "description": "Operations per second (1/mean)"
33+ }
34+ },
35+ "parameters": {
36+ "bench_tests": "json_dumps,json_loads,nbody,telco,fannkuch,regex_v8,meteor_contest",
37+ "iterations": 1
38+ },
39+ "results_summary": {}
40+ },
41+ "micro": {
42+ "benchmark": "micro_operations",
43+ "description": "CPython micro: timeit operations + GIL thread scaling on ARM64",
44+ "reference": "https://github.com/python/cpython",
45+ "software": "python",
46+ "version": "3.13.15",
47+ "architecture": "arm64",
48+ "timestamp": "2026-08-10T06:35:10Z",
49+ "performance_metrics": {
50+ "ops_per_sec": {
51+ "unit": "ops/sec",
52+ "description": "Operations per second"
53+ },
54+ "seconds_per_op": {
55+ "unit": "s",
56+ "description": "Seconds per operation"
57+ }
58+ },
59+ "parameters": {
60+ "micro_ops": [
61+ "list_comprehension",
62+ "dict_get",
63+ "string_concat",
64+ "json_parse"
65+ ],
66+ "thread_counts": [
67+ "1",
68+ "2",
69+ "4",
70+ "8",
71+ "all"
72+ ],
73+ "max_threads": 32,
74+ "iterations": 1
75+ },
76+ "results": {
77+ "micro_ops": {
78+ "list_comprehension": {
79+ "size_1000": {
80+ "seconds_per_op": 5.708e-05,
81+ "ops_per_sec": 17520.37
82+ },
83+ "size_10000": {
84+ "seconds_per_op": 0.00061735,
85+ "ops_per_sec": 1619.83
86+ },
87+ "size_100000": {
88+ "seconds_per_op": 0.00747901,
89+ "ops_per_sec": 133.71
90+ }
91+ },
92+ "dict_get": {
93+ "size_1000": {
94+ "seconds_per_op": 5e-08,
95+ "ops_per_sec": 20867995.18
96+ },
97+ "size_10000": {
98+ "seconds_per_op": 5e-08,
99+ "ops_per_sec": 20479140.66
100+ },
101+ "size_100000": {
102+ "seconds_per_op": 5e-08,
103+ "ops_per_sec": 19550304.5
104+ }
105+ },
106+ "string_concat": {
107+ "size_100": {
108+ "seconds_per_op": 0.0,
109+ "ops_per_sec": 0
110+ },
111+ "size_1000": {
112+ "seconds_per_op": 0.0,
113+ "ops_per_sec": 0
114+ },
115+ "size_10000": {
116+ "seconds_per_op": 0.0,
117+ "ops_per_sec": 0
118+ }
119+ },
120+ "json_parse": {
121+ "size_1000": {
122+ "seconds_per_op": 0.0,
123+ "ops_per_sec": 0
124+ },
125+ "size_10000": {
126+ "seconds_per_op": 0.0,
127+ "ops_per_sec": 0
128+ }
129+ }
130+ },
131+ "thread_scaling": {
132+ "threads_1": {
133+ "seconds_per_op": 0.00063066,
134+ "ops_per_sec": 1585.64
135+ },
136+ "threads_2": {
137+ "seconds_per_op": 0.00063307,
138+ "ops_per_sec": 1579.61
139+ },
140+ "threads_4": {
141+ "seconds_per_op": 0.00063262,
142+ "ops_per_sec": 1580.72
143+ },
144+ "threads_8": {
145+ "seconds_per_op": 0.00063907,
146+ "ops_per_sec": 1564.77
147+ },
148+ "threads_all": {
149+ "seconds_per_op": 0.00063963,
150+ "ops_per_sec": 1563.41
151+ }
152+ }
153+ }
154+ }
155+ },
156+ "summary": {},
157+ "software": "python",
158+ "version": "3.13.15"
159+}
Atests/python/results/3.13.15/results.txt+49-0
@@ -0,0 +1,49 @@
1+======================================================================
2+ CPython Performance Benchmark Report
3+======================================================================
4+ Generated: 2026-08-10 06:35:10 UTC
5+ Test Time: 2026-08-10T06:34:16Z
6+ 
7+ --- Environment ---
8+ Architecture: aarch64
9+ Model: Kunpeng-920
10+ CPU Model: ARM64 CPU (32 cores)
11+ CPU Cores: 32
12+ Python Version: 3.13.15
13+ OS: openEuler 24.03 SP3
14+ Kernel: 6.6.0-132.0.0.111.oe2403sp3.aarch64
15+ 
16+ --- pyperformance Benchmarks (Primary) ---
17+ Description: CPython pyperformance benchmark suite (json_dumps,json_loads,nbody,telco,fannkuch,regex_v8,meteor_contest) on ARM64
18+ Tests: json_dumps,json_loads,nbody,telco,fannkuch,regex_v8,meteor_contest
19+ 
20+ benchmark mean (ms) ops/sec min (ms)
21+ ----------------------------------------------------------------
22+ 
23+ --- Micro Benchmarks ---
24+ Micro operations (timeit):
25+ [dict_get]
26+ size_1000 : 20867995.18 ops/sec
27+ size_10000 : 20479140.66 ops/sec
28+ size_100000 : 19550304.5 ops/sec
29+ [json_parse]
30+ size_1000 : 0 ops/sec
31+ size_10000 : 0 ops/sec
32+ [list_comprehension]
33+ size_1000 : 17520.37 ops/sec
34+ size_10000 : 1619.83 ops/sec
35+ size_100000 : 133.71 ops/sec
36+ [string_concat]
37+ size_100 : 0 ops/sec
38+ size_1000 : 0 ops/sec
39+ size_10000 : 0 ops/sec
40+ Thread scaling (GIL limited):
41+ threads_all : 1563.41 ops/sec
42+ threads_1 : 1585.64 ops/sec
43+ threads_2 : 1579.61 ops/sec
44+ threads_4 : 1580.72 ops/sec
45+ threads_8 : 1564.77 ops/sec
46+ 
47+======================================================================
48+ Report generated by CPython Performance Benchmark Workflow
49+======================================================================
Atests/python/results/3.13.15/version_info.json+13-0
@@ -0,0 +1,13 @@
1+{
2+ "test_time": "2026-08-10T06:34:16Z",
3+ "Model": "Kunpeng-920",
4+ "architecture": "aarch64",
5+ "kernel": "6.6.0-132.0.0.111.oe2403sp3.aarch64",
6+ "os": "openEuler 24.03 SP3",
7+ "cpu_model": "ARM64 CPU (32 cores)",
8+ "cpu_cores": 32,
9+ "software_name": "python",
10+ "software_version": "3.13.15",
11+ "python_version": "Python 3.11.6",
12+ "gcc_version": "12.3.1"
13+}
Atests/python/results/3.14.7/benchmark_py.json+24-0
@@ -0,0 +1,24 @@
1+{
2+ "benchmark": "pyperformance",
3+ "description": "CPython pyperformance benchmark suite (json_dumps,json_loads,nbody,telco,fannkuch,regex_v8,meteor_contest) on ARM64",
4+ "reference": "https://github.com/python/pyperformance",
5+ "software": "python",
6+ "version": "3.14.7",
7+ "architecture": "arm64",
8+ "timestamp": "2026-08-10T06:26:38Z",
9+ "performance_metrics": {
10+ "mean_ms": {
11+ "unit": "ms",
12+ "description": "Mean execution time"
13+ },
14+ "ops_per_sec": {
15+ "unit": "ops/sec",
16+ "description": "Operations per second (1/mean)"
17+ }
18+ },
19+ "parameters": {
20+ "bench_tests": "json_dumps,json_loads,nbody,telco,fannkuch,regex_v8,meteor_contest",
21+ "iterations": 1
22+ },
23+ "results_summary": {}
24+}
Atests/python/results/3.14.7/micro_benchmark.json+114-0
@@ -0,0 +1,114 @@
1+{
2+ "benchmark": "micro_operations",
3+ "description": "CPython micro: timeit operations + GIL thread scaling on ARM64",
4+ "reference": "https://github.com/python/cpython",
5+ "software": "python",
6+ "version": "3.14.7",
7+ "architecture": "arm64",
8+ "timestamp": "2026-08-10T06:27:31Z",
9+ "performance_metrics": {
10+ "ops_per_sec": {
11+ "unit": "ops/sec",
12+ "description": "Operations per second"
13+ },
14+ "seconds_per_op": {
15+ "unit": "s",
16+ "description": "Seconds per operation"
17+ }
18+ },
19+ "parameters": {
20+ "micro_ops": [
21+ "list_comprehension",
22+ "dict_get",
23+ "string_concat",
24+ "json_parse"
25+ ],
26+ "thread_counts": [
27+ "1",
28+ "2",
29+ "4",
30+ "8",
31+ "all"
32+ ],
33+ "max_threads": 32,
34+ "iterations": 1
35+ },
36+ "results": {
37+ "micro_ops": {
38+ "list_comprehension": {
39+ "size_1000": {
40+ "seconds_per_op": 5.47e-05,
41+ "ops_per_sec": 18280.02
42+ },
43+ "size_10000": {
44+ "seconds_per_op": 0.00060317,
45+ "ops_per_sec": 1657.92
46+ },
47+ "size_100000": {
48+ "seconds_per_op": 0.00786819,
49+ "ops_per_sec": 127.09
50+ }
51+ },
52+ "dict_get": {
53+ "size_1000": {
54+ "seconds_per_op": 6e-08,
55+ "ops_per_sec": 15979608.81
56+ },
57+ "size_10000": {
58+ "seconds_per_op": 6e-08,
59+ "ops_per_sec": 15757993.88
60+ },
61+ "size_100000": {
62+ "seconds_per_op": 6e-08,
63+ "ops_per_sec": 16103027.53
64+ }
65+ },
66+ "string_concat": {
67+ "size_100": {
68+ "seconds_per_op": 0.0,
69+ "ops_per_sec": 0
70+ },
71+ "size_1000": {
72+ "seconds_per_op": 0.0,
73+ "ops_per_sec": 0
74+ },
75+ "size_10000": {
76+ "seconds_per_op": 0.0,
77+ "ops_per_sec": 0
78+ }
79+ },
80+ "json_parse": {
81+ "size_1000": {
82+ "seconds_per_op": 0.0,
83+ "ops_per_sec": 0
84+ },
85+ "size_10000": {
86+ "seconds_per_op": 0.0,
87+ "ops_per_sec": 0
88+ }
89+ }
90+ },
91+ "thread_scaling": {
92+ "threads_1": {
93+ "seconds_per_op": 0.00060386,
94+ "ops_per_sec": 1656.0
95+ },
96+ "threads_2": {
97+ "seconds_per_op": 0.00060667,
98+ "ops_per_sec": 1648.35
99+ },
100+ "threads_4": {
101+ "seconds_per_op": 0.0006026,
102+ "ops_per_sec": 1659.47
103+ },
104+ "threads_8": {
105+ "seconds_per_op": 0.00060183,
106+ "ops_per_sec": 1661.61
107+ },
108+ "threads_all": {
109+ "seconds_per_op": 0.00060128,
110+ "ops_per_sec": 1663.11
111+ }
112+ }
113+ }
114+}
Atests/python/results/3.14.7/results.json+159-0
@@ -0,0 +1,159 @@
1+{
2+ "test_time": "2026-08-10T06:26:35Z",
3+ "environment": {
4+ "test_time": "2026-08-10T06:26:35Z",
5+ "Model": "Kunpeng-920",
6+ "architecture": "aarch64",
7+ "kernel": "6.6.0-132.0.0.111.oe2403sp3.aarch64",
8+ "os": "openEuler 24.03 SP3",
9+ "cpu_model": "ARM64 CPU (32 cores)",
10+ "cpu_cores": 32,
11+ "software_name": "python",
12+ "software_version": "3.14.7",
13+ "python_version": "Python 3.11.6",
14+ "gcc_version": "12.3.1"
15+ },
16+ "benchmarks": {
17+ "primary": {
18+ "benchmark": "pyperformance",
19+ "description": "CPython pyperformance benchmark suite (json_dumps,json_loads,nbody,telco,fannkuch,regex_v8,meteor_contest) on ARM64",
20+ "reference": "https://github.com/python/pyperformance",
21+ "software": "python",
22+ "version": "3.14.7",
23+ "architecture": "arm64",
24+ "timestamp": "2026-08-10T06:26:38Z",
25+ "performance_metrics": {
26+ "mean_ms": {
27+ "unit": "ms",
28+ "description": "Mean execution time"
29+ },
30+ "ops_per_sec": {
31+ "unit": "ops/sec",
32+ "description": "Operations per second (1/mean)"
33+ }
34+ },
35+ "parameters": {
36+ "bench_tests": "json_dumps,json_loads,nbody,telco,fannkuch,regex_v8,meteor_contest",
37+ "iterations": 1
38+ },
39+ "results_summary": {}
40+ },
41+ "micro": {
42+ "benchmark": "micro_operations",
43+ "description": "CPython micro: timeit operations + GIL thread scaling on ARM64",
44+ "reference": "https://github.com/python/cpython",
45+ "software": "python",
46+ "version": "3.14.7",
47+ "architecture": "arm64",
48+ "timestamp": "2026-08-10T06:27:31Z",
49+ "performance_metrics": {
50+ "ops_per_sec": {
51+ "unit": "ops/sec",
52+ "description": "Operations per second"
53+ },
54+ "seconds_per_op": {
55+ "unit": "s",
56+ "description": "Seconds per operation"
57+ }
58+ },
59+ "parameters": {
60+ "micro_ops": [
61+ "list_comprehension",
62+ "dict_get",
63+ "string_concat",
64+ "json_parse"
65+ ],
66+ "thread_counts": [
67+ "1",
68+ "2",
69+ "4",
70+ "8",
71+ "all"
72+ ],
73+ "max_threads": 32,
74+ "iterations": 1
75+ },
76+ "results": {
77+ "micro_ops": {
78+ "list_comprehension": {
79+ "size_1000": {
80+ "seconds_per_op": 5.47e-05,
81+ "ops_per_sec": 18280.02
82+ },
83+ "size_10000": {
84+ "seconds_per_op": 0.00060317,
85+ "ops_per_sec": 1657.92
86+ },
87+ "size_100000": {
88+ "seconds_per_op": 0.00786819,
89+ "ops_per_sec": 127.09
90+ }
91+ },
92+ "dict_get": {
93+ "size_1000": {
94+ "seconds_per_op": 6e-08,
95+ "ops_per_sec": 15979608.81
96+ },
97+ "size_10000": {
98+ "seconds_per_op": 6e-08,
99+ "ops_per_sec": 15757993.88
100+ },
101+ "size_100000": {
102+ "seconds_per_op": 6e-08,
103+ "ops_per_sec": 16103027.53
104+ }
105+ },
106+ "string_concat": {
107+ "size_100": {
108+ "seconds_per_op": 0.0,
109+ "ops_per_sec": 0
110+ },
111+ "size_1000": {
112+ "seconds_per_op": 0.0,
113+ "ops_per_sec": 0
114+ },
115+ "size_10000": {
116+ "seconds_per_op": 0.0,
117+ "ops_per_sec": 0
118+ }
119+ },
120+ "json_parse": {
121+ "size_1000": {
122+ "seconds_per_op": 0.0,
123+ "ops_per_sec": 0
124+ },
125+ "size_10000": {
126+ "seconds_per_op": 0.0,
127+ "ops_per_sec": 0
128+ }
129+ }
130+ },
131+ "thread_scaling": {
132+ "threads_1": {
133+ "seconds_per_op": 0.00060386,
134+ "ops_per_sec": 1656.0
135+ },
136+ "threads_2": {
137+ "seconds_per_op": 0.00060667,
138+ "ops_per_sec": 1648.35
139+ },
140+ "threads_4": {
141+ "seconds_per_op": 0.0006026,
142+ "ops_per_sec": 1659.47
143+ },
144+ "threads_8": {
145+ "seconds_per_op": 0.00060183,
146+ "ops_per_sec": 1661.61
147+ },
148+ "threads_all": {
149+ "seconds_per_op": 0.00060128,
150+ "ops_per_sec": 1663.11
151+ }
152+ }
153+ }
154+ }
155+ },
156+ "summary": {},
157+ "software": "python",
158+ "version": "3.14.7"
159+}
Atests/python/results/3.14.7/results.txt+49-0
@@ -0,0 +1,49 @@
1+======================================================================
2+ CPython Performance Benchmark Report
3+======================================================================
4+ Generated: 2026-08-10 06:27:31 UTC
5+ Test Time: 2026-08-10T06:26:35Z
6+ 
7+ --- Environment ---
8+ Architecture: aarch64
9+ Model: Kunpeng-920
10+ CPU Model: ARM64 CPU (32 cores)
11+ CPU Cores: 32
12+ Python Version: 3.14.7
13+ OS: openEuler 24.03 SP3
14+ Kernel: 6.6.0-132.0.0.111.oe2403sp3.aarch64
15+ 
16+ --- pyperformance Benchmarks (Primary) ---
17+ Description: CPython pyperformance benchmark suite (json_dumps,json_loads,nbody,telco,fannkuch,regex_v8,meteor_contest) on ARM64
18+ Tests: json_dumps,json_loads,nbody,telco,fannkuch,regex_v8,meteor_contest
19+ 
20+ benchmark mean (ms) ops/sec min (ms)
21+ ----------------------------------------------------------------
22+ 
23+ --- Micro Benchmarks ---
24+ Micro operations (timeit):
25+ [dict_get]
26+ size_1000 : 15979608.81 ops/sec
27+ size_10000 : 15757993.88 ops/sec
28+ size_100000 : 16103027.53 ops/sec
29+ [json_parse]
30+ size_1000 : 0 ops/sec
31+ size_10000 : 0 ops/sec
32+ [list_comprehension]
33+ size_1000 : 18280.02 ops/sec
34+ size_10000 : 1657.92 ops/sec
35+ size_100000 : 127.09 ops/sec
36+ [string_concat]
37+ size_100 : 0 ops/sec
38+ size_1000 : 0 ops/sec
39+ size_10000 : 0 ops/sec
40+ Thread scaling (GIL limited):
41+ threads_all : 1663.11 ops/sec
42+ threads_1 : 1656.0 ops/sec
43+ threads_2 : 1648.35 ops/sec
44+ threads_4 : 1659.47 ops/sec
45+ threads_8 : 1661.61 ops/sec
46+ 
47+======================================================================
48+ Report generated by CPython Performance Benchmark Workflow
49+======================================================================
Atests/python/results/3.14.7/version_info.json+13-0
@@ -0,0 +1,13 @@
1+{
2+ "test_time": "2026-08-10T06:26:35Z",
3+ "Model": "Kunpeng-920",
4+ "architecture": "aarch64",
5+ "kernel": "6.6.0-132.0.0.111.oe2403sp3.aarch64",
6+ "os": "openEuler 24.03 SP3",
7+ "cpu_model": "ARM64 CPU (32 cores)",
8+ "cpu_cores": 32,
9+ "software_name": "python",
10+ "software_version": "3.14.7",
11+ "python_version": "Python 3.11.6",
12+ "gcc_version": "12.3.1"
13+}
Atests/python/scripts/aggregate_results.py+97-0
@@ -0,0 +1,97 @@
1+#!/usr/bin/env python3
2+import json
3+import os
4+import sys
5+from datetime import datetime, timezone
6+ 
7+ 
8+def safe_float(val, default=0.0):
9+ try:
10+ return float(val)
11+ except (ValueError, TypeError):
12+ return default
13+ 
14+ 
15+def compute_summary(primary, micro):
16+ summary = {}
17+ rs = primary.get("results_summary", {})
18+ ops_vals = [safe_float(v.get("ops_per_sec", 0)) for v in rs.values()
19+ if isinstance(v, dict) and v.get("ops_per_sec")]
20+ if ops_vals:
21+ summary["avg_ops_per_sec"] = round(sum(ops_vals) / len(ops_vals), 2)
22+ summary["max_ops_per_sec"] = round(max(ops_vals), 2)
23+ if rs:
24+ first_key = next(iter(rs))
25+ first_val = rs[first_key]
26+ if isinstance(first_val, dict):
27+ if first_val.get("ns_per_op"):
28+ ns_vals = [safe_float(v.get("ns_per_op", 0)) for v in rs.values() if isinstance(v, dict) and v.get("ns_per_op")]
29+ if ns_vals:
30+ summary["avg_ns_per_op"] = round(sum(ns_vals) / len(ns_vals), 2)
31+ summary["min_ns_per_op"] = round(min(ns_vals), 2)
32+ elif first_val.get("mean_ms"):
33+ mean_vals = [safe_float(v.get("mean_ms", 0)) for v in rs.values() if isinstance(v, dict) and v.get("mean_ms")]
34+ if mean_vals:
35+ summary["avg_mean_ms"] = round(sum(mean_vals) / len(mean_vals), 4)
36+ summary["min_mean_ms"] = round(min(mean_vals), 4)
37+ mresults = micro.get("results", {})
38+ if isinstance(mresults, dict):
39+ ts = mresults.get("thread_scaling", {})
40+ if isinstance(ts, dict) and ts:
41+ first_item = next(iter(ts.values()), {})
42+ if isinstance(first_item, dict):
43+ one_label = "threads_1" if "threads_1" in first_item else "cpu_1"
44+ one = safe_float(first_item.get(one_label, {}).get("ops_per_sec", 0)) if isinstance(first_item.get(one_label), dict) else 0
45+ if one > 0:
46+ max_t = os.cpu_count() or 4
47+ all_label = f"threads_all" if "threads_all" in first_item else f"cpu_{max_t}"
48+ allq = safe_float(first_item.get(all_label, {}).get("ops_per_sec", 0)) if isinstance(first_item.get(all_label), dict) else 0
49+ if allq > 0:
50+ summary["thread_scaling_ratio"] = round(allq / one, 2)
51+ return summary
52+ 
53+ 
54+def aggregate_results(results_dir, output_file):
55+ primary = {}
56+ micro = {}
57+ version_info = {}
58+ bench_files = {"benchmark_go.json": "primary", "benchmark_py.json": "primary",
59+ "micro_benchmark.json": "micro"}
60+ for fname, key in bench_files.items():
61+ path = os.path.join(results_dir, fname)
62+ if os.path.exists(path):
63+ with open(path) as f:
64+ data = json.load(f)
65+ if key == "primary" and not primary:
66+ primary = data
67+ elif key == "micro" and not micro:
68+ micro = data
69+ print(f"[AGGREGATE] Loaded {key} from {path}")
70+ env_path = os.path.join(results_dir, "version_info.json")
71+ if os.path.exists(env_path):
72+ with open(env_path) as f:
73+ version_info = json.load(f)
74+ summary = compute_summary(primary, micro)
75+ sw_name = version_info.get("software_name", "unknown")
76+ sw_ver = version_info.get("software_version", "unknown")
77+ result = {
78+ "test_time": version_info.get("test_time", version_info.get("timestamp",
79+ datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"))),
80+ "environment": version_info,
81+ "benchmarks": {"primary": primary, "micro": micro},
82+ "summary": summary,
83+ "software": sw_name,
84+ "version": sw_ver,
85+ }
86+ os.makedirs(os.path.dirname(os.path.abspath(output_file)) or ".", exist_ok=True)
87+ with open(output_file, "w") as f:
88+ json.dump(result, f, indent=2)
89+ print(f"[AGGREGATE] Aggregated results saved to {output_file}")
90+ return result
91+ 
92+ 
93+if __name__ == "__main__":
94+ if len(sys.argv) < 3:
95+ print("Usage: aggregate_results.py <results_dir> <output_file>")
96+ sys.exit(1)
97+ aggregate_results(sys.argv[1], sys.argv[2])
Atests/python/scripts/benchmark_py.py+126-0
@@ -0,0 +1,126 @@
1+#!/usr/bin/env python3
2+import subprocess
3+import sys
4+import os
5+import json
6+from datetime import datetime, timezone
7+ 
8+BENCH_TESTS = "json_dumps,json_loads,nbody,telco,fannkuch,regex_v8,meteor_contest"
9+ 
10+ 
11+def install_pyperformance(python_bin):
12+ result = subprocess.run(
13+ [python_bin, "-m", "pip", "install", "--break-system-packages", "pyperformance"],
14+ capture_output=True, text=True, timeout=300,
15+ )
16+ if result.returncode != 0:
17+ print(f"[BENCHMARK_PY] Failed to install pyperformance: {result.stderr[:500]}")
18+ return False
19+ print("[BENCHMARK_PY] pyperformance installed")
20+ return True
21+ 
22+ 
23+def run_pyperformance(python_bin, output_file, bench_tests=None):
24+ cmd = [python_bin, "-m", "pyperformance", "run", f"--output={output_file}", "--inherit-environ"]
25+ if bench_tests:
26+ cmd.append(f"--tests={bench_tests}")
27+ print(f"[BENCHMARK_PY] Running pyperformance: {' '.join(cmd)}")
28+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=1800)
29+ if result.returncode != 0:
30+ print(f"[BENCHMARK_PY][DEBUG] pyperformance failed: {result.stderr[:1000]}")
31+ print(f"[BENCHMARK_PY][DEBUG] stdout: {result.stdout[-1000:]}")
32+ return result.returncode == 0
33+ 
34+ 
35+def parse_pyperformance_json(json_path):
36+ with open(json_path) as f:
37+ data = json.load(f)
38+ 
39+ results = {}
40+ bench_list = data.get("benchmarks", data.get("results", []))
41+ for bench in bench_list:
42+ if not isinstance(bench, dict):
43+ continue
44+ name = bench.get("name", bench.get("benchmark", "unknown"))
45+ stats = bench.get("stats", bench.get("result", {}))
46+ if not isinstance(stats, dict):
47+ continue
48+ mean = float(stats.get("mean", stats.get("avg", 0)))
49+ median = float(stats.get("median", 0))
50+ stddev = float(stats.get("stddev", 0))
51+ min_val = float(stats.get("min", 0))
52+ ops_per_sec = round(1.0 / mean, 2) if mean > 0 else 0
53+ results[name] = {
54+ "mean_ms": round(mean * 1000, 4),
55+ "median_ms": round(median * 1000, 4) if median else 0,
56+ "stddev_ms": round(stddev * 1000, 4) if stddev else 0,
57+ "min_ms": round(min_val * 1000, 4) if min_val else 0,
58+ "ops_per_sec": ops_per_sec,
59+ }
60+ return results
61+ 
62+ 
63+def main():
64+ if len(sys.argv) < 4:
65+ print("Usage: benchmark_py.py <python_bin> <output_file> [iterations]")
66+ sys.exit(1)
67+ python_bin = sys.argv[1]
68+ output_file = sys.argv[2]
69+ iterations = int(sys.argv[3]) if len(sys.argv) >= 4 else 1
70+ 
71+ if not os.path.exists(python_bin):
72+ print(f"[BENCHMARK_PY] Python binary not found: {python_bin}")
73+ sys.exit(1)
74+ 
75+ version_str = os.environ.get("SOFTWARE_VERSION", "3.14.7")
76+ 
77+ if not install_pyperformance(python_bin):
78+ sys.exit(1)
79+ 
80+ raw_json = output_file.replace(".json", "_raw.json")
81+ all_results = {}
82+ 
83+ for it in range(iterations):
84+ print(f"[BENCHMARK_PY] Iteration {it+1}/{iterations}")
85+ if not run_pyperformance(python_bin, raw_json, BENCH_TESTS):
86+ print(f"[BENCHMARK_PY] Iteration {it+1} failed, trying minimal set...")
87+ if not run_pyperformance(python_bin, raw_json, "json_dumps,nbody"):
88+ print("[BENCHMARK_PY] Minimal set also failed")
89+ continue
90+ if os.path.exists(raw_json):
91+ parsed = parse_pyperformance_json(raw_json)
92+ for name, res in parsed.items():
93+ if name not in all_results:
94+ all_results[name] = res
95+ else:
96+ for key in ["mean_ms", "ops_per_sec"]:
97+ vals = [all_results[name][key], res[key]]
98+ all_results[name][key] = round(sum(vals) / len(vals), 4)
99+ 
100+ out = {
101+ "benchmark": "pyperformance",
102+ "description": f"CPython pyperformance benchmark suite ({BENCH_TESTS}) on ARM64",
103+ "reference": "https://github.com/python/pyperformance",
104+ "software": "python",
105+ "version": version_str,
106+ "architecture": "arm64",
107+ "timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
108+ "performance_metrics": {
109+ "mean_ms": {"unit": "ms", "description": "Mean execution time"},
110+ "ops_per_sec": {"unit": "ops/sec", "description": "Operations per second (1/mean)"},
111+ },
112+ "parameters": {
113+ "bench_tests": BENCH_TESTS,
114+ "iterations": iterations,
115+ },
116+ "results_summary": all_results,
117+ }
118+ with open(output_file, "w") as f:
119+ json.dump(out, f, indent=2)
120+ print(f"[BENCHMARK_PY] Output written to {output_file} ({len(all_results)} benchmarks)")
121+ for name, res in sorted(all_results.items()):
122+ print(f" {name}: {res.get('mean_ms', 'N/A')}ms, {res.get('ops_per_sec', 'N/A')} ops/sec")
123+ 
124+ 
125+if __name__ == "__main__":
126+ main()
Atests/python/scripts/generate_summary.py+92-0
@@ -0,0 +1,92 @@
1+#!/usr/bin/env python3
2+import sys
3+import json
4+from datetime import datetime, timezone
5+ 
6+ 
7+def generate_summary(input_json, output_file):
8+ with open(input_json) as f:
9+ data = json.load(f)
10+ lines = []
11+ lines.append("=" * 70)
12+ lines.append(" CPython Performance Benchmark Report")
13+ lines.append("=" * 70)
14+ lines.append(f" Generated: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')}")
15+ lines.append(f" Test Time: {data.get('test_time', data.get('timestamp', 'N/A'))}")
16+ lines.append("")
17+ env = data.get("environment", {})
18+ if env:
19+ lines.append(" --- Environment ---")
20+ lines.append(f" Architecture: {env.get('architecture', 'N/A')}")
21+ lines.append(f" Model: {env.get('Model', 'N/A')}")
22+ lines.append(f" CPU Model: {env.get('cpu_model', 'N/A')}")
23+ lines.append(f" CPU Cores: {env.get('cpu_cores', 'N/A')}")
24+ lines.append(f" Python Version: {env.get('software_version', 'N/A')}")
25+ lines.append(f" OS: {env.get('os', 'N/A')}")
26+ lines.append(f" Kernel: {env.get('kernel', 'N/A')}")
27+ lines.append("")
28+ benchmarks = data.get("benchmarks", {})
29+ primary = benchmarks.get("primary", {})
30+ if primary:
31+ lines.append(" --- pyperformance Benchmarks (Primary) ---")
32+ lines.append(f" Description: {primary.get('description', 'N/A')}")
33+ lines.append(f" Tests: {primary.get('parameters', {}).get('bench_tests', 'N/A')}")
34+ lines.append("")
35+ rs = primary.get("results_summary", {})
36+ header = " {:<25} {:>12} {:>12} {:>12}".format("benchmark", "mean (ms)", "ops/sec", "min (ms)")
37+ lines.append(header)
38+ lines.append(" " + "-" * (len(header) - 4))
39+ for name in sorted(rs.keys()):
40+ e = rs[name]
41+ if isinstance(e, dict) and e:
42+ lines.append(" {:<25} {:>12.4f} {:>12.2f} {:>12.4f}".format(
43+ name[:25], e.get("mean_ms", 0) or 0, e.get("ops_per_sec", 0) or 0, e.get("min_ms", 0) or 0))
44+ lines.append("")
45+ micro = benchmarks.get("micro", {})
46+ if micro:
47+ lines.append(" --- Micro Benchmarks ---")
48+ mresults = micro.get("results", {})
49+ if isinstance(mresults, dict):
50+ mo = mresults.get("micro_ops", {})
51+ if isinstance(mo, dict) and mo:
52+ lines.append(" Micro operations (timeit):")
53+ for op_name in sorted(mo.keys()):
54+ op_data = mo[op_name]
55+ if not isinstance(op_data, dict):
56+ continue
57+ lines.append(f" [{op_name}]")
58+ for size_label in sorted(op_data.keys(), key=lambda x: int(''.join(c for c in x if c.isdigit()) or 0)):
59+ e = op_data[size_label]
60+ if isinstance(e, dict) and e:
61+ lines.append(f" {size_label:<12}: {e.get('ops_per_sec', 'N/A')} ops/sec")
62+ ts = mresults.get("thread_scaling", {})
63+ if isinstance(ts, dict) and ts:
64+ lines.append(" Thread scaling (GIL limited):")
65+ for tc in sorted(ts.keys(), key=lambda x: int(''.join(c for c in x if c.isdigit()) or 0)):
66+ e = ts[tc]
67+ if isinstance(e, dict) and e:
68+ lines.append(f" {tc:<14}: {e.get('ops_per_sec', 'N/A')} ops/sec")
69+ lines.append("")
70+ summary = data.get("summary", {})
71+ if summary:
72+ lines.append(" --- Overall Summary ---")
73+ if "avg_ops_per_sec" in summary: lines.append(f" Avg ops/sec: {summary['avg_ops_per_sec']}")
74+ if "max_ops_per_sec" in summary: lines.append(f" Max ops/sec: {summary['max_ops_per_sec']}")
75+ if "avg_mean_ms" in summary: lines.append(f" Avg mean: {summary['avg_mean_ms']} ms")
76+ if "min_mean_ms" in summary: lines.append(f" Min mean (fastest): {summary['min_mean_ms']} ms")
77+ if "thread_scaling_ratio" in summary: lines.append(f" Thread scaling: {summary['thread_scaling_ratio']}x (GIL limited)")
78+ lines.append("")
79+ lines.append("=" * 70)
80+ lines.append(" Report generated by CPython Performance Benchmark Workflow")
81+ lines.append("=" * 70)
82+ summary_text = "\n".join(lines)
83+ with open(output_file, "w") as f:
84+ f.write(summary_text)
85+ print(summary_text)
86+ 
87+ 
88+if __name__ == "__main__":
89+ if len(sys.argv) < 3:
90+ print("Usage: generate_summary.py <input_json> <output_file>")
91+ sys.exit(1)
92+ generate_summary(sys.argv[1], sys.argv[2])
Atests/python/scripts/json_helper.py+309-0
@@ -0,0 +1,309 @@
1+#!/usr/bin/env python3
2+import json
3+import sys
4+import os
5+from datetime import datetime, timezone
6+ 
7+ 
8+def load_json(filepath):
9+ with open(filepath, "r") as f:
10+ return json.load(f)
11+ 
12+ 
13+def save_json(filepath, data):
14+ with open(filepath, "w") as f:
15+ json.dump(data, f, indent=2, ensure_ascii=True)
16+ 
17+ 
18+def get_nested(data, keys):
19+ for key in keys:
20+ if isinstance(data, dict) and key in data:
21+ data = data[key]
22+ elif isinstance(data, list) and key.isdigit():
23+ data = data[int(key)]
24+ else:
25+ return None
26+ return data
27+ 
28+ 
29+def cmd_get(filepath, *keys):
30+ data = load_json(filepath)
31+ result = get_nested(data, keys)
32+ if result is None:
33+ print("NULL")
34+ else:
35+ print(result)
36+ 
37+ 
38+def cmd_field_exists(filepath, field):
39+ data = load_json(filepath)
40+ if isinstance(data, dict):
41+ print(1 if field in data else 0)
42+ elif isinstance(data, list):
43+ print(1 if any(field in item for item in data if isinstance(item, dict)) else 0)
44+ else:
45+ print(0)
46+ 
47+ 
48+def cmd_count_results(filepath):
49+ data = load_json(filepath)
50+ if isinstance(data, dict) and "results" in data:
51+ print(len(data["results"]))
52+ elif isinstance(data, dict) and "results_summary" in data:
53+ rs = data["results_summary"]
54+ if isinstance(rs, dict):
55+ print(len(rs))
56+ elif isinstance(rs, list):
57+ print(len(rs))
58+ else:
59+ print(0)
60+ elif isinstance(data, list):
61+ print(len(data))
62+ else:
63+ print(0)
64+ 
65+ 
66+def cmd_throughput_ge(filepath, threshold, *keys):
67+ data = load_json(filepath)
68+ value = get_nested(data, keys)
69+ if value is None:
70+ rs = data.get("results_summary", data.get("results", {}))
71+ if isinstance(rs, dict):
72+ for v in rs.values():
73+ if isinstance(v, dict):
74+ val = get_nested(v, keys)
75+ if val is not None:
76+ try:
77+ if float(val) >= float(threshold):
78+ print(1)
79+ return
80+ except (ValueError, TypeError):
81+ pass
82+ print(0)
83+ else:
84+ try:
85+ print(1 if float(value) >= float(threshold) else 0)
86+ except (ValueError, TypeError):
87+ print(0)
88+ 
89+ 
90+def cmd_latency_le(filepath, threshold, *keys):
91+ data = load_json(filepath)
92+ value = get_nested(data, keys)
93+ if value is None:
94+ rs = data.get("results_summary", data.get("results", {}))
95+ if isinstance(rs, dict):
96+ for v in rs.values():
97+ if isinstance(v, dict):
98+ val = get_nested(v, keys)
99+ if val is not None:
100+ try:
101+ if float(val) <= float(threshold):
102+ print(1)
103+ return
104+ except (ValueError, TypeError):
105+ pass
106+ print(0)
107+ else:
108+ try:
109+ print(1 if float(value) <= float(threshold) else 0)
110+ except (ValueError, TypeError):
111+ print(0)
112+ 
113+ 
114+def cmd_version(filepath):
115+ data = load_json(filepath)
116+ if isinstance(data, dict):
117+ ver = data.get("software_version", data.get("version", "unknown"))
118+ print(ver)
119+ else:
120+ print("unknown")
121+ 
122+ 
123+def cmd_contains(filepath, keyword):
124+ with open(filepath, "r") as f:
125+ content = f.read()
126+ print(1 if keyword in content else 0)
127+ 
128+ 
129+def cmd_avg_throughput(filepath, *keys):
130+ data = load_json(filepath)
131+ results = data.get("results", [])
132+ if not results:
133+ rs = data.get("results_summary", {})
134+ if isinstance(rs, dict):
135+ values = []
136+ for v in rs.values():
137+ if isinstance(v, dict):
138+ val = get_nested(v, keys)
139+ if val is not None:
140+ try:
141+ values.append(float(val))
142+ except (ValueError, TypeError):
143+ pass
144+ if values:
145+ print(sum(values) / len(values))
146+ else:
147+ print(0)
148+ return
149+ top_val = get_nested(data, keys)
150+ if top_val is not None:
151+ try:
152+ print(float(top_val))
153+ return
154+ except (ValueError, TypeError):
155+ pass
156+ print(0)
157+ return
158+ values = []
159+ for r in results:
160+ val = get_nested(r, keys)
161+ if val is not None:
162+ try:
163+ values.append(float(val))
164+ except (ValueError, TypeError):
165+ pass
166+ if values:
167+ print(sum(values) / len(values))
168+ else:
169+ print(0)
170+ 
171+ 
172+def cmd_max_latency(filepath, *keys):
173+ data = load_json(filepath)
174+ results = data.get("results", [])
175+ if not results:
176+ rs = data.get("results_summary", {})
177+ if isinstance(rs, dict):
178+ values = []
179+ for v in rs.values():
180+ if isinstance(v, dict):
181+ val = get_nested(v, keys)
182+ if val is not None:
183+ try:
184+ values.append(float(val))
185+ except (ValueError, TypeError):
186+ pass
187+ if values:
188+ print(max(values))
189+ else:
190+ print(0)
191+ return
192+ top_val = get_nested(data, keys)
193+ if top_val is not None:
194+ try:
195+ print(float(top_val))
196+ return
197+ except (ValueError, TypeError):
198+ pass
199+ print(0)
200+ return
201+ values = []
202+ for r in results:
203+ val = get_nested(r, keys)
204+ if val is not None:
205+ try:
206+ values.append(float(val))
207+ except (ValueError, TypeError):
208+ pass
209+ if values:
210+ print(max(values))
211+ else:
212+ print(0)
213+ 
214+ 
215+def cmd_write_version_info(filepath, timestamp, model, arch, kernel, os_name, cpu_model,
216+ cores, sw_name, sw_version, python_ver, compiler_ver):
217+ data = {
218+ "test_time": str(timestamp),
219+ "Model": str(model),
220+ "architecture": str(arch),
221+ "kernel": str(kernel),
222+ "os": str(os_name),
223+ "cpu_model": str(cpu_model),
224+ "cpu_cores": int(cores),
225+ "software_name": str(sw_name),
226+ "software_version": str(sw_version),
227+ "python_version": str(python_ver),
228+ "gcc_version": str(compiler_ver)
229+ }
230+ save_json(filepath, data)
231+ 
232+ 
233+def cmd_write_build_info(filepath, timestamp, os_id, os_name, arch, kernel,
234+ gcc_ver, cmake_ver, swig_ver, faiss_ver, build_method):
235+ data = {
236+ "timestamp": str(timestamp),
237+ "os_id": str(os_id),
238+ "os_name": str(os_name),
239+ "architecture": str(arch),
240+ "kernel": str(kernel),
241+ "gcc_version": str(gcc_ver),
242+ "cmake_version": str(cmake_ver),
243+ "swig_version": str(swig_ver),
244+ "faiss_version": str(faiss_ver),
245+ "build_method": str(build_method)
246+ }
247+ save_json(filepath, data)
248+ 
249+ 
250+def cmd_merge_jsons(output_path, *input_paths):
251+ merged = {
252+ "software_name": "faiss",
253+ "primary_benchmark": {},
254+ "secondary_benchmark": {},
255+ "micro_benchmark": {},
256+ "environment": {}
257+ }
258+ for ip in input_paths:
259+ if os.path.exists(ip):
260+ data = load_json(ip)
261+ if isinstance(data, dict):
262+ bench_name = data.get("benchmark", "")
263+ if bench_name in ("ann_search", "ANN"):
264+ merged["primary_benchmark"] = data
265+ elif bench_name in ("micro_operations", "Micro-Benchmarks"):
266+ merged["micro_benchmark"] = data
267+ env_file = None
268+ for ip in input_paths:
269+ if os.path.exists(ip) and "version_info" in ip:
270+ env_file = ip
271+ break
272+ if env_file:
273+ merged["environment"] = load_json(env_file)
274+ save_json(output_path, merged)
275+ 
276+ 
277+def main():
278+ if len(sys.argv) < 3:
279+ print("Usage: json_helper.py <filepath> <command> [args...]")
280+ sys.exit(1)
281+ 
282+ filepath = sys.argv[1]
283+ command = sys.argv[2]
284+ args = sys.argv[3:]
285+ 
286+ commands = {
287+ "get": lambda: cmd_get(filepath, *args),
288+ "field_exists": lambda: cmd_field_exists(filepath, args[0] if args else ""),
289+ "count_results": lambda: cmd_count_results(filepath),
290+ "throughput_ge": lambda: cmd_throughput_ge(filepath, args[0], *args[1:]),
291+ "latency_le": lambda: cmd_latency_le(filepath, args[0], *args[1:]),
292+ "version": lambda: cmd_version(filepath),
293+ "contains": lambda: cmd_contains(filepath, args[0] if args else ""),
294+ "write_version_info": lambda: cmd_write_version_info(filepath, *args),
295+ "write_build_info": lambda: cmd_write_build_info(filepath, *args),
296+ "avg_throughput": lambda: cmd_avg_throughput(filepath, *args),
297+ "max_latency": lambda: cmd_max_latency(filepath, *args),
298+ "merge_jsons": lambda: cmd_merge_jsons(filepath, *args),
299+ }
300+ 
301+ if command not in commands:
302+ print(f"Unknown command: {command}")
303+ sys.exit(1)
304+ 
305+ commands[command]()
306+ 
307+ 
308+if __name__ == "__main__":
309+ main()
Atests/python/scripts/micro_benchmark.py+141-0
@@ -0,0 +1,141 @@
1+#!/usr/bin/env python3
2+import subprocess
3+import sys
4+import os
5+import json
6+from datetime import datetime, timezone
7+ 
8+MICRO_OPS = [
9+ ("list_comprehension", "[i*2 for i in range({n})]", [1000, 10000, 100000]),
10+ ("dict_get", "d.get(0)", [1000, 10000, 100000]),
11+ ("string_concat", "s + 'x'", [100, 1000, 10000]),
12+ ("json_parse", "json.loads(j)", [1000, 10000]),
13+]
14+ 
15+THREAD_COUNTS = [1, 2, 4, 8, "all"]
16+ 
17+ 
18+def get_max_threads():
19+ try:
20+ return int(os.cpu_count() or 4)
21+ except Exception:
22+ return 4
23+ 
24+ 
25+def run_timeit(python_bin, stmt, setup, number, repeat=5):
26+ cmd = [
27+ python_bin, "-c",
28+ f"import timeit; t = timeit.repeat('{stmt}', '{setup}', number={number}, repeat={repeat}); "
29+ f"print(min(t) / {number})",
30+ ]
31+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
32+ if result.returncode != 0:
33+ print(f"[MICRO][DEBUG] timeit failed: {result.stderr[:200]}")
34+ return 0.0
35+ try:
36+ return float(result.stdout.strip())
37+ except ValueError:
38+ return 0.0
39+ 
40+ 
41+def bench_micro_ops(python_bin):
42+ results = {}
43+ for name, stmt_tmpl, sizes in MICRO_OPS:
44+ size_results = {}
45+ for n in sizes:
46+ stmt = stmt_tmpl.format(n=n)
47+ setup = ""
48+ if "dict_get" in name:
49+ setup = f"d = {{i: i for i in range({n})}}"
50+ elif "string_concat" in name:
51+ setup = f"s = 'x' * {n}"
52+ elif "json_parse" in name:
53+ setup = "import json; import json as j_module; j = json.dumps({{str(i): i for i in range(100)}}) * {n}".replace("{n}", str(min(n, 100)))
54+ stmt = "json.loads(j)"
55+ seconds_per_op = run_timeit(python_bin, stmt, setup, number=1000)
56+ ops_per_sec = round(1.0 / seconds_per_op, 2) if seconds_per_op > 0 else 0
57+ size_results[f"size_{n}"] = {
58+ "seconds_per_op": round(seconds_per_op, 8),
59+ "ops_per_sec": ops_per_sec,
60+ }
61+ print(f"[MICRO] {name} size_{n}: {ops_per_sec} ops/sec")
62+ results[name] = size_results
63+ return results
64+ 
65+ 
66+def bench_thread_scaling(python_bin):
67+ max_threads = get_max_threads()
68+ results = {}
69+ for tc in THREAD_COUNTS:
70+ actual = tc if tc != "all" else max_threads
71+ label = f"threads_{tc}"
72+ stmt = "[i*2 for i in range(10000)]"
73+ env = os.environ.copy()
74+ env["OMP_NUM_THREADS"] = str(actual)
75+ cmd = [
76+ python_bin, "-c",
77+ f"import timeit; t = timeit.repeat('{stmt}', number=1000, repeat=3); "
78+ f"print(min(t) / 1000)",
79+ ]
80+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=60, env=env)
81+ try:
82+ sec = float(result.stdout.strip())
83+ ops = round(1.0 / sec, 2) if sec > 0 else 0
84+ except ValueError:
85+ sec, ops = 0, 0
86+ results[label] = {"seconds_per_op": round(sec, 8), "ops_per_sec": ops}
87+ print(f"[MICRO] {label}: {ops} ops/sec")
88+ return results
89+ 
90+ 
91+def main():
92+ if len(sys.argv) < 4:
93+ print("Usage: micro_benchmark.py <python_bin> <output_file> [iterations]")
94+ sys.exit(1)
95+ python_bin = sys.argv[1]
96+ output_file = sys.argv[2]
97+ iterations = int(sys.argv[3]) if len(sys.argv) >= 4 else 1
98+ 
99+ if not os.path.exists(python_bin):
100+ print(f"[MICRO] Python binary not found: {python_bin}")
101+ sys.exit(1)
102+ 
103+ version_str = os.environ.get("SOFTWARE_VERSION", "3.14.7")
104+ max_threads = get_max_threads()
105+ 
106+ print("[MICRO] Running micro_ops...")
107+ ops_results = bench_micro_ops(python_bin)
108+ 
109+ print("[MICRO] Running thread_scaling...")
110+ ts_results = bench_thread_scaling(python_bin)
111+ 
112+ out = {
113+ "benchmark": "micro_operations",
114+ "description": f"CPython micro: timeit operations + GIL thread scaling on ARM64",
115+ "reference": "https://github.com/python/cpython",
116+ "software": "python",
117+ "version": version_str,
118+ "architecture": "arm64",
119+ "timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
120+ "performance_metrics": {
121+ "ops_per_sec": {"unit": "ops/sec", "description": "Operations per second"},
122+ "seconds_per_op": {"unit": "s", "description": "Seconds per operation"},
123+ },
124+ "parameters": {
125+ "micro_ops": [name for name, _, _ in MICRO_OPS],
126+ "thread_counts": [str(t) for t in THREAD_COUNTS],
127+ "max_threads": max_threads,
128+ "iterations": iterations,
129+ },
130+ "results": {
131+ "micro_ops": ops_results,
132+ "thread_scaling": ts_results,
133+ },
134+ }
135+ with open(output_file, "w") as f:
136+ json.dump(out, f, indent=2)
137+ print(f"[MICRO] Output written to {output_file}")
138+ 
139+ 
140+if __name__ == "__main__":
141+ main()