Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
151 commits
Select commit Hold shift + click to select a range
3480718
initial implementation for pytest benchmark discovery
alvin-r Feb 25, 2025
7f917a0
Merge branch 'refs/heads/main' into pytest-benchmark
alvin-r Feb 27, 2025
133a9e3
initial implementation for tracing benchmarks using a plugin, and pro…
alvin-r Feb 28, 2025
2f26695
initial implementation of tracing benchmarks via the plugin
alvin-r Mar 4, 2025
32b0d3b
Merge branch 'main' into pytest-benchmark
alvin-r Mar 11, 2025
6b4b68a
basic version working on bubble sort
alvin-r Mar 11, 2025
887e3cb
initial attempt for codeflash_trace_decorator
alvin-r Mar 11, 2025
84bd0f0
improvements
alvin-r Mar 12, 2025
1c3919d
Merge branch 'refs/heads/main' into codeflash-trace-decorator
alvin-r Mar 12, 2025
c4694b7
work on new replay_test logic
alvin-r Mar 12, 2025
c150c05
Merge branch 'main' into codeflash-trace-decorator
alvin-r Mar 14, 2025
1801d41
initial replay test version working
alvin-r Mar 14, 2025
88a11d3
Merge branch 'main' into codeflash-trace-decorator
alvin-r Mar 14, 2025
f7466a5
replay test functionality working for functions, methods, static meth…
alvin-r Mar 14, 2025
4c19e6f
restored overwritten logic
alvin-r Mar 14, 2025
7eba031
functioning end to end, gets the funciton impact on benchmarks
alvin-r Mar 18, 2025
896aa52
modified printing of results, handle errors when collecting benchmarks
alvin-r Mar 19, 2025
ad17de4
tests pass
alvin-r Mar 19, 2025
92e6bf5
revert pyproject.toml
alvin-r Mar 19, 2025
4784723
mypy fixes
alvin-r Mar 19, 2025
5f05711
Merge branch 'main' into codeflash-trace-decorator
alvin-r Mar 19, 2025
b77a979
import changes
alvin-r Mar 20, 2025
8878baf
Merge branch 'pytest-plugin-blocker' into codeflash-trace-decorator
alvin-r Mar 20, 2025
0c2a3b6
removed benchmark skip command
alvin-r Mar 20, 2025
9a41bdd
shifted benchmark class in plugin, improved display of benchmark info
alvin-r Mar 20, 2025
5577cd5
cleanup tests better
alvin-r Mar 20, 2025
83f1c1c
Merge branch 'main' into codeflash-trace-decorator
alvin-r Mar 20, 2025
80730f9
modified paths in test
alvin-r Mar 20, 2025
d610f8c
typing fix
alvin-r Mar 20, 2025
93f583c
typing fix for 3.9
alvin-r Mar 21, 2025
d422e35
typing fix for 3.9
alvin-r Mar 21, 2025
d664040
Merge branch 'main' into codeflash-trace-decorator
alvin-r Mar 21, 2025
1637810
works with multithreading, added test
alvin-r Mar 24, 2025
684acf8
Merge branch 'refs/heads/main' into codeflash-trace-decorator
alvin-r Mar 24, 2025
6180c9d
refactored get_function_benchmark_timings and get_benchmark_timings i…
alvin-r Mar 25, 2025
fa93df6
Merge branch 'refs/heads/main' into codeflash-trace-decorator
alvin-r Mar 25, 2025
67d3f19
fixed isort
alvin-r Mar 25, 2025
f4be9be
modified PR info
alvin-r Mar 26, 2025
77f43a5
mypy fix
alvin-r Mar 26, 2025
da6385f
use dill instead of pickle
alvin-r Mar 26, 2025
f34f22f
modified the benchmarking approach. codeflash_trace and codeflash_ben…
alvin-r Mar 28, 2025
57b80ec
started implementing group by benchmark
alvin-r Mar 28, 2025
87ad743
Merge branch 'refs/heads/merge_test_results_into_models' into codefla…
alvin-r Mar 28, 2025
d03ed96
Merge branch 'merge_test_results_into_models' into codeflash-trace-de…
alvin-r Mar 28, 2025
8d95b18
Merge branch 'main' into codeflash-trace-decorator
alvin-r Mar 28, 2025
56e3447
reworked matching benchmark key to test results.
alvin-r Mar 31, 2025
5a34697
Merge branch 'main' into codeflash-trace-decorator
alvin-r Apr 1, 2025
5f86bdd
PRAGMA journal to memory to make it faster
alvin-r Apr 1, 2025
20890fa
Merge branch 'main' into codeflash-trace-decorator
alvin-r Apr 1, 2025
9764c25
benchmarks root must be subdir of tests root
alvin-r Apr 1, 2025
d703b13
replay tests are now grouped by benchmark file. each benchmark test f…
alvin-r Apr 1, 2025
14c33f9
Merge branch 'main' into codeflash-trace-decorator
alvin-r Apr 1, 2025
c6a201b
Merge branch 'main' into codeflash-trace-decorator
alvin-r Apr 1, 2025
30ec0c4
Use module path instead of file path for benchmarks, improved display…
alvin-r Apr 2, 2025
cf00212
Merge branch 'refs/heads/main' into codeflash-trace-decorator
alvin-r Apr 2, 2025
bb9c5db
benchmark flow is working. changed paths to use module_path instead o…
alvin-r Apr 2, 2025
7d9c4e1
Merge branch 'main' into codeflash-trace-decorator
alvin-r Apr 2, 2025
1928dc4
fixed string error
alvin-r Apr 2, 2025
217e239
fixed mypy error
alvin-r Apr 2, 2025
96dd780
new end to end test for benchmarking bubble sort
alvin-r Apr 2, 2025
5785875
renamed test
alvin-r Apr 2, 2025
d656d3b
fixed e2e test
alvin-r Apr 2, 2025
4d0eb3d
printing issues on github actions
alvin-r Apr 2, 2025
6100620
attempt to use horizontals for rows
alvin-r Apr 2, 2025
21a79eb
added row lines
alvin-r Apr 2, 2025
b374b6e
made benchmarks-root use resolve()
alvin-r Apr 3, 2025
27a6488
handled edge case for instrumenting codeflash trace
alvin-r Apr 3, 2025
4a24f2c
fixed slight bug with formatting table
alvin-r Apr 3, 2025
9de664b
improved file removal after errors
alvin-r Apr 3, 2025
a8d4fda
fixed a return bug
alvin-r Apr 4, 2025
4d53330
Merge branch 'jedi_ctx_fix' into codeflash-trace-decorator
alvin-r Apr 4, 2025
c82a3a3
Merge branch 'refs/heads/main' into codeflash-trace-decorator
alvin-r Apr 7, 2025
1f3fcff
Support recursive functions, and @benchmark / @pytest.mark.benchmark …
alvin-r Apr 7, 2025
fe63652
basic pickle patch version working
alvin-r Apr 8, 2025
d653d0d
draft of end to end test
alvin-r Apr 8, 2025
a73b541
initial implementation for pytest benchmark discovery
alvin-r Feb 25, 2025
965e2c8
initial implementation for tracing benchmarks using a plugin, and pro…
alvin-r Feb 28, 2025
7590c29
initial implementation of tracing benchmarks via the plugin
alvin-r Mar 4, 2025
034bed3
basic version working on bubble sort
alvin-r Mar 11, 2025
1f3fd4d
initial attempt for codeflash_trace_decorator
alvin-r Mar 11, 2025
5faccd8
improvements
alvin-r Mar 12, 2025
d6217e8
work on new replay_test logic
alvin-r Mar 12, 2025
26b2c4f
initial replay test version working
alvin-r Mar 14, 2025
adffb9d
replay test functionality working for functions, methods, static meth…
alvin-r Mar 14, 2025
f9144ec
restored overwritten logic
alvin-r Mar 14, 2025
c29c8bf
functioning end to end, gets the funciton impact on benchmarks
alvin-r Mar 18, 2025
54fe71f
modified printing of results, handle errors when collecting benchmarks
alvin-r Mar 19, 2025
5fd112a
tests pass
alvin-r Mar 19, 2025
8194554
revert pyproject.toml
alvin-r Mar 19, 2025
4c1d2af
mypy fixes
alvin-r Mar 19, 2025
6e676e9
import changes
alvin-r Mar 20, 2025
62f3b36
removed benchmark skip command
alvin-r Mar 20, 2025
a614972
shifted benchmark class in plugin, improved display of benchmark info
alvin-r Mar 20, 2025
82cb7a9
cleanup tests better
alvin-r Mar 20, 2025
7601895
modified paths in test
alvin-r Mar 20, 2025
4d69427
typing fix
alvin-r Mar 20, 2025
ebe3e12
typing fix for 3.9
alvin-r Mar 21, 2025
0449d0d
typing fix for 3.9
alvin-r Mar 21, 2025
baac964
works with multithreading, added test
alvin-r Mar 24, 2025
357f586
refactored get_function_benchmark_timings and get_benchmark_timings i…
alvin-r Mar 25, 2025
9efa47f
fixed isort
alvin-r Mar 25, 2025
64b4c64
modified PR info
alvin-r Mar 26, 2025
4c61de9
mypy fix
alvin-r Mar 26, 2025
eda0d46
use dill instead of pickle
alvin-r Mar 26, 2025
a82e9f0
modified the benchmarking approach. codeflash_trace and codeflash_ben…
alvin-r Mar 28, 2025
582bea0
started implementing group by benchmark
alvin-r Mar 28, 2025
e5a8260
reworked matching benchmark key to test results.
alvin-r Mar 31, 2025
0937329
PRAGMA journal to memory to make it faster
alvin-r Apr 1, 2025
ed8f5ef
benchmarks root must be subdir of tests root
alvin-r Apr 1, 2025
75c1be7
replay tests are now grouped by benchmark file. each benchmark test f…
alvin-r Apr 1, 2025
b3c8320
Use module path instead of file path for benchmarks, improved display…
alvin-r Apr 2, 2025
972ef46
benchmark flow is working. changed paths to use module_path instead o…
alvin-r Apr 2, 2025
06b3818
fixed string error
alvin-r Apr 2, 2025
37577e7
fixed mypy error
alvin-r Apr 2, 2025
5c30d3e
new end to end test for benchmarking bubble sort
alvin-r Apr 2, 2025
906e434
renamed test
alvin-r Apr 2, 2025
821fa47
fixed e2e test
alvin-r Apr 2, 2025
41f7e0a
printing issues on github actions
alvin-r Apr 2, 2025
c20f29a
attempt to use horizontals for rows
alvin-r Apr 2, 2025
d1a8d25
added row lines
alvin-r Apr 2, 2025
705105c
made benchmarks-root use resolve()
alvin-r Apr 3, 2025
26546de
handled edge case for instrumenting codeflash trace
alvin-r Apr 3, 2025
0c04adf
fixed slight bug with formatting table
alvin-r Apr 3, 2025
30d32bb
improved file removal after errors
alvin-r Apr 3, 2025
c997b90
fixed a return bug
alvin-r Apr 4, 2025
d6ed1c3
Support recursive functions, and @benchmark / @pytest.mark.benchmark …
alvin-r Apr 7, 2025
a4c4c2d
Merge remote-tracking branch 'origin/codeflash-trace-decorator' into …
alvin-r Apr 10, 2025
40e416e
Merge branch 'refs/heads/main' into codeflash-trace-decorator
alvin-r Apr 11, 2025
3158f9c
end to end test that proves picklepatcher works. example shown is a s…
alvin-r Apr 11, 2025
9578854
Merge branch 'main' into codeflash-trace-decorator
alvin-r Apr 11, 2025
4bb0aad
minor fix for removing files
alvin-r Apr 11, 2025
790d77c
fixes to sync with main
alvin-r Apr 11, 2025
fce641e
Merge branch 'main' into codeflash-trace-decorator
alvin-r Apr 11, 2025
b70c4c9
Merge branch 'main' into codeflash-trace-decorator
alvin-r Apr 15, 2025
28fd746
cmd init changes
alvin-r Apr 15, 2025
4e8483b
created benchmarks for codeflash, modified codeflash-optimize to use …
alvin-r Apr 16, 2025
efc91d6
Merge branch 'main' into codeflash-trace-decorator
alvin-r Apr 16, 2025
0680f79
added benchmarks root
alvin-r Apr 16, 2025
583b464
removed comment
alvin-r Apr 16, 2025
1eaaad7
debugging
alvin-r Apr 16, 2025
ab9079b
debugging
alvin-r Apr 16, 2025
d7274ec
removed benchmark-skip
alvin-r Apr 16, 2025
a624221
added pytest-benchmark as dependency
alvin-r Apr 16, 2025
605d078
updated pyproject
alvin-r Apr 16, 2025
78871fe
gha failing on multithreaded t est
alvin-r Apr 16, 2025
0146d82
line number test is off by 1 for python versions 39 and 310, removed …
alvin-r Apr 17, 2025
6c1a369
Merge branch 'main' into codeflash-trace-decorator
alvin-r Apr 17, 2025
3017ccf
100 max function calls before flushing to disk instead of 1000
alvin-r Apr 17, 2025
f14cf01
skip multithreaded benchmark test if machine is single threaded (fixe…
alvin-r Apr 17, 2025
e5ca10f
marked multithreaded trace benchmarks test to be skipped during CI as…
alvin-r Apr 17, 2025
683c9f6
shift check for pickle placerholder access error in comparator
alvin-r Apr 17, 2025
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
initial attempt for codeflash_trace_decorator
  • Loading branch information
alvin-r committed Apr 10, 2025
commit 1f3fd4d2dba1b6dc8ada99e4fb34f2016ac6596d
73 changes: 73 additions & 0 deletions codeflash/benchmarking/codeflash_trace_decorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import functools
import pickle
import sqlite3
import time
import os

def codeflash_trace(output_file: str):
"""A decorator factory that returns a decorator that measures the execution time
of a function and pickles its arguments using the highest protocol available.

Args:
output_file: Path to the SQLite database file where results will be stored

Returns:
The decorator function

"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# Measure execution time
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()

# Calculate execution time
execution_time = end_time - start_time

# Measure overhead
overhead_start_time = time.time()

try:
# Connect to the database
con = sqlite3.connect(output_file)
cur = con.cursor()
cur.execute("PRAGMA synchronous = OFF")

# Check if table exists and create it if it doesn't
cur.execute(
"CREATE TABLE IF NOT EXISTS function_calls(function_name TEXT, class_name TEXT, file_name TEXT, benchmark_function_name TEXT, benchmark_file_name TEXT,"
"time_ns INTEGER, args BLOB, kwargs BLOB)"
)

# Pickle the arguments
pickled_args = pickle.dumps(args, protocol=pickle.HIGHEST_PROTOCOL)
pickled_kwargs = pickle.dumps(kwargs, protocol=pickle.HIGHEST_PROTOCOL)

# Get benchmark info from environment
benchmark_function_name = os.environ.get("CODEFLASH_BENCHMARK_FUNCTION_NAME")
benchmark_file_name = os.environ.get("CODEFLASH_BENCHMARK_FILE_NAME")
# Insert the data
cur.execute(
"INSERT INTO function_calls (function_name, classname, filename, benchmark_function_name, benchmark_file_name, time_ns, args, kwargs) "
"VALUES (?, ?, ?, ?, ?, ?)",
(func.__name__, func.__module__, func.__code__.co_filename,
execution_time, pickled_args, pickled_kwargs)
)

# Commit and close
con.commit()
con.close()

overhead_end_time = time.time()

print(f"Function '{func.__name__}' took {execution_time:.6f} seconds to execute")
print(f"Function '{func.__name__}' overhead took {overhead_end_time - overhead_start_time:.6f} seconds to execute")

except Exception as e:
print(f"Error in codeflash_trace: {e}")

return result
return wrapper
return decorator
1 change: 1 addition & 0 deletions codeflash/benchmarking/instrument_codeflash_trace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

46 changes: 5 additions & 41 deletions codeflash/benchmarking/plugin/plugin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import pytest

from codeflash.tracer import Tracer
from pathlib import Path
import time

class CodeFlashPlugin:
@staticmethod
Expand All @@ -12,18 +10,6 @@ def pytest_addoption(parser):
default=False,
help="Enable CodeFlash tracing"
)
parser.addoption(
"--functions",
action="store",
default="",
help="Comma-separated list of additional functions to trace"
)
parser.addoption(
"--benchmarks-root",
action="store",
default=".",
help="Root directory for benchmarks"
)

@staticmethod
def pytest_plugin_registered(plugin, manager):
Expand All @@ -49,32 +35,10 @@ def benchmark(request):

class Benchmark:
def __call__(self, func, *args, **kwargs):
func_name = func.__name__
test_name = request.node.name
additional_functions = request.config.getoption("--functions").split(",")
trace_functions = [f for f in additional_functions if f]
print("Tracing functions: ", trace_functions)

# Get benchmarks root directory from command line option
benchmarks_root = Path(request.config.getoption("--benchmarks-root"))

# Create .trace directory if it doesn't exist
trace_dir = benchmarks_root / '.codeflash_trace'
trace_dir.mkdir(exist_ok=True)

# Set output path to the .trace directory
output_path = trace_dir / f"{test_name}.trace"

tracer = Tracer(
output=str(output_path), # Convert Path to string for Tracer
functions=trace_functions,
max_function_count=256,
benchmark=True
)

with tracer:
result = func(*args, **kwargs)

start = time.time_ns()
result = func(*args, **kwargs)
end = time.time_ns()
print(f"Benchmark: {func.__name__} took {end - start} ns")
return result

return Benchmark()
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
from plugin.plugin import CodeFlashPlugin

benchmarks_root = sys.argv[1]
function_list = sys.argv[2]
if __name__ == "__main__":
import pytest

try:
exitcode = pytest.main(
[benchmarks_root, "--benchmarks-root", benchmarks_root, "--codeflash-trace", "-p", "no:benchmark", "-s", "--functions", function_list,"-o", "addopts="], plugins=[CodeFlashPlugin()]
[benchmarks_root, "--codeflash-trace", "-p", "no:benchmark", "-s", "-o", "addopts="], plugins=[CodeFlashPlugin()]
)
except Exception as e:
print(f"Failed to collect tests: {e!s}")
Expand Down
5 changes: 2 additions & 3 deletions codeflash/benchmarking/trace_benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
from pathlib import Path
import subprocess

def trace_benchmarks_pytest(benchmarks_root: Path, project_root: Path, function_list: list[str] = []) -> None:
def trace_benchmarks_pytest(benchmarks_root: Path, project_root: Path) -> None:
result = subprocess.run(
[
SAFE_SYS_EXECUTABLE,
Path(__file__).parent / "pytest_new_process_trace_benchmarks.py",
str(benchmarks_root),
",".join(function_list)
benchmarks_root,
],
cwd=project_root,
check=False,
Expand Down
35 changes: 23 additions & 12 deletions codeflash/optimization/optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from codeflash.verification.verification_utils import TestConfig
from codeflash.benchmarking.get_trace_info import get_function_benchmark_timings, get_benchmark_timings
from codeflash.benchmarking.utils import print_benchmark_table
from collections import defaultdict
if TYPE_CHECKING:
from argparse import Namespace

Expand Down Expand Up @@ -91,18 +92,28 @@ def run(self) -> None:
module_root=self.args.module_root,
)
if self.args.benchmark:
all_functions_to_optimize = [
function
for functions_list in file_to_funcs_to_optimize.values()
for function in functions_list
]
logger.info(f"Tracing existing benchmarks for {len(all_functions_to_optimize)} functions")
trace_benchmarks_pytest(self.args.benchmarks_root, self.args.project_root, [fto.qualified_name_with_file_name for fto in all_functions_to_optimize])
logger.info("Finished tracing existing benchmarks")
trace_dir = Path(self.args.benchmarks_root) / ".codeflash_trace"
function_benchmark_timings = get_function_benchmark_timings(trace_dir, all_functions_to_optimize)
total_benchmark_timings = get_benchmark_timings(trace_dir)
print_benchmark_table(function_benchmark_timings, total_benchmark_timings)
# Insert decorator
file_path_to_source_code = defaultdict(str)
for file in file_to_funcs_to_optimize:
with file.open("r", encoding="utf8") as f:
file_path_to_source_code[file] = f.read()
try:
for functions_to_optimize in file_to_funcs_to_optimize.values():
for fto in functions_to_optimize:
pass
#instrument_codeflash_trace_decorator(fto)
trace_benchmarks_pytest(self.args.project_root) # Simply run all tests that use pytest-benchmark
logger.info("Finished tracing existing benchmarks")
finally:
# Restore original source code
for file in file_path_to_source_code:
with file.open("w", encoding="utf8") as f:
f.write(file_path_to_source_code[file])

# trace_dir = Path(self.args.benchmarks_root) / ".codeflash_trace"
# function_benchmark_timings = get_function_benchmark_timings(trace_dir, all_functions_to_optimize)
# total_benchmark_timings = get_benchmark_timings(trace_dir)
# print_benchmark_table(function_benchmark_timings, total_benchmark_timings)


optimizations_found: int = 0
Expand Down
2 changes: 1 addition & 1 deletion tests/test_trace_benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ def test_trace_benchmarks():
# Test the trace_benchmarks function
project_root = Path(__file__).parent.parent / "code_to_optimize"
benchmarks_root = project_root / "tests" / "pytest" / "benchmarks"
trace_benchmarks_pytest(benchmarks_root, project_root, ["sorter"])
trace_benchmarks_pytest(benchmarks_root, project_root)