diff --git a/scripts/compare_ra.sh b/scripts/compare_ra.sh index 1ddc259..89b8cfa 100755 --- a/scripts/compare_ra.sh +++ b/scripts/compare_ra.sh @@ -1,12 +1,15 @@ #!/usr/bin/env bash -# compare_ra.sh — 对比寄存器分配(新版)与旧版编译器的汇编质量和运行性能 +# compare_ra.sh — 寄存器分配编译器分析工具 # -# 用法: -# ./scripts/compare_ra.sh --old feature/mir # 对比 feature/mir 分支 -# ./scripts/compare_ra.sh --old 70234dd # 对比指定 commit -# ./scripts/compare_ra.sh --old feature/mir --tests performance # 仅性能测试 -# ./scripts/compare_ra.sh --old feature/mir --mode asm # 仅对比汇编 -# ./scripts/compare_ra.sh --old path/to/old/compiler --no-build # 使用已构建的旧编译器 +# 对比模式: +# ./scripts/compare_ra.sh --old feature/mir # 对比 feature/mir 分支 vs 本地 +# ./scripts/compare_ra.sh --old 70234dd --new 8ece3ac # 对比两个指定 commit +# ./scripts/compare_ra.sh --old path/to/old/compiler --no-build # 使用已构建的旧编译器 +# +# 单版本统计模式: +# ./scripts/compare_ra.sh --ref 5b8303d # 统计指定版本的数据 +# ./scripts/compare_ra.sh --ref feature/ra --tests performance # 仅统计性能数据 +# ./scripts/compare_ra.sh --ref /path/to/compiler --no-build # 使用预构建编译器 # # 输出: 终端表格 + compare_result/ 目录下的详细文件 @@ -14,47 +17,70 @@ set -euo pipefail # ========== 参数解析 ========== OLD_REF="" -MODE="all" # asm | run | all -TEST_SET="all" # functional | performance | all +NEW_REF="" # 空 = 使用本地 build 目录 +SINGLE_REF="" # --ref: 单版本统计模式,不与其它版本比较 +MODE="all" # asm | run | all +TEST_SET="all" # functional | performance | all NO_BUILD=false +RUN_COUNT=1 # 单版本模式下每个测试的执行次数 OLD_COMPILER_PATH="" NEW_COMPILER_PATH="./build/bin/compiler" WORKTREE_DIR="" +NEW_WORKTREE_DIR="" +SINGLE_WORKTREE_DIR="" KEEP_WORKTREE=false usage() { - echo "用法: $0 --old [选项]" + echo "用法:" + echo " 对比模式: $0 --old [选项]" + echo " 单版本模式: $0 --ref [选项]" echo "" - echo "必选:" + echo "对比模式选项(--old 与 --ref 互斥):" echo " --old 对比基线:git 分支名、commit hash、或旧编译器路径" + echo " --new 新版基线:git 分支名或 commit hash(默认使用本地 ./build/bin/compiler)" + echo "" + echo "单版本模式选项:" + echo " --ref 统计指定版本的汇编和运行数据(不进行版本间对比)" echo "" - echo "可选:" - echo " --mode 对比模式: asm (仅汇编) | run (仅运行) | all (默认)" + echo "通用选项:" + echo " --mode 模式: asm (仅汇编) | run (仅运行) | all (默认)" echo " --tests 测试集: functional | performance | all (默认)" - echo " --no-build 旧编译器已构建好,--old 指向编译器可执行文件路径" - echo " --keep-worktree 保留旧的 git worktree(默认会删除)" + echo " --runs 单版本模式下每个测试的执行次数(默认 1)" + echo " --no-build 指向编译器可执行文件路径,跳过构建" + echo " --keep-worktree 保留 git worktree(默认会删除)" echo "" echo "示例:" - echo " $0 --old feature/mir # 对比 feature/mir 分支" - echo " $0 --old 70234dd # 对比特定 commit" + echo " $0 --old feature/mir # 对比 feature/mir 分支 vs 本地版本" + echo " $0 --old 70234dd --new 8ece3ac # 对比两个指定 commit" echo " $0 --old feature/mir --tests performance --mode run # 仅对比性能测试的运行时间" echo " $0 --old /tmp/old-compiler --no-build # 使用预构建的旧编译器" + echo " $0 --ref 5b8303d --mode asm # 统计 5b8303d 版本的汇编数据" + echo " $0 --ref feature/ra --tests performance # 统计 feature/ra 分支的性能数据" exit 1 } while [[ $# -gt 0 ]]; do case "$1" in --old) OLD_REF="$2"; shift 2 ;; + --new) NEW_REF="$2"; shift 2 ;; + --ref) SINGLE_REF="$2"; shift 2 ;; --mode) MODE="$2"; shift 2 ;; --tests) TEST_SET="$2"; shift 2 ;; + --runs) RUN_COUNT="$2"; shift 2 ;; --no-build) NO_BUILD=true; shift ;; --keep-worktree) KEEP_WORKTREE=true; shift ;; *) echo "未知参数: $1"; usage ;; esac done -if [[ -z "$OLD_REF" ]]; then - echo "错误: 必须指定 --old" +if [[ -n "$SINGLE_REF" ]]; then + # 单版本模式:--ref 与 --old / --new 互斥 + if [[ -n "$OLD_REF" || -n "$NEW_REF" ]]; then + echo "错误: --ref 与 --old / --new 互斥" + usage + fi +elif [[ -z "$OLD_REF" ]]; then + echo "错误: 必须指定 --old(对比模式)或 --ref(单版本模式)" usage fi @@ -139,6 +165,12 @@ setup_old_compiler() { # ========== 确保新版编译器存在 ========== setup_new_compiler() { + # 如果指定了 --new ,从 worktree 构建新版编译器 + if [[ -n "$NEW_REF" ]]; then + setup_new_compiler_from_ref + return + fi + if [[ ! -x "$NEW_COMPILER_PATH" ]]; then echo "错误: 新编译器不存在,请先构建: cmake -B build && cmake --build build -j" exit 1 @@ -147,6 +179,178 @@ setup_new_compiler() { echo "新版编译器: $NEW_COMPILER" } +setup_new_compiler_from_ref() { + if [[ "$NO_BUILD" == true ]]; then + if [[ ! -x "$NEW_REF" ]]; then + echo "错误: 新编译器路径不存在或不可执行: $NEW_REF" + exit 1 + fi + NEW_COMPILER="$(realpath "$NEW_REF")" + echo "使用预构建新编译器: $NEW_COMPILER" + return + fi + + if ! git rev-parse --verify "$NEW_REF" >/dev/null 2>&1; then + echo "错误: '$NEW_REF' 不是有效的 git 引用(分支/commit)" + exit 1 + fi + + NEW_WORKTREE_DIR="$PROJECT_DIR/.worktree-new-$(echo "$NEW_REF" | tr '/' '-')" + local new_build_dir="$NEW_WORKTREE_DIR/build" + + echo "=== 准备新版编译器 ===" + echo "Git 引用: $NEW_REF" + echo "Worktree: $NEW_WORKTREE_DIR" + + if [[ -d "$NEW_WORKTREE_DIR" ]]; then + echo "移除已有 worktree..." + git worktree remove --force "$NEW_WORKTREE_DIR" 2>/dev/null || rm -rf "$NEW_WORKTREE_DIR" + fi + + git worktree add "$NEW_WORKTREE_DIR" "$NEW_REF" + echo "Worktree 已创建: $NEW_WORKTREE_DIR" + + echo "生成 ANTLR 语法分析器..." + java -jar "$NEW_WORKTREE_DIR/third_party/antlr-4.13.2-complete.jar" \ + -Dlanguage=Cpp \ + -visitor -no-listener \ + -Xexact-output-dir \ + -o "$new_build_dir/generated/antlr4" \ + "$NEW_WORKTREE_DIR/src/antlr4/SysY.g4" > "$RESULT_DIR/build_new.log" 2>&1 || { + echo "错误: ANTLR 生成失败,日志:" + tail -20 "$RESULT_DIR/build_new.log" + exit 1 + } + + echo "构建新版编译器..." + cmake -S "$NEW_WORKTREE_DIR" -B "$new_build_dir" -DCMAKE_BUILD_TYPE=Release >> "$RESULT_DIR/build_new.log" 2>&1 || { + echo "错误: 新版 cmake 配置失败,日志:" + tail -20 "$RESULT_DIR/build_new.log" + exit 1 + } + cmake --build "$new_build_dir" -j"$(nproc 2>/dev/null || echo 4)" >> "$RESULT_DIR/build_new.log" 2>&1 || { + echo "错误: 新版构建失败,日志:" + tail -30 "$RESULT_DIR/build_new.log" + exit 1 + } + + NEW_COMPILER="$new_build_dir/bin/compiler" + if [[ ! -x "$NEW_COMPILER" ]]; then + echo "错误: 新编译器未生成: $NEW_COMPILER" + exit 1 + fi + echo "新编译器已构建: $NEW_COMPILER" +} + +# ========== 单版本模式:构建编译器 ========== +setup_ref_compiler() { + if [[ "$NO_BUILD" == true ]]; then + if [[ ! -x "$SINGLE_REF" ]]; then + echo "错误: 编译器路径不存在或不可执行: $SINGLE_REF" + exit 1 + fi + NEW_COMPILER="$(realpath "$SINGLE_REF")" + echo "使用预构建编译器: $NEW_COMPILER" + return + fi + + if ! git rev-parse --verify "$SINGLE_REF" >/dev/null 2>&1; then + echo "错误: '$SINGLE_REF' 不是有效的 git 引用(分支/commit),或使用 --no-build 指定编译器路径" + exit 1 + fi + + SINGLE_WORKTREE_DIR="$PROJECT_DIR/.worktree-ref-$(echo "$SINGLE_REF" | tr '/' '-')" + local build_dir="$SINGLE_WORKTREE_DIR/build" + + echo "=== 准备编译器 ===" + echo "Git 引用: $SINGLE_REF" + echo "Worktree: $SINGLE_WORKTREE_DIR" + + if [[ -d "$SINGLE_WORKTREE_DIR" ]]; then + echo "移除已有 worktree..." + git worktree remove --force "$SINGLE_WORKTREE_DIR" 2>/dev/null || rm -rf "$SINGLE_WORKTREE_DIR" + fi + + git worktree add "$SINGLE_WORKTREE_DIR" "$SINGLE_REF" + echo "Worktree 已创建: $SINGLE_WORKTREE_DIR" + + echo "生成 ANTLR 语法分析器..." + java -jar "$SINGLE_WORKTREE_DIR/third_party/antlr-4.13.2-complete.jar" \ + -Dlanguage=Cpp \ + -visitor -no-listener \ + -Xexact-output-dir \ + -o "$build_dir/generated/antlr4" \ + "$SINGLE_WORKTREE_DIR/src/antlr4/SysY.g4" > "$RESULT_DIR/build.log" 2>&1 || { + echo "错误: ANTLR 生成失败,日志:" + tail -20 "$RESULT_DIR/build.log" + exit 1 + } + + echo "构建编译器..." + cmake -S "$SINGLE_WORKTREE_DIR" -B "$build_dir" -DCMAKE_BUILD_TYPE=Release >> "$RESULT_DIR/build.log" 2>&1 || { + echo "错误: cmake 配置失败,日志:" + tail -20 "$RESULT_DIR/build.log" + exit 1 + } + cmake --build "$build_dir" -j"$(nproc 2>/dev/null || echo 4)" >> "$RESULT_DIR/build.log" 2>&1 || { + echo "错误: 构建失败,日志:" + tail -30 "$RESULT_DIR/build.log" + exit 1 + } + + NEW_COMPILER="$build_dir/bin/compiler" + if [[ ! -x "$NEW_COMPILER" ]]; then + echo "错误: 编译器未生成: $NEW_COMPILER" + exit 1 + fi + echo "编译器已构建: $NEW_COMPILER" +} + +# ========== 版本信息 ========== +# 获取 worktree 目录中的 commit 信息 +get_worktree_commit() { + local wt_dir="$1" + if [[ -d "$wt_dir/.git" ]] || git -C "$wt_dir" rev-parse --git-dir >/dev/null 2>&1; then + git -C "$wt_dir" log -1 --format="%h %s" 2>/dev/null || echo "unknown" + else + echo "unknown" + fi +} + +# 获取本地仓库状态描述 +get_local_version() { + local commit; commit=$(git log -1 --format="%h %s" 2>/dev/null || echo "unknown") + if git status --porcelain | grep -q '^[ M]'; then + echo "$commit (+uncommitted changes)" + else + echo "$commit" + fi +} + +show_compiler_versions() { + echo "" + echo "=== 编译器版本信息 ===" + + # 旧版版本 + if [[ "$NO_BUILD" == true ]]; then + echo "旧版: $OLD_REF (预构建)" + elif [[ -n "$WORKTREE_DIR" && -d "$WORKTREE_DIR" ]]; then + echo "旧版: $(get_worktree_commit "$WORKTREE_DIR")" + fi + + # 新版版本 + if [[ -n "$NEW_REF" ]]; then + if [[ "$NO_BUILD" == true ]]; then + echo "新版: $NEW_REF (预构建)" + elif [[ -n "$NEW_WORKTREE_DIR" && -d "$NEW_WORKTREE_DIR" ]]; then + echo "新版: $(get_worktree_commit "$NEW_WORKTREE_DIR")" + fi + else + echo "新版: $(get_local_version)" + fi + echo "" +} + # ========== 工具检查 ========== check_tools() { if [[ "$NO_BUILD" == false ]]; then @@ -341,11 +545,289 @@ compare_run() { > "$RESULT_DIR/run/$stem.result" } -# ========== 主流程 ========== -main() { +# ========== 单版本汇编分析 ========== +analyze_asm() { + local test_file="$1" + local stem; stem=$(basename "$test_file" .sy) + + local asm="$RESULT_DIR/asm/$stem.s" + + mkdir -p "$RESULT_DIR/asm" + + "$NEW_COMPILER" --emit-asm "$test_file" > "$asm" 2>/dev/null || { + echo "COMPILE_FAIL" > "$RESULT_DIR/asm/$stem.result" + return + } + + local inst mem branches + inst=$(grep -cE '^\s+\w+\s' "$asm" 2>/dev/null || echo 0) + mem=$(grep -cE '\b(ldr|str|ldur|stur|ldp|stp)\b' "$asm" 2>/dev/null || echo 0) + branches=$(grep -cE '\bb(|\.\w+)\s' "$asm" 2>/dev/null || echo 0) + + echo "$stem $inst $mem $branches" > "$RESULT_DIR/asm/$stem.result" +} + +# ========== 单版本运行分析 ========== +analyze_run() { + local test_file="$1" + local stem; stem=$(basename "$test_file" .sy) + local test_dir; test_dir=$(dirname "$test_file") + + local exe="$RESULT_DIR/run/$stem" + local stdin_file="$test_dir/$stem.in" + local expected_file="$test_dir/$stem.out" + + mkdir -p "$RESULT_DIR/run" + + local asm="$RESULT_DIR/run/$stem.s" + "$NEW_COMPILER" --emit-asm "$test_file" > "$asm" 2>/dev/null || { + echo "COMPILE_FAIL" > "$RESULT_DIR/run/$stem.result" + return + } + aarch64-linux-gnu-gcc -no-pie "$asm" -L"$PROJECT_DIR/sylib" -lsysy -static -o "$exe" 2>/dev/null || { + echo "LINK_FAIL" > "$RESULT_DIR/run/$stem.result" + return + } + + # 多次执行收集时间数据 + local times=() total_sec=0 match="N" rc="N/A" + set +eo pipefail + for ((run=1; run<=RUN_COUNT; run++)); do + local out="$RESULT_DIR/run/$stem.run$run.out" + local time_val="N/A" + if [[ -f "$stdin_file" ]]; then + time_val=$( { time qemu-aarch64 -L /usr/aarch64-linux-gnu "$exe" < "$stdin_file" > "$out" 2>/dev/null; echo $? > "$out.rc"; } 2>&1 | grep real | awk '{print $2}' || echo "N/A") + else + time_val=$( { time qemu-aarch64 -L /usr/aarch64-linux-gnu "$exe" > "$out" 2>/dev/null; echo $? > "$out.rc"; } 2>&1 | grep real | awk '{print $2}' || echo "N/A") + fi + rc=$(cat "$out.rc" 2>/dev/null || echo "1") + times+=("$time_val") + + # 转换为秒并累加 + local sec + sec=$(echo "$time_val" | sed 's/m/ /;s/s//' | awk '{printf "%.3f", $1 * 60 + $2}' 2>/dev/null || echo 0) + total_sec=$(echo "scale=3; $total_sec + $sec" | bc 2>/dev/null || echo "$total_sec") + done + set -eo pipefail + + # 计算平均时间 + local avg_time="N/A" + if [[ "$(echo "$RUN_COUNT > 0" | bc -l 2>/dev/null)" == "1" ]]; then + local avg_sec + avg_sec=$(echo "scale=3; $total_sec / $RUN_COUNT" | bc 2>/dev/null || echo "0") + # 格式化为 m:ss.s 格式 + local avg_min avg_s + avg_min=$(echo "scale=0; $avg_sec / 1" | bc 2>/dev/null || echo 0) + avg_min=$(( avg_min / 60 )) + avg_s=$(echo "scale=3; $avg_sec - $avg_min * 60" | bc 2>/dev/null || echo "0") + avg_time=$(printf "%dm%.3fs" "$avg_min" "$avg_s") + fi + + # 检查输出匹配(使用最后一次运行的输出) + local last_out="$RESULT_DIR/run/$stem.run$RUN_COUNT.out" + local actual="$RESULT_DIR/run/$stem.actual" + { + cat "$last_out" + if [[ -s "$last_out" ]] && (( $(tail -c 1 "$last_out" | wc -l) == 0 )); then + printf '\n' + fi + printf '%s\n' "$rc" + } > "$actual" + + if [[ -f "$expected_file" ]]; then + diff -w -q "$actual" "$expected_file" >/dev/null 2>&1 && match="Y" + fi + + # 保存结果: stem run1 run2 ... runN avg match rc + printf '%s' "$stem" > "$RESULT_DIR/run/$stem.result" + for t in "${times[@]}"; do + printf ' %s' "$t" >> "$RESULT_DIR/run/$stem.result" + done + printf ' %s %s %s\n' "$avg_time" "$match" "$rc" >> "$RESULT_DIR/run/$stem.result" +} + +# ========== 单版本主流程 ========== +main_single() { + echo "=============================================" + echo " 寄存器分配编译器 — 单版本统计" + echo " 版本: $SINGLE_REF" + echo " 测试集: $TEST_SET" + echo " 模式: $MODE" + echo "=============================================" + echo "" + + check_tools + setup_ref_compiler + + # 版本信息 + echo "" + echo "=== 编译器版本信息 ===" + if [[ "$NO_BUILD" == true ]]; then + echo "版本: $SINGLE_REF (预构建)" + elif [[ -n "$SINGLE_WORKTREE_DIR" && -d "$SINGLE_WORKTREE_DIR" ]]; then + echo "版本: $(get_worktree_commit "$SINGLE_WORKTREE_DIR")" + fi + echo "" + + local tests + mapfile -t tests < <(get_tests) + local total=${#tests[@]} + echo "共 $total 个测试用例" + echo "" + + # ========== 汇编统计 ========== + if [[ "$MODE" == "asm" || "$MODE" == "all" ]]; then + echo "=== 汇编统计 ===" + local count=0 + for test_file in "${tests[@]}"; do + analyze_asm "$test_file" + count=$((count + 1)) + printf "\r 进度: %d/%d" "$count" "$total" + done + echo "" + + echo "" + printf "%-30s %8s %8s %8s\n" \ + "测试用例" "指令数" "访存数" "分支数" + printf "%-30s %8s %8s %8s\n" \ + "------------------------------" "--------" "--------" "--------" + + local total_inst=0 total_mem=0 total_branches=0 valid_count=0 + for f in "$RESULT_DIR/asm"/*.result; do + [[ -f "$f" ]] || continue + local result + result=$(cat "$f") + if [[ "$result" == *"FAIL"* ]]; then + printf "%-30s %8s\n" "$(basename "$f" .result)" "编译失败" + continue + fi + read -r stem inst mem branches <<< "$result" + printf "%-30s %8d %8d %8d\n" "$stem" "$inst" "$mem" "$branches" + total_inst=$((total_inst + inst)) + total_mem=$((total_mem + mem)) + total_branches=$((total_branches + branches)) + valid_count=$((valid_count + 1)) + done + + if [[ "$valid_count" -gt 0 ]]; then + printf "%-30s %8d %8d %8d\n" \ + "--- 合计 ---" "$total_inst" "$total_mem" "$total_branches" + fi + echo "" + echo "汇编文件: $RESULT_DIR/asm/*.s" + fi + + # ========== 运行统计 ========== + if [[ "$MODE" == "run" || "$MODE" == "all" ]]; then + echo "=== 运行统计 ===" + local count=0 + for test_file in "${tests[@]}"; do + analyze_run "$test_file" + count=$((count + 1)) + printf "\r 进度: %d/%d" "$count" "$total" + done + echo "" + + # 动态表头:每个 run 一列 + 平均 + 匹配 + 退出码 + echo "" + if [[ "$RUN_COUNT" -eq 1 ]]; then + printf "%-30s %10s %6s %8s\n" \ + "测试用例" "耗时" "匹配" "退出码" + printf "%-30s %10s %6s %8s\n" \ + "------------------------------" "----------" "------" "--------" + else + printf "%-30s" "测试用例" + for ((i=1; i<=RUN_COUNT; i++)); do printf " %10s" "Run$i"; done + printf " %10s %6s %8s\n" "平均" "匹配" "退出码" + printf "%-30s" "------------------------------" + for ((i=1; i<=RUN_COUNT; i++)); do printf " %10s" "----------"; done + printf " %10s %6s %8s\n" "----------" "------" "--------" + fi + + local pass=0 total_valid=0 + local total_avg_sec=0 + for f in "$RESULT_DIR/run"/*.result; do + [[ -f "$f" ]] || continue + local result + result=$(cat "$f") + if [[ "$result" == *"FAIL"* ]]; then + printf "%-30s %10s\n" "$(basename "$f" .result)" "$result" + continue + fi + + # 解析: stem run1 run2 ... runN avg match rc + local stem match rc avg_time + local times=() + read -r stem rest <<< "$result" + # rest 包含所有 run 时间 + avg + match + rc + # 从后往前取: rc, match, avg, then remaining are run times + set -- $rest + local num_runs=$RUN_COUNT + # total fields = num_runs + 2 (avg + match + rc) → wait: avg is already one field + # Actually: fields = run1 run2 ... runN avg match rc + # So last field = rc, second to last = match, third to last = avg + # Everything before that = run times + local total_fields=$# + rc="${@: -1}" + match="${@: -2:1}" + avg_time="${@: -3:1}" + # run times are fields 1 to (total_fields - 3) + local run_fields=$((total_fields - 3)) + for ((i=1; i<=run_fields; i++)); do + times+=("${!i}") + done + + # 输出行 + if [[ "$RUN_COUNT" -eq 1 ]]; then + printf "%-30s %10s %6s %8s\n" "$stem" "${times[0]}" "$match" "$rc" + else + printf "%-30s" "$stem" + for t in "${times[@]}"; do printf " %10s" "$t"; done + printf " %10s %6s %8s\n" "$avg_time" "$match" "$rc" + fi + [[ "$match" == "Y" ]] && pass=$((pass + 1)) + total_valid=$((total_valid + 1)) + + # 累加平均时间(转换为秒) + local sec + sec=$(echo "$avg_time" | sed 's/m/ /;s/s//' | awk '{printf "%.3f", $1 * 60 + $2}' 2>/dev/null || echo 0) + total_avg_sec=$(echo "scale=3; $total_avg_sec + $sec" | bc 2>/dev/null || echo "$total_avg_sec") + done + + if [[ "$total_valid" -gt 0 ]]; then + echo "" + printf "输出匹配率: %d/%d\n" "$pass" "$total_valid" + echo "" + if [[ "$RUN_COUNT" -eq 1 ]]; then + printf "%-30s %10.3f\n" "--- 总时间 (秒) ---" "$total_avg_sec" + else + printf "%-30s %10.3f\n" "--- 总平均时间 (秒) ---" "$total_avg_sec" + fi + fi + fi + + # ========== 清理 ========== + if [[ "$KEEP_WORKTREE" == false ]]; then + if [[ -n "$SINGLE_WORKTREE_DIR" ]]; then + echo "" + echo "清理 worktree..." + git worktree remove --force "$SINGLE_WORKTREE_DIR" 2>/dev/null || true + fi + fi + + echo "" + echo "统计结果保存在: $RESULT_DIR" +} + +main_compare() { echo "=============================================" echo " 寄存器分配编译器对比" echo " 旧版: $OLD_REF" + if [[ -n "$NEW_REF" ]]; then + echo " 新版: $NEW_REF" + else + echo " 新版: 本地构建 (./build/bin/compiler)" + fi echo " 测试集: $TEST_SET" echo " 模式: $MODE" echo "=============================================" @@ -354,6 +836,7 @@ main() { check_tools setup_old_compiler setup_new_compiler + show_compiler_versions local tests mapfile -t tests < <(get_tests) @@ -428,6 +911,7 @@ main() { "------------------------------" "----------" "----------" "--------" "------" "------" "--------" "--------" local pass_old=0 pass_new=0 total_valid=0 + local total_old_sec=0 total_new_sec=0 for f in "$RESULT_DIR/run"/*.result; do [[ -f "$f" ]] || continue local result @@ -442,23 +926,53 @@ main() { [[ "$old_match" == "Y" ]] && pass_old=$((pass_old + 1)) [[ "$new_match" == "Y" ]] && pass_new=$((pass_new + 1)) total_valid=$((total_valid + 1)) + + # 累加时间(转换为秒) + local old_sec new_sec + old_sec=$(echo "$old_time" | sed 's/m/ /;s/s//' | awk '{printf "%.3f", $1 * 60 + $2}' 2>/dev/null || echo 0) + new_sec=$(echo "$new_time" | sed 's/m/ /;s/s//' | awk '{printf "%.3f", $1 * 60 + $2}' 2>/dev/null || echo 0) + total_old_sec=$(echo "scale=3; $total_old_sec + $old_sec" | bc 2>/dev/null || echo "$total_old_sec") + total_new_sec=$(echo "scale=3; $total_new_sec + $new_sec" | bc 2>/dev/null || echo "$total_new_sec") done if [[ "$total_valid" -gt 0 ]]; then echo "" printf "输出匹配率: 旧版 %d/%d, 新版 %d/%d\n" "$pass_old" "$total_valid" "$pass_new" "$total_valid" + + # 总时间汇总行 + local total_speedup="N/A" + if [[ "$(echo "$total_new_sec > 0" | bc -l 2>/dev/null)" == "1" ]]; then + total_speedup=$(echo "scale=2; $total_old_sec / $total_new_sec" | bc 2>/dev/null || echo "N/A") + fi + echo "" + printf "%-30s %10s %10s %8s\n" "--- 总时间 ---" "旧版(秒)" "新版(秒)" "总加速比" + printf "%-30s %10.3f %10.3f %8s\n" "全部测试" "$total_old_sec" "$total_new_sec" "$total_speedup" fi fi # ========== 清理 ========== - if [[ -n "$WORKTREE_DIR" && "$KEEP_WORKTREE" == false ]]; then - echo "" - echo "清理 worktree..." - git worktree remove --force "$WORKTREE_DIR" 2>/dev/null || true + if [[ "$KEEP_WORKTREE" == false ]]; then + if [[ -n "$WORKTREE_DIR" ]]; then + echo "" + echo "清理旧 worktree..." + git worktree remove --force "$WORKTREE_DIR" 2>/dev/null || true + fi + if [[ -n "$NEW_WORKTREE_DIR" ]]; then + echo "清理新 worktree..." + git worktree remove --force "$NEW_WORKTREE_DIR" 2>/dev/null || true + fi fi echo "" echo "对比结果保存在: $RESULT_DIR" } +main() { + if [[ -n "$SINGLE_REF" ]]; then + main_single + else + main_compare + fi +} + main \ No newline at end of file