|
|
|
|
@ -6,13 +6,17 @@
|
|
|
|
|
# 性能分 = geometric_mean(参考时间 / 我们时间) × 100
|
|
|
|
|
# 正确分 = 通过用例数 / 总用例数 × 100
|
|
|
|
|
# 总分 = 正确分 × 0.5 + 性能分 × 0.5
|
|
|
|
|
#
|
|
|
|
|
# 支持 gcc / clang 双参考编译器,--ref both 同时对比。
|
|
|
|
|
# 参考编译先经 SysY→C 预处理(const int → #define),
|
|
|
|
|
# 编译失败的用例不计入性能分。
|
|
|
|
|
# ============================================================
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
|
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
|
COMPILER="${PROJECT_DIR}/build/bin/compiler"
|
|
|
|
|
TEST_DIR="${PROJECT_DIR}/2026test"
|
|
|
|
|
TEST_DIR="${PROJECT_DIR}/2026test" # 默认,可用 -d 覆盖
|
|
|
|
|
RESULT_DIR="${PROJECT_DIR}/score_results"
|
|
|
|
|
SYLIB="${PROJECT_DIR}/sylib/sylib.c"
|
|
|
|
|
|
|
|
|
|
@ -34,9 +38,28 @@ canon_diff() {
|
|
|
|
|
>/dev/null 2>&1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# ---- 编译器配置 ----
|
|
|
|
|
GCC="aarch64-linux-gnu-gcc"
|
|
|
|
|
GCC_COMPILE_FLAGS="-std=gnu89 -O2 -x c"
|
|
|
|
|
GCC_LINK_FLAGS="-static"
|
|
|
|
|
|
|
|
|
|
# 检测 clang:优先用交叉编译器,其次尝试系统 clang + target
|
|
|
|
|
CLANG=""
|
|
|
|
|
CLANG_TARGET="aarch64-linux-gnu"
|
|
|
|
|
for cand in aarch64-linux-gnu-clang clang; do
|
|
|
|
|
if command -v "$cand" >/dev/null 2>&1; then
|
|
|
|
|
CLANG="$cand"
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
CLANG_COMPILE_FLAGS="-std=gnu89 -x c"
|
|
|
|
|
if [[ -n "$CLANG" ]]; then
|
|
|
|
|
if [[ "$CLANG" != "aarch64-linux-gnu-clang" ]]; then
|
|
|
|
|
CLANG_COMPILE_FLAGS="$CLANG_COMPILE_FLAGS --target=$CLANG_TARGET"
|
|
|
|
|
fi
|
|
|
|
|
CLANG_COMPILE_FLAGS="$CLANG_COMPILE_FLAGS -fno-addrsig"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
QEMU="qemu-aarch64"
|
|
|
|
|
QEMU_FLAGS="-L /usr/aarch64-linux-gnu -s 209715200"
|
|
|
|
|
COMPILER_FLAGS="-O"
|
|
|
|
|
@ -44,6 +67,7 @@ TIMEOUT_SEC=300
|
|
|
|
|
CATEGORY="all"
|
|
|
|
|
JOBS="$(nproc)"
|
|
|
|
|
QUIET=false
|
|
|
|
|
REF="gcc" # gcc | clang | both
|
|
|
|
|
|
|
|
|
|
usage() {
|
|
|
|
|
cat <<EOF
|
|
|
|
|
@ -51,9 +75,11 @@ usage() {
|
|
|
|
|
|
|
|
|
|
选项:
|
|
|
|
|
-c, --category CAT 测试类别: functional, h_functional, performance, all (默认 all)
|
|
|
|
|
--ref REF 参考编译器: gcc (默认), clang, both
|
|
|
|
|
-t, --timeout SEC 单个用例超时秒数 (默认 $TIMEOUT_SEC)
|
|
|
|
|
-j, --jobs N 并行数 (默认 CPU 核数)
|
|
|
|
|
-q, --quiet 只输出最终分数
|
|
|
|
|
-d, --test-dir DIR 测试用例目录 (默认 2026test)
|
|
|
|
|
-h, --help 显示帮助
|
|
|
|
|
EOF
|
|
|
|
|
exit 0
|
|
|
|
|
@ -62,23 +88,44 @@ EOF
|
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
|
|
|
case "$1" in
|
|
|
|
|
-c|--category) CATEGORY="$2"; shift 2 ;;
|
|
|
|
|
-t|--timeout) TIMEOUT_SEC="$2"; shift 2 ;;
|
|
|
|
|
-j|--jobs) JOBS="$2"; shift 2 ;;
|
|
|
|
|
-q|--quiet) QUIET=true; shift ;;
|
|
|
|
|
-h|--help) usage ;;
|
|
|
|
|
--ref) REF="$2"; shift 2 ;;
|
|
|
|
|
-t|--timeout) TIMEOUT_SEC="$2"; shift 2 ;;
|
|
|
|
|
-j|--jobs) JOBS="$2"; shift 2 ;;
|
|
|
|
|
-q|--quiet) QUIET=true; shift ;;
|
|
|
|
|
-d|--test-dir) TEST_DIR="$2"; shift 2 ;;
|
|
|
|
|
-h|--help) usage ;;
|
|
|
|
|
*) echo -e "${RED}未知选项: $1${NC}"; usage ;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# 验证 --ref
|
|
|
|
|
if [[ "$REF" != "gcc" && "$REF" != "clang" && "$REF" != "both" ]]; then
|
|
|
|
|
echo -e "${RED}错误: --ref 必须是 gcc, clang 或 both${NC}"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# 环境检查
|
|
|
|
|
if [[ ! -x "$COMPILER" ]]; then
|
|
|
|
|
echo -e "${RED}错误: 编译器未找到: $COMPILER${NC}"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
if ! command -v "$GCC" &>/dev/null; then
|
|
|
|
|
|
|
|
|
|
USE_GCC=false
|
|
|
|
|
USE_CLANG=false
|
|
|
|
|
case "$REF" in
|
|
|
|
|
gcc) USE_GCC=true ;;
|
|
|
|
|
clang) USE_CLANG=true ;;
|
|
|
|
|
both) USE_GCC=true; USE_CLANG=true ;;
|
|
|
|
|
esac
|
|
|
|
|
|
|
|
|
|
if $USE_GCC && ! command -v "$GCC" &>/dev/null; then
|
|
|
|
|
echo -e "${RED}错误: 参考编译器未找到: $GCC${NC}"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
if $USE_CLANG && [[ -z "$CLANG" ]]; then
|
|
|
|
|
echo -e "${RED}错误: clang 未找到,安装: sudo apt install clang${NC}"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
mkdir -p "$RESULT_DIR"
|
|
|
|
|
|
|
|
|
|
@ -102,131 +149,120 @@ done
|
|
|
|
|
|
|
|
|
|
TOTAL_TESTS="${#TEST_FILES[@]}"
|
|
|
|
|
if [[ "$TOTAL_TESTS" -eq 0 ]]; then
|
|
|
|
|
echo -e "${RED}错误: 未找到测试用例${NC}"
|
|
|
|
|
echo -e "${RED}错误: 未找到测试用例 (TEST_DIR)${NC}"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# 构建参考标签
|
|
|
|
|
REF_LABEL=""
|
|
|
|
|
if $USE_GCC && $USE_CLANG; then
|
|
|
|
|
REF_LABEL="gcc -O2 + clang -O2"
|
|
|
|
|
elif $USE_GCC; then
|
|
|
|
|
REF_LABEL="gcc -O2"
|
|
|
|
|
else
|
|
|
|
|
REF_LABEL="clang -O2"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if ! $QUIET; then
|
|
|
|
|
echo -e "${BLUE}========================================================${NC}"
|
|
|
|
|
echo -e "${BLUE} 本地评分 (比赛计分公式)${NC}"
|
|
|
|
|
echo -e "${BLUE}========================================================${NC}"
|
|
|
|
|
echo -e "测试类别: ${CATEGORY}"
|
|
|
|
|
echo -e "用例数: ${TOTAL_TESTS}"
|
|
|
|
|
echo -e "参考编译器: ${GCC} ${GCC_COMPILE_FLAGS}"
|
|
|
|
|
echo -e "参考编译器: ${REF_LABEL}"
|
|
|
|
|
echo -e "超时: ${TIMEOUT_SEC}s 并行: ${JOBS}"
|
|
|
|
|
echo -e "${BLUE}========================================================${NC}"
|
|
|
|
|
echo ""
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
# 单个测试执行(通过临时脚本,避免函数导出兼容性问题)
|
|
|
|
|
# Phase 1: 创建工作目录 + 批量 SysY → C 预处理
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
|
|
|
|
run_one() {
|
|
|
|
|
local sy_file="$1"
|
|
|
|
|
local idx="$2"
|
|
|
|
|
local base_name cat_name work_dir in_file out_file
|
|
|
|
|
PREPROC_MAP="$RESULT_DIR/.preprocess_map"
|
|
|
|
|
: > "$PREPROC_MAP"
|
|
|
|
|
|
|
|
|
|
base_name="$(basename "${sy_file%.sy}")"
|
|
|
|
|
cat_name="$(basename "$(dirname "$sy_file")")"
|
|
|
|
|
for f in "${TEST_FILES[@]}"; do
|
|
|
|
|
base_name="$(basename "${f%.sy}")"
|
|
|
|
|
cat_name="$(basename "$(dirname "$f")")"
|
|
|
|
|
work_dir="$RESULT_DIR/$cat_name/$base_name"
|
|
|
|
|
mkdir -p "$work_dir"
|
|
|
|
|
echo "$f|$work_dir/gcc_input.c"
|
|
|
|
|
done > "$PREPROC_MAP"
|
|
|
|
|
|
|
|
|
|
python3 -c "
|
|
|
|
|
import re, os
|
|
|
|
|
with open('$PREPROC_MAP') as f:
|
|
|
|
|
for line in f:
|
|
|
|
|
line = line.strip()
|
|
|
|
|
if not line:
|
|
|
|
|
continue
|
|
|
|
|
sy_file, c_file = line.split('|')
|
|
|
|
|
os.makedirs(os.path.dirname(c_file), exist_ok=True)
|
|
|
|
|
with open(sy_file) as fin:
|
|
|
|
|
content = fin.read()
|
|
|
|
|
# const int X = V → #define X V (行首匹配,避免误替换)
|
|
|
|
|
content = re.sub(r'^const int (\w+) = ([^;]+);', r'#define \1 \2', content, flags=re.MULTILINE)
|
|
|
|
|
with open(c_file, 'w') as fout:
|
|
|
|
|
fout.write(content)
|
|
|
|
|
" 2>/dev/null
|
|
|
|
|
|
|
|
|
|
rm -f "$PREPROC_MAP"
|
|
|
|
|
|
|
|
|
|
in_file="${sy_file%.sy}.in"
|
|
|
|
|
out_file="${sy_file%.sy}.out"
|
|
|
|
|
|
|
|
|
|
# ---- 我们的编译器 ----
|
|
|
|
|
local our_asm="$work_dir/our.s" our_exe="$work_dir/our.exe" our_out="$work_dir/our.out"
|
|
|
|
|
local our_time=1 our_correct=0 our_compile_ok=0
|
|
|
|
|
|
|
|
|
|
if timeout 30 "$COMPILER" $COMPILER_FLAGS -S -o "$our_asm" "$sy_file" 2>/dev/null; then
|
|
|
|
|
if "$GCC" $GCC_LINK_FLAGS "$our_asm" "$SYLIB" -o "$our_exe" -lm 2>/dev/null; then
|
|
|
|
|
our_compile_ok=1
|
|
|
|
|
local start_ns end_ns exit_code=0
|
|
|
|
|
start_ns="$(date +%s%3N)"
|
|
|
|
|
if [[ -f "$in_file" ]]; then
|
|
|
|
|
timeout "$TIMEOUT_SEC" "$QEMU" $QEMU_FLAGS "$our_exe" < "$in_file" > "$our_out" 2>/dev/null || exit_code=$?
|
|
|
|
|
else
|
|
|
|
|
timeout "$TIMEOUT_SEC" "$QEMU" $QEMU_FLAGS "$our_exe" > "$our_out" 2>/dev/null || exit_code=$?
|
|
|
|
|
fi
|
|
|
|
|
end_ns="$(date +%s%3N)"
|
|
|
|
|
our_time=$((end_ns - start_ns))
|
|
|
|
|
if [[ $our_time -lt 1 ]]; then our_time=1; fi
|
|
|
|
|
|
|
|
|
|
if [[ $exit_code -eq 0 ]]; then
|
|
|
|
|
if [[ -f "$out_file" ]]; then
|
|
|
|
|
if diff -q "$our_out" "$out_file" >/dev/null 2>&1; then
|
|
|
|
|
our_correct=1
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
our_correct=1
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ---- 参考编译器 (gcc -O2) ----
|
|
|
|
|
local ref_asm="$work_dir/ref.s" ref_exe="$work_dir/ref.exe" ref_out="$work_dir/ref.out"
|
|
|
|
|
local ref_time=999999 ref_ok=0
|
|
|
|
|
|
|
|
|
|
if "$GCC" $GCC_COMPILE_FLAGS -S -o "$ref_asm" "$sy_file" 2>/dev/null; then
|
|
|
|
|
if "$GCC" $GCC_LINK_FLAGS "$ref_asm" "$SYLIB" -o "$ref_exe" -lm 2>/dev/null; then
|
|
|
|
|
local ref_start ref_end ref_exit=0
|
|
|
|
|
ref_start="$(date +%s%3N)"
|
|
|
|
|
if [[ -f "$in_file" ]]; then
|
|
|
|
|
timeout "$TIMEOUT_SEC" "$QEMU" $QEMU_FLAGS "$ref_exe" < "$in_file" > "$ref_out" 2>/dev/null || ref_exit=$?
|
|
|
|
|
else
|
|
|
|
|
timeout "$TIMEOUT_SEC" "$QEMU" $QEMU_FLAGS "$ref_exe" > "$ref_out" 2>/dev/null || ref_exit=$?
|
|
|
|
|
fi
|
|
|
|
|
ref_end="$(date +%s%3N)"
|
|
|
|
|
ref_time=$((ref_end - ref_start))
|
|
|
|
|
ref_ok=1
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
if [[ $ref_time -lt 1 ]]; then ref_time=1; fi
|
|
|
|
|
|
|
|
|
|
# 写结果文件
|
|
|
|
|
echo "$our_correct $our_time $ref_time $ref_ok $our_compile_ok" > "$work_dir/result.txt"
|
|
|
|
|
|
|
|
|
|
# 打印
|
|
|
|
|
if [[ $our_correct -eq 1 ]]; then
|
|
|
|
|
local ratio
|
|
|
|
|
ratio=$(awk "BEGIN { printf \"%.4f\", $ref_time / $our_time }" 2>/dev/null || echo "0")
|
|
|
|
|
printf "[${GREEN}AC${NC}] %-45s our=%6dms ref=%6dms ratio=%s\n" \
|
|
|
|
|
"$cat_name/$base_name" "$our_time" "$ref_time" "$ratio"
|
|
|
|
|
elif [[ $our_compile_ok -eq 0 ]]; then
|
|
|
|
|
printf "[${RED}CE${NC}] %-45s (编译失败)\n" "$cat_name/$base_name"
|
|
|
|
|
else
|
|
|
|
|
printf "[${RED}WA${NC}] %-45s our=%6dms ref=%6dms (输出不匹配)\n" \
|
|
|
|
|
"$cat_name/$base_name" "$our_time" "$ref_time"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
if ! $QUIET; then
|
|
|
|
|
echo -e "SysY→C 预处理完成 (${TOTAL_TESTS} 文件)"
|
|
|
|
|
echo ""
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
# 并行执行
|
|
|
|
|
# Phase 2: 并行执行所有测试
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
|
|
|
|
if ! $QUIET; then
|
|
|
|
|
echo -e "${BLUE}正在运行 ${TOTAL_TESTS} 个测试 (${JOBS} 并行)...${NC}"
|
|
|
|
|
echo ""
|
|
|
|
|
fi
|
|
|
|
|
run_worker() {
|
|
|
|
|
local idx="$1" sy_file="$2"
|
|
|
|
|
|
|
|
|
|
# 并行执行:使用后台任务 + wait 控制并发
|
|
|
|
|
run_with_args() {
|
|
|
|
|
local sy_file="$1"
|
|
|
|
|
local base_name cat_name work_dir in_file out_file
|
|
|
|
|
|
|
|
|
|
base_name="$(basename "${sy_file%.sy}")"
|
|
|
|
|
cat_name="$(basename "$(dirname "$sy_file")")"
|
|
|
|
|
work_dir="$RESULT_DIR/$cat_name/$base_name"
|
|
|
|
|
mkdir -p "$work_dir"
|
|
|
|
|
in_file="${sy_file%.sy}.in"
|
|
|
|
|
out_file="${sy_file%.sy}.out"
|
|
|
|
|
local gcc_src="$work_dir/gcc_input.c"
|
|
|
|
|
|
|
|
|
|
# ---- 我们的编译器 ----
|
|
|
|
|
local our_asm="$work_dir/our.s" our_exe="$work_dir/our.exe" our_out="$work_dir/our.out"
|
|
|
|
|
local our_time=1 our_correct=0 our_compile_ok=0
|
|
|
|
|
|
|
|
|
|
if timeout 30 "$COMPILER" $COMPILER_FLAGS -S -o "$our_asm" "$sy_file" 2>/dev/null; then
|
|
|
|
|
timeout 60 "$COMPILER" $COMPILER_FLAGS -S -o "$our_asm" "$sy_file" 2>/dev/null &
|
|
|
|
|
local our_comp_pid=$!
|
|
|
|
|
|
|
|
|
|
# ---- gcc 参考 ----
|
|
|
|
|
local gcc_ref_asm="$work_dir/ref_gcc.s" gcc_ref_exe="$work_dir/ref_gcc.exe" gcc_ref_out="$work_dir/ref_gcc.out"
|
|
|
|
|
local gcc_ref_time=999999 gcc_ref_ok=0
|
|
|
|
|
local gcc_comp_pid=0
|
|
|
|
|
|
|
|
|
|
if $USE_GCC; then
|
|
|
|
|
"$GCC" $GCC_COMPILE_FLAGS -S -o "$gcc_ref_asm" "$gcc_src" 2>/dev/null &
|
|
|
|
|
gcc_comp_pid=$!
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ---- clang 参考 ----
|
|
|
|
|
local clang_ref_asm="$work_dir/ref_clang.s" clang_ref_exe="$work_dir/ref_clang.exe" clang_ref_out="$work_dir/ref_clang.out"
|
|
|
|
|
local clang_ref_time=999999 clang_ref_ok=0
|
|
|
|
|
local clang_comp_pid=0
|
|
|
|
|
|
|
|
|
|
if $USE_CLANG; then
|
|
|
|
|
"$CLANG" $CLANG_COMPILE_FLAGS -S -O2 -o "$clang_ref_asm" "$gcc_src" 2>/dev/null &
|
|
|
|
|
clang_comp_pid=$!
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# 等待编译完成
|
|
|
|
|
wait $our_comp_pid 2>/dev/null || true
|
|
|
|
|
[[ $gcc_comp_pid -ne 0 ]] && wait $gcc_comp_pid 2>/dev/null || true
|
|
|
|
|
[[ $clang_comp_pid -ne 0 ]] && wait $clang_comp_pid 2>/dev/null || true
|
|
|
|
|
|
|
|
|
|
# ---- 链接并运行我们的 ----
|
|
|
|
|
if [[ -s "$our_asm" ]]; then
|
|
|
|
|
if "$GCC" $GCC_LINK_FLAGS "$our_asm" "$SYLIB" -o "$our_exe" -lm 2>/dev/null; then
|
|
|
|
|
our_compile_ok=1
|
|
|
|
|
local start_ns end_ns exit_code=0
|
|
|
|
|
@ -239,8 +275,7 @@ run_with_args() {
|
|
|
|
|
end_ns="$(date +%s%3N)"
|
|
|
|
|
our_time=$((end_ns - start_ns))
|
|
|
|
|
[[ $our_time -lt 1 ]] && our_time=1
|
|
|
|
|
# SysY 程序可返回任意值,不能用 exit_code==0 判断正确性
|
|
|
|
|
# .out 文件格式(与 2026test.sh 一致): stdout + [换行] + 退出码
|
|
|
|
|
|
|
|
|
|
local our_actual="$work_dir/our.actual.out"
|
|
|
|
|
{
|
|
|
|
|
cat "$our_out"
|
|
|
|
|
@ -249,6 +284,7 @@ run_with_args() {
|
|
|
|
|
fi
|
|
|
|
|
echo "$exit_code"
|
|
|
|
|
} > "$our_actual"
|
|
|
|
|
|
|
|
|
|
if [[ -f "$out_file" ]]; then
|
|
|
|
|
canon_diff "$our_actual" "$out_file" && our_correct=1
|
|
|
|
|
else
|
|
|
|
|
@ -257,121 +293,224 @@ run_with_args() {
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
local ref_asm="$work_dir/ref.s" ref_exe="$work_dir/ref.exe" ref_out="$work_dir/ref.out"
|
|
|
|
|
local ref_time=999999
|
|
|
|
|
# ---- 链接并运行 gcc 参考 ----
|
|
|
|
|
if $USE_GCC && [[ -s "$gcc_ref_asm" ]]; then
|
|
|
|
|
if "$GCC" $GCC_LINK_FLAGS "$gcc_ref_asm" "$SYLIB" -o "$gcc_ref_exe" -lm 2>/dev/null; then
|
|
|
|
|
gcc_ref_ok=1
|
|
|
|
|
local ref_start ref_end ref_exit=0
|
|
|
|
|
ref_start="$(date +%s%3N)"
|
|
|
|
|
if [[ -f "$in_file" ]]; then
|
|
|
|
|
timeout "$TIMEOUT_SEC" "$QEMU" $QEMU_FLAGS "$gcc_ref_exe" < "$in_file" > "$gcc_ref_out" 2>/dev/null || ref_exit=$?
|
|
|
|
|
else
|
|
|
|
|
timeout "$TIMEOUT_SEC" "$QEMU" $QEMU_FLAGS "$gcc_ref_exe" > "$gcc_ref_out" 2>/dev/null || ref_exit=$?
|
|
|
|
|
fi
|
|
|
|
|
ref_end="$(date +%s%3N)"
|
|
|
|
|
gcc_ref_time=$((ref_end - ref_start))
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
[[ $gcc_ref_time -lt 1 ]] && gcc_ref_time=1
|
|
|
|
|
|
|
|
|
|
if "$GCC" $GCC_COMPILE_FLAGS -S -o "$ref_asm" "$sy_file" 2>/dev/null; then
|
|
|
|
|
if "$GCC" $GCC_LINK_FLAGS "$ref_asm" "$SYLIB" -o "$ref_exe" -lm 2>/dev/null; then
|
|
|
|
|
# ---- 链接并运行 clang 参考 ----
|
|
|
|
|
if $USE_CLANG && [[ -s "$clang_ref_asm" ]]; then
|
|
|
|
|
if "$GCC" $GCC_LINK_FLAGS "$clang_ref_asm" "$SYLIB" -o "$clang_ref_exe" -lm 2>/dev/null; then
|
|
|
|
|
clang_ref_ok=1
|
|
|
|
|
local ref_start ref_end ref_exit=0
|
|
|
|
|
ref_start="$(date +%s%3N)"
|
|
|
|
|
if [[ -f "$in_file" ]]; then
|
|
|
|
|
timeout "$TIMEOUT_SEC" "$QEMU" $QEMU_FLAGS "$ref_exe" < "$in_file" > "$ref_out" 2>/dev/null || ref_exit=$?
|
|
|
|
|
timeout "$TIMEOUT_SEC" "$QEMU" $QEMU_FLAGS "$clang_ref_exe" < "$in_file" > "$clang_ref_out" 2>/dev/null || ref_exit=$?
|
|
|
|
|
else
|
|
|
|
|
timeout "$TIMEOUT_SEC" "$QEMU" $QEMU_FLAGS "$ref_exe" > "$ref_out" 2>/dev/null || ref_exit=$?
|
|
|
|
|
timeout "$TIMEOUT_SEC" "$QEMU" $QEMU_FLAGS "$clang_ref_exe" > "$clang_ref_out" 2>/dev/null || ref_exit=$?
|
|
|
|
|
fi
|
|
|
|
|
ref_end="$(date +%s%3N)"
|
|
|
|
|
ref_time=$((ref_end - ref_start))
|
|
|
|
|
clang_ref_time=$((ref_end - ref_start))
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
[[ $ref_time -lt 1 ]] && ref_time=1
|
|
|
|
|
[[ $clang_ref_time -lt 1 ]] && clang_ref_time=1
|
|
|
|
|
|
|
|
|
|
echo "$our_correct $our_time $ref_time $our_compile_ok" > "$work_dir/result.txt"
|
|
|
|
|
# 写结果(7 字段)
|
|
|
|
|
echo "$our_correct $our_time $gcc_ref_time $gcc_ref_ok $clang_ref_time $clang_ref_ok $our_compile_ok" > "$work_dir/result.txt"
|
|
|
|
|
|
|
|
|
|
# 打印单行结果
|
|
|
|
|
if [[ $our_correct -eq 1 ]]; then
|
|
|
|
|
local ratio
|
|
|
|
|
ratio=$(awk "BEGIN { printf \"%.4f\", $ref_time / $our_time }" 2>/dev/null || echo "0")
|
|
|
|
|
printf "[${GREEN}AC${NC}] %-45s our=%6dms ref=%6dms ratio=%s\n" \
|
|
|
|
|
"$cat_name/$base_name" "$our_time" "$ref_time" "$ratio"
|
|
|
|
|
local ratio_str=""
|
|
|
|
|
if $USE_GCC; then
|
|
|
|
|
if [[ $gcc_ref_ok -eq 1 ]] && [[ $gcc_ref_time -gt 0 ]]; then
|
|
|
|
|
ratio_str+=" gcc=$(awk "BEGIN { printf \"%.2f\", $gcc_ref_time / $our_time }" 2>/dev/null || echo "N/A")"
|
|
|
|
|
else
|
|
|
|
|
ratio_str+=" gcc=N/A"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
if $USE_CLANG; then
|
|
|
|
|
if [[ $clang_ref_ok -eq 1 ]] && [[ $clang_ref_time -gt 0 ]]; then
|
|
|
|
|
ratio_str+=" clang=$(awk "BEGIN { printf \"%.2f\", $clang_ref_time / $our_time }" 2>/dev/null || echo "N/A")"
|
|
|
|
|
else
|
|
|
|
|
ratio_str+=" clang=N/A"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
printf "[${GREEN}AC${NC}] %-45s our=%6dms%s\n" \
|
|
|
|
|
"$cat_name/$base_name" "$our_time" "$ratio_str"
|
|
|
|
|
elif [[ $our_compile_ok -eq 0 ]]; then
|
|
|
|
|
printf "[${RED}CE${NC}] %-45s (编译失败)\n" "$cat_name/$base_name"
|
|
|
|
|
else
|
|
|
|
|
printf "[${RED}WA${NC}] %-45s our=%6dms ref=%6dms (输出不匹配)\n" \
|
|
|
|
|
"$cat_name/$base_name" "$our_time" "$ref_time"
|
|
|
|
|
printf "[${RED}WA${NC}] %-45s our=%6dms (输出不匹配)\n" \
|
|
|
|
|
"$cat_name/$base_name" "$our_time"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 简单后台并行
|
|
|
|
|
running=0
|
|
|
|
|
for f in "${TEST_FILES[@]}"; do
|
|
|
|
|
run_with_args "$f" &
|
|
|
|
|
running=$((running + 1))
|
|
|
|
|
if [[ $running -ge $JOBS ]]; then
|
|
|
|
|
wait -n 2>/dev/null || true
|
|
|
|
|
running=$((running - 1))
|
|
|
|
|
fi
|
|
|
|
|
if ! $QUIET; then
|
|
|
|
|
echo -e "${BLUE}正在运行 ${TOTAL_TESTS} 个测试 (${JOBS} 并行)...${NC}"
|
|
|
|
|
echo ""
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# 构建队列
|
|
|
|
|
declare -a QUEUE
|
|
|
|
|
for i in "${!TEST_FILES[@]}"; do
|
|
|
|
|
QUEUE+=("$i|${TEST_FILES[$i]}")
|
|
|
|
|
done
|
|
|
|
|
wait
|
|
|
|
|
|
|
|
|
|
if [[ $JOBS -gt 1 && ${#QUEUE[@]} -gt 1 ]]; then
|
|
|
|
|
export RESULT_DIR COMPILER COMPILER_FLAGS
|
|
|
|
|
export GCC GCC_COMPILE_FLAGS GCC_LINK_FLAGS
|
|
|
|
|
export CLANG CLANG_COMPILE_FLAGS
|
|
|
|
|
export QEMU QEMU_FLAGS SYLIB TIMEOUT_SEC
|
|
|
|
|
export USE_GCC USE_CLANG
|
|
|
|
|
export RED GREEN YELLOW BLUE BOLD NC
|
|
|
|
|
export -f run_worker canon_diff
|
|
|
|
|
|
|
|
|
|
printf '%s\n' "${QUEUE[@]}" | xargs -P "$JOBS" -L 1 bash -c '
|
|
|
|
|
IFS="|" read -r idx sy <<< "$1"
|
|
|
|
|
run_worker "$idx" "$sy"
|
|
|
|
|
' _
|
|
|
|
|
else
|
|
|
|
|
for item in "${QUEUE[@]}"; do
|
|
|
|
|
IFS='|' read -r idx sy <<< "$item"
|
|
|
|
|
run_worker "$idx" "$sy"
|
|
|
|
|
done
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
# 收集结果
|
|
|
|
|
# Phase 3: 收集结果并计算分数
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
|
|
|
|
passed=0
|
|
|
|
|
declare -a ratios
|
|
|
|
|
|
|
|
|
|
for cat in "${CATEGORIES[@]}"; do
|
|
|
|
|
for sy_file in "$TEST_DIR/$cat"/*.sy; do
|
|
|
|
|
[[ -f "$sy_file" ]] || continue
|
|
|
|
|
base_name="$(basename "${sy_file%.sy}")"
|
|
|
|
|
work_dir="$RESULT_DIR/$cat/$base_name"
|
|
|
|
|
result_file="$work_dir/result.txt"
|
|
|
|
|
|
|
|
|
|
if [[ -f "$result_file" ]]; then
|
|
|
|
|
read -r test_correct our_time ref_time ref_ok compile_ok < "$result_file" || true
|
|
|
|
|
if [[ "$test_correct" == "1" ]]; then
|
|
|
|
|
passed=$((passed + 1))
|
|
|
|
|
if [[ $ref_time -gt 0 ]] && [[ $our_time -gt 0 ]]; then
|
|
|
|
|
ratio=$(echo "scale=10; $ref_time / $our_time" | bc 2>/dev/null || echo "1.0")
|
|
|
|
|
ratios+=("$ratio")
|
|
|
|
|
compute_score() {
|
|
|
|
|
local ref_name="$1" # "gcc" or "clang"
|
|
|
|
|
local ref_time_field="$2" # field position in result.txt
|
|
|
|
|
local ref_ok_field="$3"
|
|
|
|
|
|
|
|
|
|
local passed=0 perf_count=0
|
|
|
|
|
declare -a ratios
|
|
|
|
|
|
|
|
|
|
for cat in "${CATEGORIES[@]}"; do
|
|
|
|
|
for sy_file in "$TEST_DIR/$cat"/*.sy; do
|
|
|
|
|
[[ -f "$sy_file" ]] || continue
|
|
|
|
|
base_name="$(basename "${sy_file%.sy}")"
|
|
|
|
|
work_dir="$RESULT_DIR/$cat/$base_name"
|
|
|
|
|
result_file="$work_dir/result.txt"
|
|
|
|
|
|
|
|
|
|
if [[ -f "$result_file" ]]; then
|
|
|
|
|
read -r our_correct our_time gcc_time gcc_ok clang_time clang_ok our_compile_ok < "$result_file" || true
|
|
|
|
|
if [[ "$our_correct" == "1" ]]; then
|
|
|
|
|
passed=$((passed + 1))
|
|
|
|
|
local ref_time ref_ok
|
|
|
|
|
if [[ "$ref_name" == "gcc" ]]; then
|
|
|
|
|
ref_time="$gcc_time"; ref_ok="$gcc_ok"
|
|
|
|
|
else
|
|
|
|
|
ref_time="$clang_time"; ref_ok="$clang_ok"
|
|
|
|
|
fi
|
|
|
|
|
if [[ "$ref_ok" == "1" ]] && [[ $ref_time -gt 0 ]] && [[ $our_time -gt 0 ]]; then
|
|
|
|
|
ratio=$(echo "scale=10; $ref_time / $our_time" | bc 2>/dev/null || echo "1.0")
|
|
|
|
|
ratios+=("$ratio")
|
|
|
|
|
perf_count=$((perf_count + 1))
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
done
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# 计算几何平均
|
|
|
|
|
GEOM_MEAN="0.0"
|
|
|
|
|
if [[ ${#ratios[@]} -gt 0 ]]; then
|
|
|
|
|
product="1.0"
|
|
|
|
|
for r in "${ratios[@]}"; do
|
|
|
|
|
product=$(echo "scale=20; $product * $r" | bc 2>/dev/null)
|
|
|
|
|
done
|
|
|
|
|
GEOM_MEAN=$(echo "scale=10; e(l($product) / ${#ratios[@]})" | bc -l 2>/dev/null || echo "1.0")
|
|
|
|
|
fi
|
|
|
|
|
# 几何平均
|
|
|
|
|
local geom_mean="0.0"
|
|
|
|
|
if [[ $perf_count -gt 0 ]]; then
|
|
|
|
|
local product="1.0"
|
|
|
|
|
for r in "${ratios[@]}"; do
|
|
|
|
|
product=$(echo "scale=20; $product * $r" | bc 2>/dev/null)
|
|
|
|
|
done
|
|
|
|
|
geom_mean=$(echo "scale=10; e(l($product) / $perf_count)" | bc -l 2>/dev/null || echo "1.0")
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
PERF_SCORE=$(echo "scale=4; $GEOM_MEAN * 100" | bc -l 2>/dev/null)
|
|
|
|
|
if (( $(echo "$PERF_SCORE > 100" | bc -l 2>/dev/null) )); then
|
|
|
|
|
PERF_SCORE="100.0000"
|
|
|
|
|
fi
|
|
|
|
|
local perf_score=$(echo "scale=4; $geom_mean * 100" | bc -l 2>/dev/null)
|
|
|
|
|
if (( $(echo "$perf_score > 100" | bc -l 2>/dev/null) )); then
|
|
|
|
|
perf_score="100.0000"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
local correct_score=$(echo "scale=4; $passed / $TOTAL_TESTS * 100" | bc -l 2>/dev/null)
|
|
|
|
|
local total_score=$(echo "scale=4; $correct_score * 0.5 + $perf_score * 0.5" | bc -l 2>/dev/null)
|
|
|
|
|
|
|
|
|
|
echo "$passed|$perf_count|$geom_mean|$perf_score|$correct_score|$total_score|$perf_count"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
print_score_block() {
|
|
|
|
|
local ref_label="$1"
|
|
|
|
|
local result="$2"
|
|
|
|
|
|
|
|
|
|
CORRECT_SCORE=$(echo "scale=4; $passed / $TOTAL_TESTS * 100" | bc -l 2>/dev/null)
|
|
|
|
|
TOTAL_SCORE=$(echo "scale=4; $CORRECT_SCORE * 0.5 + $PERF_SCORE * 0.5" | bc -l 2>/dev/null)
|
|
|
|
|
IFS='|' read -r passed perf_count geom_mean perf_score correct_score total_score n_ratios <<< "$result"
|
|
|
|
|
|
|
|
|
|
echo -e "${BOLD}--- vs ${ref_label} ---${NC}"
|
|
|
|
|
printf " %-20s ${GREEN}%d / %d${NC}\n" "正确用例:" "$passed" "$TOTAL_TESTS"
|
|
|
|
|
printf " %-20s ${GREEN}%d${NC}\n" "参与性能分用例:" "$perf_count"
|
|
|
|
|
echo ""
|
|
|
|
|
printf " %-20s %8s\n" "--------------------" "--------"
|
|
|
|
|
printf " %-20s ${GREEN}%8.4f${NC}\n" "正确分:" "$correct_score"
|
|
|
|
|
printf " %-20s ${YELLOW}%8.4f${NC}\n" "性能分:" "$perf_score"
|
|
|
|
|
printf " %-20s ${BLUE}${BOLD}%8.4f${NC}\n" "总分:" "$total_score"
|
|
|
|
|
echo ""
|
|
|
|
|
printf " 几何平均 (ref/our): %.6f\n" "$geom_mean"
|
|
|
|
|
printf " 有效性能用例数: %d\n" "$n_ratios"
|
|
|
|
|
echo ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
echo -e "${BLUE}========================================================${NC}"
|
|
|
|
|
echo -e "${BLUE} 评分结果${NC}"
|
|
|
|
|
echo -e "${BLUE}========================================================${NC}"
|
|
|
|
|
echo ""
|
|
|
|
|
printf " %-20s ${GREEN}%d / %d${NC}\n" "正确用例:" "$passed" "$TOTAL_TESTS"
|
|
|
|
|
echo ""
|
|
|
|
|
printf " ${BOLD}%-20s %8s${NC}\n" "指标" "分数"
|
|
|
|
|
printf " %-20s %8s\n" "--------------------" "--------"
|
|
|
|
|
printf " %-20s ${GREEN}%8.4f${NC}\n" "正确分:" "$CORRECT_SCORE"
|
|
|
|
|
printf " %-20s ${YELLOW}%8.4f${NC}\n" "性能分:" "$PERF_SCORE"
|
|
|
|
|
printf " %-20s ${BLUE}${BOLD}%8.4f${NC}\n" "总分:" "$TOTAL_SCORE"
|
|
|
|
|
echo ""
|
|
|
|
|
printf " 几何平均 (ref/our): %.6f\n" "$GEOM_MEAN"
|
|
|
|
|
printf " 有效用例数: %d\n" "${#ratios[@]}"
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
if $USE_GCC; then
|
|
|
|
|
gcc_result=$(compute_score "gcc" 3 4)
|
|
|
|
|
print_score_block "gcc -O2" "$gcc_result"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if $USE_CLANG; then
|
|
|
|
|
clang_result=$(compute_score "clang" 5 6)
|
|
|
|
|
print_score_block "clang -O2" "$clang_result"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# 保存
|
|
|
|
|
{
|
|
|
|
|
echo "正确分: $CORRECT_SCORE"
|
|
|
|
|
echo "性能分: $PERF_SCORE"
|
|
|
|
|
echo "总分: $TOTAL_SCORE"
|
|
|
|
|
echo "几何平均: $GEOM_MEAN"
|
|
|
|
|
echo "通过: $passed / $TOTAL_TESTS"
|
|
|
|
|
echo "参考编译器: $REF_LABEL"
|
|
|
|
|
echo "测试类别: $CATEGORY"
|
|
|
|
|
echo ""
|
|
|
|
|
if $USE_GCC; then
|
|
|
|
|
echo "--- vs gcc -O2 ---"
|
|
|
|
|
IFS='|' read -r passed perf_count geom_mean perf_score correct_score total_score n_ratios <<< "$gcc_result"
|
|
|
|
|
echo "正确分: $correct_score"
|
|
|
|
|
echo "性能分: $perf_score"
|
|
|
|
|
echo "总分: $total_score"
|
|
|
|
|
echo "几何平均: $geom_mean"
|
|
|
|
|
echo "通过: $passed / $TOTAL_TESTS"
|
|
|
|
|
echo "参与性能分: $n_ratios"
|
|
|
|
|
echo ""
|
|
|
|
|
fi
|
|
|
|
|
if $USE_CLANG; then
|
|
|
|
|
echo "--- vs clang -O2 ---"
|
|
|
|
|
IFS='|' read -r passed perf_count geom_mean perf_score correct_score total_score n_ratios <<< "$clang_result"
|
|
|
|
|
echo "正确分: $correct_score"
|
|
|
|
|
echo "性能分: $perf_score"
|
|
|
|
|
echo "总分: $total_score"
|
|
|
|
|
echo "几何平均: $geom_mean"
|
|
|
|
|
echo "通过: $passed / $TOTAL_TESTS"
|
|
|
|
|
echo "参与性能分: $n_ratios"
|
|
|
|
|
fi
|
|
|
|
|
} > "$RESULT_DIR/score.txt"
|
|
|
|
|
|
|
|
|
|
echo -e "${BLUE}结果已保存到: ${RESULT_DIR}/score.txt${NC}"
|
|
|
|
|
|