Skip to content

Commit b11145c

Browse files
authored
Merge branch 'main' into dependabot/uv/wheel-0.46.2
2 parents 188a5e7 + c7b5db3 commit b11145c

15 files changed

Lines changed: 1415 additions & 39 deletions

codeflash/api/aiservice.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,10 @@ def get_new_explanation( # noqa: D417
460460
optimized_throughput: str | None = None,
461461
throughput_improvement: str | None = None,
462462
function_references: str | None = None,
463+
acceptance_reason: str | None = None,
464+
original_concurrency_ratio: str | None = None,
465+
optimized_concurrency_ratio: str | None = None,
466+
concurrency_improvement: str | None = None,
463467
codeflash_version: str = codeflash_version,
464468
) -> str:
465469
"""Optimize the given python code for performance by making a request to the Django endpoint.
@@ -480,8 +484,12 @@ def get_new_explanation( # noqa: D417
480484
- original_throughput: str | None - throughput for the baseline code (operations per second)
481485
- optimized_throughput: str | None - throughput for the optimized code (operations per second)
482486
- throughput_improvement: str | None - throughput improvement percentage
483-
- current codeflash version
484487
- function_references: str | None - where the function is called in the codebase
488+
- acceptance_reason: str | None - why the optimization was accepted (runtime, throughput, or concurrency)
489+
- original_concurrency_ratio: str | None - concurrency ratio for the baseline code
490+
- optimized_concurrency_ratio: str | None - concurrency ratio for the optimized code
491+
- concurrency_improvement: str | None - concurrency improvement percentage
492+
- codeflash_version: str - current codeflash version
485493
486494
Returns
487495
-------
@@ -505,6 +513,10 @@ def get_new_explanation( # noqa: D417
505513
"optimized_throughput": optimized_throughput,
506514
"throughput_improvement": throughput_improvement,
507515
"function_references": function_references,
516+
"acceptance_reason": acceptance_reason,
517+
"original_concurrency_ratio": original_concurrency_ratio,
518+
"optimized_concurrency_ratio": optimized_concurrency_ratio,
519+
"concurrency_improvement": concurrency_improvement,
508520
"codeflash_version": codeflash_version,
509521
"call_sequence": self.get_next_sequence(),
510522
}

codeflash/code_utils/codeflash_wrap_decorator.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import gc
55
import os
66
import sqlite3
7+
import time
78
from enum import Enum
89
from functools import wraps
910
from pathlib import Path
@@ -165,3 +166,45 @@ async def async_wrapper(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401
165166
return return_value
166167

167168
return async_wrapper
169+
170+
171+
def codeflash_concurrency_async(func: F) -> F:
172+
"""Measures concurrent vs sequential execution performance for async functions."""
173+
174+
@wraps(func)
175+
async def async_wrapper(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401
176+
function_name = func.__name__
177+
concurrency_factor = int(os.environ.get("CODEFLASH_CONCURRENCY_FACTOR", "10"))
178+
179+
test_module_name = os.environ.get("CODEFLASH_TEST_MODULE", "")
180+
test_class_name = os.environ.get("CODEFLASH_TEST_CLASS", "")
181+
test_function = os.environ.get("CODEFLASH_TEST_FUNCTION", "")
182+
loop_index = os.environ.get("CODEFLASH_LOOP_INDEX", "0")
183+
184+
# Phase 1: Sequential execution timing
185+
gc.disable()
186+
try:
187+
seq_start = time.perf_counter_ns()
188+
for _ in range(concurrency_factor):
189+
result = await func(*args, **kwargs)
190+
sequential_time = time.perf_counter_ns() - seq_start
191+
finally:
192+
gc.enable()
193+
194+
# Phase 2: Concurrent execution timing
195+
gc.disable()
196+
try:
197+
conc_start = time.perf_counter_ns()
198+
tasks = [func(*args, **kwargs) for _ in range(concurrency_factor)]
199+
await asyncio.gather(*tasks)
200+
concurrent_time = time.perf_counter_ns() - conc_start
201+
finally:
202+
gc.enable()
203+
204+
# Output parseable metrics
205+
tag = f"{test_module_name}:{test_class_name}:{test_function}:{function_name}:{loop_index}"
206+
print(f"!@######CONC:{tag}:{sequential_time}:{concurrent_time}:{concurrency_factor}######@!")
207+
208+
return result
209+
210+
return async_wrapper

codeflash/code_utils/concolic_utils.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,20 @@
1010

1111
from codeflash.code_utils.compat import SAFE_SYS_EXECUTABLE, codeflash_temp_dir
1212

13+
# Known CrossHair limitations that produce invalid Python syntax in generated tests:
14+
# - "<locals>" - higher-order functions returning nested functions
15+
# - " object at 0x" - objects with default __repr__
16+
# - "<list_iterator" - iterator objects
17+
CROSSHAIR_KNOWN_LIMITATION_PATTERNS = ("<locals>", " object at 0x", "<list_iterator")
18+
1319

1420
def is_valid_concolic_test(test_code: str, project_root: Optional[str] = None) -> bool:
1521
try:
1622
ast.parse(test_code)
1723
except SyntaxError:
18-
sentry_sdk.capture_message(f"CrossHair generated test with syntax error:\n{test_code}")
24+
is_known_limitation = any(pattern in test_code for pattern in CROSSHAIR_KNOWN_LIMITATION_PATTERNS)
25+
if not is_known_limitation:
26+
sentry_sdk.capture_message(f"CrossHair generated test with syntax error:\n{test_code}")
1927
return False
2028

2129
temp_path = (codeflash_temp_dir / f"concolic_test_{uuid.uuid4().hex}.py").resolve()

codeflash/code_utils/config_consts.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
MAX_FUNCTION_TEST_SECONDS = 60
1111
MIN_IMPROVEMENT_THRESHOLD = 0.05
1212
MIN_THROUGHPUT_IMPROVEMENT_THRESHOLD = 0.10 # 10% minimum improvement for async throughput
13+
MIN_CONCURRENCY_IMPROVEMENT_THRESHOLD = 0.20 # 20% concurrency ratio improvement required
14+
CONCURRENCY_FACTOR = 10 # Number of concurrent executions for concurrency benchmark
1315
MAX_TEST_FUNCTION_RUNS = 50
1416
MAX_CUMULATIVE_TEST_RUNTIME_NANOSECONDS = 100e6 # 100ms
1517
TOTAL_LOOPING_TIME = 10.0 # 10 second candidate benchmarking budget

codeflash/code_utils/instrument_existing_tests.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,9 +1439,12 @@ def __init__(self, function: FunctionToOptimize, mode: TestingMode = TestingMode
14391439
self.added_decorator = False
14401440

14411441
# Choose decorator based on mode
1442-
self.decorator_name = (
1443-
"codeflash_behavior_async" if mode == TestingMode.BEHAVIOR else "codeflash_performance_async"
1444-
)
1442+
if mode == TestingMode.BEHAVIOR:
1443+
self.decorator_name = "codeflash_behavior_async"
1444+
elif mode == TestingMode.CONCURRENCY:
1445+
self.decorator_name = "codeflash_concurrency_async"
1446+
else:
1447+
self.decorator_name = "codeflash_performance_async"
14451448

14461449
def visit_ClassDef(self, node: cst.ClassDef) -> None:
14471450
# Track when we enter a class
@@ -1484,12 +1487,14 @@ def _is_target_decorator(self, decorator_node: cst.Name | cst.Attribute | cst.Ca
14841487
"codeflash_trace_async",
14851488
"codeflash_behavior_async",
14861489
"codeflash_performance_async",
1490+
"codeflash_concurrency_async",
14871491
}
14881492
if isinstance(decorator_node, cst.Call) and isinstance(decorator_node.func, cst.Name):
14891493
return decorator_node.func.value in {
14901494
"codeflash_trace_async",
14911495
"codeflash_behavior_async",
14921496
"codeflash_performance_async",
1497+
"codeflash_concurrency_async",
14931498
}
14941499
return False
14951500

@@ -1501,6 +1506,14 @@ def __init__(self, mode: TestingMode = TestingMode.BEHAVIOR) -> None:
15011506
self.mode = mode
15021507
self.has_import = False
15031508

1509+
def _get_decorator_name(self) -> str:
1510+
"""Get the decorator name based on the testing mode."""
1511+
if self.mode == TestingMode.BEHAVIOR:
1512+
return "codeflash_behavior_async"
1513+
if self.mode == TestingMode.CONCURRENCY:
1514+
return "codeflash_concurrency_async"
1515+
return "codeflash_performance_async"
1516+
15041517
def visit_ImportFrom(self, node: cst.ImportFrom) -> None:
15051518
# Check if the async decorator import is already present
15061519
if (
@@ -1512,9 +1525,7 @@ def visit_ImportFrom(self, node: cst.ImportFrom) -> None:
15121525
and node.module.attr.value == "codeflash_wrap_decorator"
15131526
and not isinstance(node.names, cst.ImportStar)
15141527
):
1515-
decorator_name = (
1516-
"codeflash_behavior_async" if self.mode == TestingMode.BEHAVIOR else "codeflash_performance_async"
1517-
)
1528+
decorator_name = self._get_decorator_name()
15181529
for import_alias in node.names:
15191530
if import_alias.name.value == decorator_name:
15201531
self.has_import = True
@@ -1525,9 +1536,7 @@ def leave_Module(self, original_node: cst.Module, updated_node: cst.Module) -> c
15251536
return updated_node
15261537

15271538
# Choose import based on mode
1528-
decorator_name = (
1529-
"codeflash_behavior_async" if self.mode == TestingMode.BEHAVIOR else "codeflash_performance_async"
1530-
)
1539+
decorator_name = self._get_decorator_name()
15311540

15321541
# Parse the import statement into a CST node
15331542
import_node = cst.parse_statement(f"from codeflash.code_utils.codeflash_wrap_decorator import {decorator_name}")

codeflash/models/models.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ class BestOptimization(BaseModel):
161161
winning_replay_benchmarking_test_results: Optional[TestResults] = None
162162
line_profiler_test_results: dict
163163
async_throughput: Optional[int] = None
164+
concurrency_metrics: Optional[ConcurrencyMetrics] = None
164165

165166

166167
@dataclass(frozen=True)
@@ -172,6 +173,14 @@ def __str__(self) -> str:
172173
return f"{self.module_path}::{self.function_name}"
173174

174175

176+
@dataclass
177+
class ConcurrencyMetrics:
178+
sequential_time_ns: int
179+
concurrent_time_ns: int
180+
concurrency_factor: int
181+
concurrency_ratio: float # sequential_time / concurrent_time
182+
183+
175184
@dataclass
176185
class BenchmarkDetail:
177186
benchmark_name: str
@@ -336,6 +345,7 @@ class OptimizedCandidateResult(BaseModel):
336345
optimization_candidate_index: int
337346
total_candidate_timing: int
338347
async_throughput: Optional[int] = None
348+
concurrency_metrics: Optional[ConcurrencyMetrics] = None
339349

340350

341351
class GeneratedTests(BaseModel):
@@ -557,6 +567,7 @@ class OriginalCodeBaseline(BaseModel):
557567
runtime: int
558568
coverage_results: Optional[CoverageData]
559569
async_throughput: Optional[int] = None
570+
concurrency_metrics: Optional[ConcurrencyMetrics] = None
560571

561572

562573
class CoverageStatus(Enum):
@@ -648,6 +659,7 @@ class TestingMode(enum.Enum):
648659
BEHAVIOR = "behavior"
649660
PERFORMANCE = "performance"
650661
LINE_PROFILE = "line_profile"
662+
CONCURRENCY = "concurrency"
651663

652664

653665
# TODO this class is duplicated in codeflash_capture

0 commit comments

Comments
 (0)