pgsi-analyzer 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pgsi_analyzer/__init__.py +24 -0
- pgsi_analyzer/benchmark/__init__.py +19 -0
- pgsi_analyzer/benchmark/builder.py +265 -0
- pgsi_analyzer/benchmark/executor.py +505 -0
- pgsi_analyzer/benchmark/orchestrator.py +413 -0
- pgsi_analyzer/benchmark/provider.py +128 -0
- pgsi_analyzer/benchmark/results_collector.py +109 -0
- pgsi_analyzer/benchmarks/__init__.py +15 -0
- pgsi_analyzer/benchmarks/binary-trees/README.md +85 -0
- pgsi_analyzer/benchmarks/binary-trees/cpython/main.py +104 -0
- pgsi_analyzer/benchmarks/binary-trees/ctypes/binary_tree.c +37 -0
- pgsi_analyzer/benchmarks/binary-trees/ctypes/main.py +73 -0
- pgsi_analyzer/benchmarks/binary-trees/cython/main.py +19 -0
- pgsi_analyzer/benchmarks/binary-trees/cython/raw.c +8855 -0
- pgsi_analyzer/benchmarks/binary-trees/cython/raw.pyx +88 -0
- pgsi_analyzer/benchmarks/binary-trees/cython/setup.py +9 -0
- pgsi_analyzer/benchmarks/binary-trees/py_compile/__compiler.py +8 -0
- pgsi_analyzer/benchmarks/binary-trees/py_compile/main.py +105 -0
- pgsi_analyzer/benchmarks/binary-trees/pypy/main.py +105 -0
- pgsi_analyzer/benchmarks/discovery.py +123 -0
- pgsi_analyzer/benchmarks/fannkuch-redux/README.md +116 -0
- pgsi_analyzer/benchmarks/fannkuch-redux/cpython/main.py +96 -0
- pgsi_analyzer/benchmarks/fannkuch-redux/ctypes/fannkuch.c +68 -0
- pgsi_analyzer/benchmarks/fannkuch-redux/ctypes/main.py +51 -0
- pgsi_analyzer/benchmarks/fannkuch-redux/cython/main.py +28 -0
- pgsi_analyzer/benchmarks/fannkuch-redux/cython/raw.c +13311 -0
- pgsi_analyzer/benchmarks/fannkuch-redux/cython/raw.pyx +58 -0
- pgsi_analyzer/benchmarks/fannkuch-redux/cython/setup.py +17 -0
- pgsi_analyzer/benchmarks/fannkuch-redux/py_compile/__compiler.py +8 -0
- pgsi_analyzer/benchmarks/fannkuch-redux/py_compile/main.py +96 -0
- pgsi_analyzer/benchmarks/fannkuch-redux/pypy/main.py +96 -0
- pgsi_analyzer/benchmarks/fasta/README.md +61 -0
- pgsi_analyzer/benchmarks/fasta/cpython/main.py +171 -0
- pgsi_analyzer/benchmarks/fasta/ctypes/fasta.c +99 -0
- pgsi_analyzer/benchmarks/fasta/ctypes/main.py +85 -0
- pgsi_analyzer/benchmarks/fasta/cython/main.py +48 -0
- pgsi_analyzer/benchmarks/fasta/cython/raw.c +14552 -0
- pgsi_analyzer/benchmarks/fasta/cython/raw.pyx +103 -0
- pgsi_analyzer/benchmarks/fasta/cython/setup.py +17 -0
- pgsi_analyzer/benchmarks/fasta/py_compile/__compiler.py +8 -0
- pgsi_analyzer/benchmarks/fasta/py_compile/main.py +171 -0
- pgsi_analyzer/benchmarks/fasta/pypy/main.py +172 -0
- pgsi_analyzer/benchmarks/hanoi/README.md +33 -0
- pgsi_analyzer/benchmarks/hanoi/cpython/main.py +91 -0
- pgsi_analyzer/benchmarks/hanoi/ctypes/hanoi.c +20 -0
- pgsi_analyzer/benchmarks/hanoi/ctypes/main.py +62 -0
- pgsi_analyzer/benchmarks/hanoi/cython/main.py +54 -0
- pgsi_analyzer/benchmarks/hanoi/cython/raw.c +8536 -0
- pgsi_analyzer/benchmarks/hanoi/cython/raw.pyx +22 -0
- pgsi_analyzer/benchmarks/hanoi/cython/setup.py +9 -0
- pgsi_analyzer/benchmarks/hanoi/py_compile/__compailer.py +8 -0
- pgsi_analyzer/benchmarks/hanoi/py_compile/main.py +91 -0
- pgsi_analyzer/benchmarks/hanoi/pypy/main.py +91 -0
- pgsi_analyzer/benchmarks/k-nucleotide/README.md +93 -0
- pgsi_analyzer/benchmarks/k-nucleotide/cpython/main.py +87 -0
- pgsi_analyzer/benchmarks/k-nucleotide/ctypes/kmer_counter.c +67 -0
- pgsi_analyzer/benchmarks/k-nucleotide/ctypes/main.py +101 -0
- pgsi_analyzer/benchmarks/k-nucleotide/cython/main.py +70 -0
- pgsi_analyzer/benchmarks/k-nucleotide/cython/raw.c +8560 -0
- pgsi_analyzer/benchmarks/k-nucleotide/cython/raw.pyx +35 -0
- pgsi_analyzer/benchmarks/k-nucleotide/cython/setup.py +9 -0
- pgsi_analyzer/benchmarks/k-nucleotide/dna.txt +8 -0
- pgsi_analyzer/benchmarks/k-nucleotide/py_compile/__compiler.py +8 -0
- pgsi_analyzer/benchmarks/k-nucleotide/py_compile/main.py +87 -0
- pgsi_analyzer/benchmarks/k-nucleotide/pypy/main.py +87 -0
- pgsi_analyzer/benchmarks/knn/README.md +47 -0
- pgsi_analyzer/benchmarks/knn/cpython/main.py +105 -0
- pgsi_analyzer/benchmarks/knn/ctypes/knn.c +43 -0
- pgsi_analyzer/benchmarks/knn/ctypes/main.py +76 -0
- pgsi_analyzer/benchmarks/knn/cython/main.py +49 -0
- pgsi_analyzer/benchmarks/knn/cython/raw.c +30444 -0
- pgsi_analyzer/benchmarks/knn/cython/raw.pyx +55 -0
- pgsi_analyzer/benchmarks/knn/cython/setup.py +9 -0
- pgsi_analyzer/benchmarks/knn/py_compile/__compiler.py +8 -0
- pgsi_analyzer/benchmarks/knn/py_compile/main.py +117 -0
- pgsi_analyzer/benchmarks/knn/pypy/main.py +117 -0
- pgsi_analyzer/benchmarks/mandelbrot/README.md +40 -0
- pgsi_analyzer/benchmarks/mandelbrot/cpython/main.py +127 -0
- pgsi_analyzer/benchmarks/mandelbrot/ctypes/main.py +70 -0
- pgsi_analyzer/benchmarks/mandelbrot/ctypes/mandelbrot.c +33 -0
- pgsi_analyzer/benchmarks/mandelbrot/cython/main.py +48 -0
- pgsi_analyzer/benchmarks/mandelbrot/cython/raw.c +8122 -0
- pgsi_analyzer/benchmarks/mandelbrot/cython/raw.pyx +36 -0
- pgsi_analyzer/benchmarks/mandelbrot/cython/setup.py +9 -0
- pgsi_analyzer/benchmarks/mandelbrot/py_compile/__compiler.py +8 -0
- pgsi_analyzer/benchmarks/mandelbrot/py_compile/main.py +126 -0
- pgsi_analyzer/benchmarks/mandelbrot/pypy/main.py +128 -0
- pgsi_analyzer/benchmarks/n-body/README.md +57 -0
- pgsi_analyzer/benchmarks/n-body/cpython/main.py +181 -0
- pgsi_analyzer/benchmarks/n-body/ctypes/main.py +100 -0
- pgsi_analyzer/benchmarks/n-body/ctypes/nbody.c +84 -0
- pgsi_analyzer/benchmarks/n-body/cython/main.py +76 -0
- pgsi_analyzer/benchmarks/n-body/cython/raw.c +30596 -0
- pgsi_analyzer/benchmarks/n-body/cython/raw.pyx +90 -0
- pgsi_analyzer/benchmarks/n-body/cython/setup.py +9 -0
- pgsi_analyzer/benchmarks/n-body/py_compile/__compiler.py +8 -0
- pgsi_analyzer/benchmarks/n-body/py_compile/main.py +180 -0
- pgsi_analyzer/benchmarks/n-body/pypy/main.py +180 -0
- pgsi_analyzer/benchmarks/n-queens/README.md +44 -0
- pgsi_analyzer/benchmarks/n-queens/cpython/main.py +131 -0
- pgsi_analyzer/benchmarks/n-queens/ctypes/main.py +66 -0
- pgsi_analyzer/benchmarks/n-queens/ctypes/n_queens.c +76 -0
- pgsi_analyzer/benchmarks/n-queens/cython/main.py +52 -0
- pgsi_analyzer/benchmarks/n-queens/cython/raw.c +27786 -0
- pgsi_analyzer/benchmarks/n-queens/cython/raw.pyx +64 -0
- pgsi_analyzer/benchmarks/n-queens/cython/setup.py +9 -0
- pgsi_analyzer/benchmarks/n-queens/py_compile/__compailer.py +8 -0
- pgsi_analyzer/benchmarks/n-queens/py_compile/main.py +131 -0
- pgsi_analyzer/benchmarks/n-queens/pypy/main.py +131 -0
- pgsi_analyzer/benchmarks/pi-digits/README.md +56 -0
- pgsi_analyzer/benchmarks/pi-digits/cpython/main.py +59 -0
- pgsi_analyzer/benchmarks/pi-digits/ctypes/main.py +52 -0
- pgsi_analyzer/benchmarks/pi-digits/ctypes/pi_gauss_legendre.c +19 -0
- pgsi_analyzer/benchmarks/pi-digits/cython/main.py +30 -0
- pgsi_analyzer/benchmarks/pi-digits/cython/raw.c +8000 -0
- pgsi_analyzer/benchmarks/pi-digits/cython/raw.pyx +28 -0
- pgsi_analyzer/benchmarks/pi-digits/cython/setup.py +9 -0
- pgsi_analyzer/benchmarks/pi-digits/py_compile/__compiler.py +8 -0
- pgsi_analyzer/benchmarks/pi-digits/py_compile/main.py +59 -0
- pgsi_analyzer/benchmarks/pi-digits/pypy/main.py +59 -0
- pgsi_analyzer/benchmarks/regex-redux/README.md +97 -0
- pgsi_analyzer/benchmarks/regex-redux/cpython/input_fasta.txt +2 -0
- pgsi_analyzer/benchmarks/regex-redux/cpython/main.py +116 -0
- pgsi_analyzer/benchmarks/regex-redux/ctypes/input_fasta.txt +2 -0
- pgsi_analyzer/benchmarks/regex-redux/ctypes/main.py +71 -0
- pgsi_analyzer/benchmarks/regex-redux/ctypes/regex_redux.c +110 -0
- pgsi_analyzer/benchmarks/regex-redux/cython/input_fasta.txt +2 -0
- pgsi_analyzer/benchmarks/regex-redux/cython/main.py +68 -0
- pgsi_analyzer/benchmarks/regex-redux/cython/raw.c +9999 -0
- pgsi_analyzer/benchmarks/regex-redux/cython/raw.pyx +84 -0
- pgsi_analyzer/benchmarks/regex-redux/cython/setup.py +9 -0
- pgsi_analyzer/benchmarks/regex-redux/py_compile/__compiler.py +8 -0
- pgsi_analyzer/benchmarks/regex-redux/py_compile/input_fasta.txt +2 -0
- pgsi_analyzer/benchmarks/regex-redux/py_compile/main.py +116 -0
- pgsi_analyzer/benchmarks/regex-redux/pypy/input_fasta.txt +2 -0
- pgsi_analyzer/benchmarks/regex-redux/pypy/main.py +113 -0
- pgsi_analyzer/benchmarks/registry.py +210 -0
- pgsi_analyzer/benchmarks/reverse-complement/README.md +93 -0
- pgsi_analyzer/benchmarks/reverse-complement/cpython/main.py +56 -0
- pgsi_analyzer/benchmarks/reverse-complement/ctypes/main.py +47 -0
- pgsi_analyzer/benchmarks/reverse-complement/ctypes/reverse_comlement.c +28 -0
- pgsi_analyzer/benchmarks/reverse-complement/cython/main.py +26 -0
- pgsi_analyzer/benchmarks/reverse-complement/cython/raw.c +8364 -0
- pgsi_analyzer/benchmarks/reverse-complement/cython/raw.pyx +39 -0
- pgsi_analyzer/benchmarks/reverse-complement/cython/setup.py +9 -0
- pgsi_analyzer/benchmarks/reverse-complement/py_compile/__compiler.py +8 -0
- pgsi_analyzer/benchmarks/reverse-complement/py_compile/main.py +56 -0
- pgsi_analyzer/benchmarks/reverse-complement/pypy/main.py +56 -0
- pgsi_analyzer/benchmarks/sieve/README.md +67 -0
- pgsi_analyzer/benchmarks/sieve/cpython/main.py +60 -0
- pgsi_analyzer/benchmarks/sieve/ctypes/main.py +53 -0
- pgsi_analyzer/benchmarks/sieve/ctypes/sieve.c +54 -0
- pgsi_analyzer/benchmarks/sieve/cython/main.py +24 -0
- pgsi_analyzer/benchmarks/sieve/cython/raw.c +8828 -0
- pgsi_analyzer/benchmarks/sieve/cython/raw.pyx +38 -0
- pgsi_analyzer/benchmarks/sieve/cython/setup.py +9 -0
- pgsi_analyzer/benchmarks/sieve/py_compile/__compiler.py +8 -0
- pgsi_analyzer/benchmarks/sieve/py_compile/main.py +60 -0
- pgsi_analyzer/benchmarks/sieve/pypy/main.py +60 -0
- pgsi_analyzer/benchmarks/spectral-norm/README.md +72 -0
- pgsi_analyzer/benchmarks/spectral-norm/cpython/main.py +83 -0
- pgsi_analyzer/benchmarks/spectral-norm/ctypes/main.py +49 -0
- pgsi_analyzer/benchmarks/spectral-norm/ctypes/spectralnorm.c +71 -0
- pgsi_analyzer/benchmarks/spectral-norm/cython/main.py +27 -0
- pgsi_analyzer/benchmarks/spectral-norm/cython/raw.c +9385 -0
- pgsi_analyzer/benchmarks/spectral-norm/cython/raw.pyx +57 -0
- pgsi_analyzer/benchmarks/spectral-norm/cython/setup.py +9 -0
- pgsi_analyzer/benchmarks/spectral-norm/py_compile/__compiler.py +8 -0
- pgsi_analyzer/benchmarks/spectral-norm/py_compile/main.py +83 -0
- pgsi_analyzer/benchmarks/spectral-norm/pypy/main.py +83 -0
- pgsi_analyzer/benchmarks/strassen/README.md +113 -0
- pgsi_analyzer/benchmarks/strassen/cpython/main.py +101 -0
- pgsi_analyzer/benchmarks/strassen/ctypes/main.py +83 -0
- pgsi_analyzer/benchmarks/strassen/ctypes/strassen.c +91 -0
- pgsi_analyzer/benchmarks/strassen/cython/main.py +53 -0
- pgsi_analyzer/benchmarks/strassen/cython/raw.c +10465 -0
- pgsi_analyzer/benchmarks/strassen/cython/raw.pyx +71 -0
- pgsi_analyzer/benchmarks/strassen/cython/setup.py +9 -0
- pgsi_analyzer/benchmarks/strassen/py_compile/__compiler.py +8 -0
- pgsi_analyzer/benchmarks/strassen/py_compile/main.py +101 -0
- pgsi_analyzer/benchmarks/strassen/pypy/main.py +102 -0
- pgsi_analyzer/benchmarks/template.py +349 -0
- pgsi_analyzer/cli/__init__.py +10 -0
- pgsi_analyzer/cli/main.py +478 -0
- pgsi_analyzer/config/cpu_power.csv +4904 -0
- pgsi_analyzer/config/cpu_power.source.json +15 -0
- pgsi_analyzer/config.py +300 -0
- pgsi_analyzer/gui/__init__.py +8 -0
- pgsi_analyzer/gui/app.py +1105 -0
- pgsi_analyzer/measurement/__init__.py +15 -0
- pgsi_analyzer/measurement/cpu_power_resolver.py +215 -0
- pgsi_analyzer/measurement/energy.py +296 -0
- pgsi_analyzer/measurement/estimators.py +315 -0
- pgsi_analyzer/measurement/time.py +138 -0
- pgsi_analyzer/models/__init__.py +24 -0
- pgsi_analyzer/models/aggregation.py +246 -0
- pgsi_analyzer/models/carbon.py +85 -0
- pgsi_analyzer/models/combination.py +201 -0
- pgsi_analyzer/models/greenscore.py +190 -0
- pgsi_analyzer/platform/__init__.py +26 -0
- pgsi_analyzer/platform/detection.py +102 -0
- pgsi_analyzer/platform/hardware.py +202 -0
- pgsi_analyzer/platform/paths.py +94 -0
- pgsi_analyzer/utils/__init__.py +37 -0
- pgsi_analyzer/utils/errors.py +28 -0
- pgsi_analyzer/utils/validation.py +46 -0
- pgsi_analyzer-1.0.0.dist-info/METADATA +552 -0
- pgsi_analyzer-1.0.0.dist-info/RECORD +212 -0
- pgsi_analyzer-1.0.0.dist-info/WHEEL +5 -0
- pgsi_analyzer-1.0.0.dist-info/entry_points.txt +3 -0
- pgsi_analyzer-1.0.0.dist-info/licenses/LICENSE +21 -0
- pgsi_analyzer-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pgsi-analyzer: A comprehensive benchmarking and analysis tool for comparing
|
|
3
|
+
energy consumption, execution time, and carbon footprint of Python execution methods.
|
|
4
|
+
|
|
5
|
+
This package provides tools for measuring and analyzing the sustainability
|
|
6
|
+
of different Python execution methods using the GreenScore metric.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
__version__ = "1.0.0"
|
|
10
|
+
__author__ = "Md Fatin Shadab Turja"
|
|
11
|
+
__license__ = "MIT"
|
|
12
|
+
|
|
13
|
+
# Package metadata
|
|
14
|
+
__all__ = [
|
|
15
|
+
"__version__",
|
|
16
|
+
"__author__",
|
|
17
|
+
"__license__",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
# Top-level imports will be added as modules are implemented
|
|
21
|
+
# from .measurement import measure_energy_to_csv, measure_time_to_csv
|
|
22
|
+
# from .models import calculate_greenscore, calculate_carbon_footprint
|
|
23
|
+
# from .platform import detect_platform, get_system_info
|
|
24
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Benchmark execution framework for pgsi-analyzer.
|
|
3
|
+
|
|
4
|
+
This module provides tools for building, executing, and orchestrating
|
|
5
|
+
benchmark runs across multiple Python execution methods.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .builder import build_benchmark, requires_build
|
|
9
|
+
from .executor import execute_benchmark
|
|
10
|
+
from .orchestrator import run_benchmark_suite
|
|
11
|
+
from .provider import FileSystemProvider
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"build_benchmark",
|
|
15
|
+
"requires_build",
|
|
16
|
+
"execute_benchmark",
|
|
17
|
+
"run_benchmark_suite",
|
|
18
|
+
"FileSystemProvider",
|
|
19
|
+
]
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Benchmark build system for Cython and ctypes benchmarks.
|
|
3
|
+
|
|
4
|
+
Handles compilation of Cython extensions and C shared libraries
|
|
5
|
+
before benchmark execution. Compilation is done separately from
|
|
6
|
+
measurement to ensure only execution time/energy is measured.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
import platform
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
from ..utils import ConfigurationError, PlatformError
|
|
16
|
+
from ..config import ToolPaths
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def requires_build(method: str) -> bool:
|
|
20
|
+
"""
|
|
21
|
+
Check if a method requires compilation before execution.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
method: Execution method name
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
True if method requires build step
|
|
28
|
+
"""
|
|
29
|
+
return method in ("cython", "ctypes")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def build_cython(
|
|
33
|
+
benchmark_path: Path,
|
|
34
|
+
build_dir: Optional[Path] = None,
|
|
35
|
+
tool_paths: Optional[ToolPaths] = None,
|
|
36
|
+
) -> Path:
|
|
37
|
+
"""
|
|
38
|
+
Build Cython extension module.
|
|
39
|
+
|
|
40
|
+
Runs: python setup.py build_ext --inplace
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
benchmark_path: Path to Cython benchmark directory (contains setup.py)
|
|
44
|
+
build_dir: Optional directory for build artifacts (defaults to benchmark_path)
|
|
45
|
+
tool_paths: Optional ToolPaths configuration for Python executable
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Path to benchmark directory (build artifacts are in-place)
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
ConfigurationError: If setup.py not found or build fails
|
|
52
|
+
"""
|
|
53
|
+
if build_dir is None:
|
|
54
|
+
build_dir = benchmark_path
|
|
55
|
+
|
|
56
|
+
setup_py = benchmark_path / "setup.py"
|
|
57
|
+
if not setup_py.exists():
|
|
58
|
+
raise ConfigurationError(f"setup.py not found in {benchmark_path}")
|
|
59
|
+
|
|
60
|
+
# Use configured Python or default
|
|
61
|
+
python_exe = str(tool_paths.python) if tool_paths else sys.executable
|
|
62
|
+
|
|
63
|
+
# Change to benchmark directory for build
|
|
64
|
+
original_cwd = Path.cwd()
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
# Build extension in-place
|
|
68
|
+
result = subprocess.run(
|
|
69
|
+
[python_exe, "setup.py", "build_ext", "--inplace"],
|
|
70
|
+
cwd=str(benchmark_path),
|
|
71
|
+
capture_output=True,
|
|
72
|
+
text=True,
|
|
73
|
+
timeout=300, # 5 minute timeout for compilation
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if result.returncode != 0:
|
|
77
|
+
raise ConfigurationError(
|
|
78
|
+
f"Cython build failed for {benchmark_path}:\n"
|
|
79
|
+
f"stdout: {result.stdout}\n"
|
|
80
|
+
f"stderr: {result.stderr}"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return benchmark_path
|
|
84
|
+
|
|
85
|
+
except subprocess.TimeoutExpired:
|
|
86
|
+
raise ConfigurationError(f"Cython build timed out for {benchmark_path}")
|
|
87
|
+
except Exception as e:
|
|
88
|
+
raise ConfigurationError(f"Cython build error for {benchmark_path}: {e}")
|
|
89
|
+
finally:
|
|
90
|
+
# Restore original working directory
|
|
91
|
+
import os
|
|
92
|
+
os.chdir(str(original_cwd))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def build_ctypes(
|
|
96
|
+
benchmark_path: Path,
|
|
97
|
+
build_dir: Optional[Path] = None,
|
|
98
|
+
tool_paths: Optional[ToolPaths] = None,
|
|
99
|
+
) -> Path:
|
|
100
|
+
"""
|
|
101
|
+
Build C shared library for ctypes benchmark.
|
|
102
|
+
|
|
103
|
+
Compiles .c files to .so (Linux) or .dll (Windows).
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
benchmark_path: Path to ctypes benchmark directory (contains .c files)
|
|
107
|
+
build_dir: Optional directory for build artifacts (defaults to benchmark_path)
|
|
108
|
+
tool_paths: Optional ToolPaths configuration for C compiler
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Path to benchmark directory (shared library is in-place)
|
|
112
|
+
|
|
113
|
+
Raises:
|
|
114
|
+
ConfigurationError: If compilation fails
|
|
115
|
+
PlatformError: If C compiler not available
|
|
116
|
+
"""
|
|
117
|
+
if build_dir is None:
|
|
118
|
+
build_dir = benchmark_path
|
|
119
|
+
|
|
120
|
+
# Find .c files (sorted for deterministic library name)
|
|
121
|
+
c_files = sorted(benchmark_path.glob("*.c"))
|
|
122
|
+
if not c_files:
|
|
123
|
+
raise ConfigurationError(f"No .c files found in {benchmark_path}")
|
|
124
|
+
|
|
125
|
+
# Determine output library name and extension
|
|
126
|
+
if platform.system() == "Windows":
|
|
127
|
+
lib_ext = ".dll"
|
|
128
|
+
# Use first .c file name as base
|
|
129
|
+
lib_name = c_files[0].stem
|
|
130
|
+
else:
|
|
131
|
+
lib_ext = ".so"
|
|
132
|
+
lib_name = f"lib{c_files[0].stem}"
|
|
133
|
+
|
|
134
|
+
lib_path = benchmark_path / f"{lib_name}{lib_ext}"
|
|
135
|
+
|
|
136
|
+
# Check if already compiled and up-to-date
|
|
137
|
+
if lib_path.exists():
|
|
138
|
+
# Check if .c files are newer than library
|
|
139
|
+
c_newer = any(c.stat().st_mtime > lib_path.stat().st_mtime for c in c_files)
|
|
140
|
+
if not c_newer:
|
|
141
|
+
return benchmark_path # Already built and up-to-date
|
|
142
|
+
|
|
143
|
+
# Determine compiler
|
|
144
|
+
if tool_paths and tool_paths.c_compiler:
|
|
145
|
+
compiler_exe = str(tool_paths.c_compiler)
|
|
146
|
+
else:
|
|
147
|
+
# Fallback to auto-detection
|
|
148
|
+
if platform.system() == "Windows":
|
|
149
|
+
compiler_exe = "gcc" # Will try cl.exe if gcc fails
|
|
150
|
+
else:
|
|
151
|
+
compiler_exe = "gcc"
|
|
152
|
+
|
|
153
|
+
# Compile command
|
|
154
|
+
if platform.system() == "Windows":
|
|
155
|
+
# Windows: use configured compiler or try gcc first, then cl.exe
|
|
156
|
+
compile_cmd = [
|
|
157
|
+
compiler_exe,
|
|
158
|
+
"-shared",
|
|
159
|
+
"-o", str(lib_path),
|
|
160
|
+
*[str(c) for c in c_files],
|
|
161
|
+
"-fPIC",
|
|
162
|
+
]
|
|
163
|
+
else:
|
|
164
|
+
# Linux/macOS: use gcc
|
|
165
|
+
compile_cmd = [
|
|
166
|
+
compiler_exe,
|
|
167
|
+
"-shared",
|
|
168
|
+
"-fPIC",
|
|
169
|
+
"-o", str(lib_path),
|
|
170
|
+
*[str(c) for c in c_files],
|
|
171
|
+
]
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
result = subprocess.run(
|
|
175
|
+
compile_cmd,
|
|
176
|
+
cwd=str(benchmark_path),
|
|
177
|
+
capture_output=True,
|
|
178
|
+
text=True,
|
|
179
|
+
timeout=300, # 5 minute timeout
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
if result.returncode != 0:
|
|
183
|
+
# If configured compiler failed, try fallback detection
|
|
184
|
+
if tool_paths and tool_paths.c_compiler:
|
|
185
|
+
# User configured compiler failed
|
|
186
|
+
raise ConfigurationError(
|
|
187
|
+
f"ctypes compilation failed with configured compiler '{compiler_exe}':\n"
|
|
188
|
+
f"Command: {' '.join(compile_cmd)}\n"
|
|
189
|
+
f"stdout: {result.stdout}\n"
|
|
190
|
+
f"stderr: {result.stderr}"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# Try alternative: check if gcc is available
|
|
194
|
+
gcc_check = subprocess.run(
|
|
195
|
+
["gcc", "--version"],
|
|
196
|
+
capture_output=True,
|
|
197
|
+
text=True,
|
|
198
|
+
)
|
|
199
|
+
if gcc_check.returncode != 0:
|
|
200
|
+
raise PlatformError(
|
|
201
|
+
"C compiler not found. Configure PGSI_CC_PATH, use --cc-path, "
|
|
202
|
+
"or install gcc/cl.exe to build ctypes benchmarks."
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
raise ConfigurationError(
|
|
206
|
+
f"ctypes compilation failed for {benchmark_path}:\n"
|
|
207
|
+
f"Command: {' '.join(compile_cmd)}\n"
|
|
208
|
+
f"stdout: {result.stdout}\n"
|
|
209
|
+
f"stderr: {result.stderr}"
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
if not lib_path.exists():
|
|
213
|
+
raise ConfigurationError(
|
|
214
|
+
f"Compilation succeeded but library not found: {lib_path}"
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
return benchmark_path
|
|
218
|
+
|
|
219
|
+
except subprocess.TimeoutExpired:
|
|
220
|
+
raise ConfigurationError(f"ctypes compilation timed out for {benchmark_path}")
|
|
221
|
+
except FileNotFoundError:
|
|
222
|
+
raise PlatformError(
|
|
223
|
+
"C compiler not found. Configure PGSI_CC_PATH, use --cc-path, "
|
|
224
|
+
"or install gcc/cl.exe to build ctypes benchmarks."
|
|
225
|
+
)
|
|
226
|
+
except Exception as e:
|
|
227
|
+
raise ConfigurationError(f"ctypes compilation error for {benchmark_path}: {e}")
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def build_benchmark(
|
|
231
|
+
algorithm: str,
|
|
232
|
+
method: str,
|
|
233
|
+
benchmark_path: Path,
|
|
234
|
+
tool_paths: Optional[ToolPaths] = None,
|
|
235
|
+
) -> Path:
|
|
236
|
+
"""
|
|
237
|
+
Build a benchmark if it requires compilation.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
algorithm: Algorithm name (for error messages)
|
|
241
|
+
method: Execution method
|
|
242
|
+
benchmark_path: Path to benchmark directory
|
|
243
|
+
tool_paths: Optional ToolPaths configuration
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Path to benchmark directory (ready for execution)
|
|
247
|
+
|
|
248
|
+
Raises:
|
|
249
|
+
ConfigurationError: If build fails
|
|
250
|
+
PlatformError: If build tools not available
|
|
251
|
+
"""
|
|
252
|
+
if not requires_build(method):
|
|
253
|
+
return benchmark_path # No build needed
|
|
254
|
+
|
|
255
|
+
# Compatibility guard: older registries/discovery may resolve build-based
|
|
256
|
+
# methods to .../main.py. Build routines require the method directory.
|
|
257
|
+
if benchmark_path.is_file() and benchmark_path.name == "main.py":
|
|
258
|
+
benchmark_path = benchmark_path.parent
|
|
259
|
+
|
|
260
|
+
if method == "cython":
|
|
261
|
+
return build_cython(benchmark_path, tool_paths=tool_paths)
|
|
262
|
+
elif method == "ctypes":
|
|
263
|
+
return build_ctypes(benchmark_path, tool_paths=tool_paths)
|
|
264
|
+
else:
|
|
265
|
+
raise ValueError(f"Unknown build method: {method}")
|