Skip to content

Commit eb3fcb6

Browse files
authored
Merge pull request #2020 from codeflash-ai/fix/js-benchmarking-calibrate
[JS] Improvements for benchmarking (reduce the noise) & calibration
2 parents cafcd7f + 52173dc commit eb3fcb6

6 files changed

Lines changed: 281 additions & 97 deletions

File tree

codeflash/languages/javascript/support.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,9 +2059,9 @@ def process_generated_test_strings(
20592059
generated_test_source, project_module_system, test_cfg.tests_project_rootdir
20602060
)
20612061

2062-
# Add .js extensions to relative imports for ESM projects
2063-
# TypeScript + ESM requires explicit .js extensions even for .ts source files
2064-
if project_module_system == ModuleSystem.ES_MODULE:
2062+
# Add .js extensions to relative imports for ESM projects — but NOT for Jest,
2063+
# which resolves .ts imports without .js extensions via its transform/resolver.
2064+
if project_module_system == ModuleSystem.ES_MODULE and test_cfg.test_framework != "jest":
20652065
from codeflash.languages.javascript.module_system import add_js_extensions_to_relative_imports
20662066

20672067
generated_test_source = add_js_extensions_to_relative_imports(generated_test_source)
@@ -2264,10 +2264,13 @@ def get_module_path(self, source_file: Path, project_root: Path, tests_root: Pat
22642264
# (e.g. \t → tab, \n → newline) and would break imports on Windows.
22652265
rel_path = os.path.relpath(str(source_without_ext), str(tests_root_abs)).replace("\\", "/")
22662266

2267-
# For ESM, add .js extension (TypeScript convention)
2268-
# TypeScript requires imports to reference the OUTPUT file extension (.js),
2269-
# even when the source file is .ts. This is required for Node.js ESM resolution.
2270-
if module_system == ModuleSystem.ES_MODULE:
2267+
# For ESM, add .js extension (TypeScript convention) — but only for Vitest/native ESM.
2268+
# Jest resolves .ts imports without .js extensions via its transform/resolver config,
2269+
# so adding .js breaks Jest module resolution (Cannot find module '../foo.js').
2270+
from codeflash.languages.test_framework import get_js_test_framework_or_default
2271+
2272+
test_framework = get_js_test_framework_or_default()
2273+
if module_system == ModuleSystem.ES_MODULE and test_framework != "jest":
22712274
rel_path = rel_path + ".js"
22722275
logger.debug(
22732276
f"!lsp|Module path (ESM): source={source_file_abs}, tests_root={tests_root_abs}, "
@@ -2286,7 +2289,7 @@ def get_module_path(self, source_file: Path, project_root: Path, tests_root: Pat
22862289
# For fallback, also check module system
22872290
module_system = detect_module_system(project_root, source_file)
22882291
path_without_ext = "../" + rel_path.with_suffix("").as_posix()
2289-
if module_system == ModuleSystem.ES_MODULE:
2292+
if module_system == ModuleSystem.ES_MODULE and test_framework != "jest":
22902293
return path_without_ext + ".js"
22912294
return path_without_ext
22922295

codeflash/languages/javascript/test_runner.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,9 @@ def run_jest_benchmarking_tests(
11081108
jest_env["CODEFLASH_PERF_TARGET_DURATION_MS"] = str(target_duration_ms)
11091109
jest_env["CODEFLASH_PERF_STABILITY_CHECK"] = "true" if stability_check else "false"
11101110
jest_env["CODEFLASH_LOOP_INDEX"] = "1" # Initial value for compatibility
1111+
# Warmup and calibration for accurate benchmarking
1112+
jest_env["CODEFLASH_PERF_WARMUP_ITERATIONS"] = "5"
1113+
jest_env["CODEFLASH_PERF_MIN_TIME_NS"] = "5000" # 5us minimum time for calibration
11111114

11121115
# Enable console output for timing markers
11131116
# Some projects mock console.log in test setup (e.g., based on LOG_LEVEL or DEBUG)
@@ -1123,10 +1126,13 @@ def run_jest_benchmarking_tests(
11231126
# Configure ESM support if project uses ES Modules
11241127
_configure_esm_environment(jest_env, effective_cwd)
11251128

1126-
# Increase Node.js heap size for large TypeScript projects
1129+
# Increase Node.js heap size and expose GC for accurate benchmarking
11271130
existing_node_options = jest_env.get("NODE_OPTIONS", "")
11281131
if "--max-old-space-size" not in existing_node_options:
1129-
jest_env["NODE_OPTIONS"] = f"{existing_node_options} --max-old-space-size=4096".strip()
1132+
existing_node_options = f"{existing_node_options} --max-old-space-size=4096".strip()
1133+
if "--expose-gc" not in existing_node_options:
1134+
existing_node_options = f"{existing_node_options} --expose-gc".strip()
1135+
jest_env["NODE_OPTIONS"] = existing_node_options
11301136

11311137
# Subprocess timeout: target_duration + 120s headroom for Jest startup
11321138
# and TS compilation. capturePerf's time budget governs actual looping.

codeflash/languages/javascript/vitest_runner.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,17 @@ def run_vitest_benchmarking_tests(
681681
vitest_env["CODEFLASH_PERF_TARGET_DURATION_MS"] = str(target_duration_ms)
682682
vitest_env["CODEFLASH_PERF_STABILITY_CHECK"] = "true" if stability_check else "false"
683683
vitest_env["CODEFLASH_LOOP_INDEX"] = "1"
684+
# Warmup and calibration for accurate benchmarking
685+
vitest_env["CODEFLASH_PERF_WARMUP_ITERATIONS"] = "5"
686+
vitest_env["CODEFLASH_PERF_MIN_TIME_NS"] = "5000" # 5us minimum time for calibration
687+
688+
# Expose GC for accurate benchmarking (allows capturePerf to force GC before timing)
689+
existing_node_options = vitest_env.get("NODE_OPTIONS", "")
690+
if "--expose-gc" not in existing_node_options:
691+
existing_node_options = f"{existing_node_options} --expose-gc".strip()
692+
if "--max-old-space-size" not in existing_node_options:
693+
existing_node_options = f"{existing_node_options} --max-old-space-size=4096".strip()
694+
vitest_env["NODE_OPTIONS"] = existing_node_options
684695

685696
# Set test module for marker identification (use first test file as reference)
686697
if test_files:

0 commit comments

Comments
 (0)