#!/bin/bash set -e set -o pipefail RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' TEST_ROOT="./test_merged" OUTPUT_DIR="./lab5_results" COMPILER="./build/bin/compiler" VERIFY_SCRIPT="./scripts/verify_asm.sh" BASELINE_DIR="./lab3_results" MAX_CASES=0 STOP_ON_FIRST_FAILURE=false START_FROM=1 KEEP_OLD=false show_help() { cat << 'EOF' 用法: ./test5.sh [选项] 说明: 批量执行 Lab5(寄存器分配优化)测试。 默认会递归扫描测试目录下所有 .sy 文件, 使用寄存器分配优化编译并运行,验证输出正确性。 结果保存在 lab5_results,与 lab3_results 对比。 选项: -h, --help 显示此帮助信息 -n, --max-cases N 最多运行 N 个测试用例 (0=不限制) -x, --stop-on-failure 遇到第一个失败即停止 -s, --start-from N 从第 N 个测试开始 -k, --keep 保留旧输出目录,不删除 -t, --test-root DIR 指定测试目录 -o, --output-dir DIR 指定输出目录 示例: ./test5.sh # 运行所有测试 ./test5.sh -n 10 # 只运行前10个 ./test5.sh -s # 失败即停止 ./test5.sh --start-from 5 # 从第5个开始 EOF } parse_args() { while [[ $# -gt 0 ]]; do case $1 in -h|--help) show_help exit 0 ;; -n|--max-cases) MAX_CASES="$2" shift 2 ;; -x|--stop-on-failure) STOP_ON_FIRST_FAILURE=true shift ;; -s|--start-from) START_FROM="$2" shift 2 ;; -k|--keep) KEEP_OLD=true shift ;; -t|--test-root) TEST_ROOT="$2" shift 2 ;; -o|--output-dir) OUTPUT_DIR="$2" shift 2 ;; *) echo -e "${RED}未知参数: $1${NC}" show_help exit 1 ;; esac done } parse_args "$@" if [[ ! -x "$COMPILER" ]]; then echo -e "${RED}错误: 编译器不存在或不可执行: $COMPILER${NC}" echo "请先编译项目: cd build && cmake .. && make -j4" exit 1 fi if [[ ! -d "$TEST_ROOT" ]]; then echo -e "${RED}错误: 测试目录不存在: $TEST_ROOT${NC}" exit 1 fi if [[ ! -x "$VERIFY_SCRIPT" ]]; then echo -e "${RED}错误: verify脚本不可执行: $VERIFY_SCRIPT${NC}" exit 1 fi # 创建输出目录 if [[ "$KEEP_OLD" != "true" ]]; then rm -rf "$OUTPUT_DIR" fi mkdir -p "$OUTPUT_DIR" # 初始化统计文件 LOG_FILE="$OUTPUT_DIR/lab5_batch.log" FAIL_FILE="$OUTPUT_DIR/failed_cases.txt" ERROR_LOG_FILE="$OUTPUT_DIR/error_log.txt" TIME_SUMMARY_FILE="$OUTPUT_DIR/time_summary.txt" OPT_REPORT_FILE="$OUTPUT_DIR/optimization_report.txt" if [[ "$KEEP_OLD" != "true" ]]; then : > "$LOG_FILE" : > "$FAIL_FILE" : > "$ERROR_LOG_FILE" : > "$TIME_SUMMARY_FILE" fi # 初始化优化报告 { echo "================================================" echo "Lab5 寄存器分配优化对比报告" echo "================================================" echo "测试模式: ASM" echo "基线目录: $BASELINE_DIR" echo "生成时间: $(date)" echo "================================================" echo "" echo "格式: 测试用例 | 基线时间(ms) | 优化后时间(ms) | 提升幅度" echo "------------------------------------------------" } > "$OPT_REPORT_FILE" echo "Lab5 批量测试日志 - $(date)" >> "$LOG_FILE" echo "TEST_ROOT=$TEST_ROOT" >> "$LOG_FILE" echo "OUTPUT_DIR=$OUTPUT_DIR" >> "$LOG_FILE" echo "BASELINE_DIR=$BASELINE_DIR" >> "$LOG_FILE" echo "MAX_CASES=$MAX_CASES" >> "$LOG_FILE" echo "================================================" >> "$LOG_FILE" collect_sy_files() { find "$TEST_ROOT" -type f -name '*.sy' | sort } # 计算优化提升百分比 calc_improvement() { local old_time="$1" local new_time="$2" if [[ -z "$old_time" || -z "$new_time" ]]; then echo "" return fi if [[ "$old_time" -eq 0 ]]; then echo "" return fi local improvement=$(awk -v old="$old_time" -v new="$new_time" 'BEGIN { printf "%.2f", ((old - new) / old) * 100 }') echo "$improvement" } run_single_test() { local sy_file="$1" local case_num="$2" local filename=$(basename "$sy_file") local base_name="${filename%.sy}" # 获取相对路径(去掉 TEST_ROOT 前缀) local rel_path="${sy_file#$TEST_ROOT/}" local rel_dir=$(dirname "$rel_path") # 如果 rel_dir 是 ".",则设为空字符串 if [[ "$rel_dir" == "." ]]; then rel_dir="" fi # 构建输出目录 local case_out_dir="$OUTPUT_DIR" if [[ -n "$rel_dir" ]]; then case_out_dir="$OUTPUT_DIR/$rel_dir" fi mkdir -p "$case_out_dir" # 删除旧的时间文件 rm -f "$case_out_dir/${base_name}_time.txt" echo -ne "${YELLOW}[$case_num] $filename ... ${NC}" # 调用 verify 脚本(临时禁用 set -e) set +e "$VERIFY_SCRIPT" "$sy_file" "$case_out_dir" --run > /dev/null 2>&1 code=$? set -e # 读取优化后的运行时间 local opt_time_file="$case_out_dir/${base_name}_time.txt" local opt_time="" if [[ -f "$opt_time_file" ]]; then opt_time=$(cat "$opt_time_file") fi # 读取基线时间 local baseline_time="" local baseline_time_file="" # 构建基线时间文件的搜索路径 local baseline_search_paths=( "$BASELINE_DIR/$rel_path/${base_name}_time.txt" "$BASELINE_DIR/$rel_dir/${base_name}_time.txt" ) # 如果上面找不到,则用 find 查找 if [[ ! -f "${baseline_search_paths[0]}" ]] && [[ ! -f "${baseline_search_paths[1]}" ]]; then baseline_time_file=$(find "$BASELINE_DIR" -name "${base_name}_time.txt" 2>/dev/null | head -n 1) fi # 尝试从搜索路径中查找 for path in "${baseline_search_paths[@]}"; do if [[ -f "$path" ]]; then baseline_time_file="$path" break fi done if [[ -f "$baseline_time_file" ]]; then baseline_time=$(cat "$baseline_time_file") fi if [[ $code -eq 0 ]]; then SUCCESS=$((SUCCESS + 1)) # 显示运行时间和优化对比 if [[ -n "$opt_time" && "$opt_time" =~ ^[0-9]+$ ]]; then # 累加统计 total_time_sum=$((total_time_sum + opt_time)) time_cases_count=$((time_cases_count + 1)) # 写入汇总文件 echo "$rel_path: ${opt_time}ms" >> "$TIME_SUMMARY_FILE" # 先打印"成功",再打印时间对比 echo -ne "${GREEN}成功${NC} " # 优化对比 if [[ -n "$baseline_time" && "$baseline_time" =~ ^[0-9]+$ ]]; then improvement=$(calc_improvement "$baseline_time" "$opt_time") if [[ -n "$improvement" ]]; then if (( $(echo "$improvement > 0" | bc -l 2>/dev/null || echo "0") )); then echo -ne "${GREEN}(${baseline_time}ms->${opt_time}ms,+${improvement}%)${NC}" total_improvement_sum=$(echo "$total_improvement_sum + $improvement" | bc -l 2>/dev/null || echo "0") improvement_count=$((improvement_count + 1)) elif (( $(echo "$improvement < 0" | bc -l 2>/dev/null || echo "0") )); then echo -ne "${RED}(${baseline_time}ms->${opt_time}ms,${improvement}%)${NC}" total_degradation_sum=$(echo "$total_degradation_sum + (0 - $improvement)" | bc -l 2>/dev/null || echo "0") degradation_count=$((degradation_count + 1)) else echo -ne "(${baseline_time}ms->${opt_time}ms,+0.00%)" fi # 写入优化报告 echo "$rel_path | $baseline_time | $opt_time | ${improvement}%" >> "$OPT_REPORT_FILE" else echo -ne "(${baseline_time}ms->${opt_time}ms)" fi else echo -ne "(${opt_time}ms,无基线数据)" fi echo "" else echo -e "${GREEN}成功${NC} (运行时间: N/A)" fi echo "[SUCCESS] $sy_file" >> "$LOG_FILE" else FAILED=$((FAILED + 1)) echo "$sy_file" >> "$FAIL_FILE" # 先打印"失败",再显示时间(如果有) if [[ -n "$opt_time" && "$opt_time" =~ ^[0-9]+$ ]]; then echo -e "${RED}失败${NC} (${opt_time}ms)" else echo -e "${RED}失败${NC}" fi echo "[FAILED] $sy_file (exit=$code)" >> "$LOG_FILE" # 写入错误日志 echo "========================================" >> "$ERROR_LOG_FILE" echo "测试失败: $sy_file" >> "$ERROR_LOG_FILE" echo "退出码: $code" >> "$ERROR_LOG_FILE" echo "时间: $(date)" >> "$ERROR_LOG_FILE" echo "========================================" >> "$ERROR_LOG_FILE" if [[ "$STOP_ON_FIRST_FAILURE" = true ]]; then echo -e "${RED}========================================${NC}" echo -e "${RED}在第一个失败处停止测试${NC}" echo -e "${RED}失败文件: $sy_file${NC}" echo -e "${RED}日志: $LOG_FILE${NC}" echo -e "${RED}错误日志: $ERROR_LOG_FILE${NC}" echo -e "${RED}========================================${NC}" return 1 fi fi return 0 } main() { local sy_files=($(collect_sy_files)) local total=${#sy_files[@]} if [[ $total -eq 0 ]]; then echo -e "${YELLOW}警告: 未找到任何测试文件${NC}" exit 0 fi echo -e "${BLUE}========================================${NC}" echo -e "${BLUE}Lab5 寄存器分配优化测试 (ASM模式)${NC}" echo -e "${BLUE}========================================${NC}" echo "测试目录: $TEST_ROOT" echo "输出目录: $OUTPUT_DIR" echo "基线目录: $BASELINE_DIR" echo "测试用例总数: $total" echo "" SUCCESS=0 FAILED=0 case_num=0 # 统计变量 total_time_sum=0 time_cases_count=0 total_improvement_sum=0 improvement_count=0 total_degradation_sum=0 degradation_count=0 for sy_file in "${sy_files[@]}"; do case_num=$((case_num + 1)) if [[ $case_num -lt $START_FROM ]]; then continue fi # 检查实际执行的用例数是否达到限制 local executed=$((case_num - START_FROM + 1)) if [[ $MAX_CASES -gt 0 && $executed -gt $MAX_CASES ]]; then break fi set +e run_single_test "$sy_file" "$case_num" test_result=$? set -e if [[ $test_result -eq 1 ]] && [[ "$STOP_ON_FIRST_FAILURE" = true ]]; then break fi done # 添加优化报告尾部 { echo "" echo "================================================" echo "优化对比摘要" echo "================================================" if [[ $improvement_count -gt 0 ]]; then avg_improvement=$(echo "scale=2; $total_improvement_sum / $improvement_count" | bc 2>/dev/null || echo "0") echo "平均性能提升: ${avg_improvement}% (基于 $improvement_count 个测试)" fi if [[ $degradation_count -gt 0 ]]; then avg_degradation=$(echo "scale=2; $total_degradation_sum / $degradation_count" | bc 2>/dev/null || echo "0") echo "平均性能退化: ${avg_degradation}% (基于 $degradation_count 个测试)" fi echo "" echo "================================================" } >> "$OPT_REPORT_FILE" RATE="0.00" if [[ $case_num -gt 0 ]]; then RATE=$(awk -v s="$SUCCESS" -v t="$case_num" 'BEGIN { printf "%.2f", (s*100.0)/t }') fi echo "" echo -e "${BLUE}========================================${NC}" echo -e "${BLUE}Lab5 批量测试完成${NC}" echo -e "${BLUE}执行用例: $case_num${NC}" echo -e "${GREEN}成功: $SUCCESS${NC}" echo -e "${RED}失败: $FAILED${NC}" echo -e "${BLUE}成功率: ${RATE}%${NC}" # 显示平均运行时间 if [[ $time_cases_count -gt 0 ]]; then avg_time=$((total_time_sum / time_cases_count)) echo -e "${BLUE}平均运行时间: ${avg_time}ms (基于 ${time_cases_count} 个成功用例)${NC}" fi # 显示平均优化提升 if [[ $improvement_count -gt 0 ]]; then avg_improvement=$(echo "scale=2; $total_improvement_sum / $improvement_count" | bc 2>/dev/null || echo "0") echo -e "${GREEN}平均性能提升: ${avg_improvement}% (基于 ${improvement_count} 个测试)${NC}" fi if [[ $degradation_count -gt 0 ]]; then avg_degradation=$(echo "scale=2; $total_degradation_sum / $degradation_count" | bc 2>/dev/null || echo "0") echo -e "${RED}平均性能退化: ${avg_degradation}% (基于 ${degradation_count} 个测试)${NC}" fi echo -e "${BLUE}时间汇总: $TIME_SUMMARY_FILE${NC}" echo -e "${BLUE}优化报告: $OPT_REPORT_FILE${NC}" echo -e "${BLUE}日志: $LOG_FILE${NC}" if [[ $FAILED -gt 0 ]]; then echo -e "${RED}失败清单: $FAIL_FILE${NC}" echo -e "${RED}错误日志: $ERROR_LOG_FILE${NC}" fi echo -e "${BLUE}========================================${NC}" exit 0 } main