diff --git a/Makefile b/Makefile index 61ae4e3..5e011b6 100644 --- a/Makefile +++ b/Makefile @@ -248,3 +248,249 @@ clean-tests: clean-inputs clean-outputs clean: @echo "Cleaning up $(BUILD_DIR)/..." @rm -rf $(BUILD_DIR) + +# ----------------------------------------------------------------------------- +# Benchmark Target (Advanced) +# - N, M 증가 (Step) +# - 동일 조건 반복 (Repeat) +# - 유효성 검사 (Validator) 포함 +# - 상세 정보(Seed, Command) CSV 기록 +# ----------------------------------------------------------------------------- + +BENCH_TIMESTAMP = $(shell date +%Y%m%d_%H%M%S) +BENCH_CSV = $(LOG_DIR)/benchmark_results_$(BENCH_TIMESTAMP).csv +BENCH_META = $(LOG_DIR)/benchmark_results_$(BENCH_TIMESTAMP).meta + +START_N ?= 10000 +END_N ?= 50000 +STEP_N ?= 1000 +# M 계산식 (bash arithmetic expansion 문법) +M_CALC ?= n * 4 + +# 동일 조건 반복 횟수 +REPEAT ?= 5 + +.PHONY: benchmark +benchmark: all $(CHK_EXE) $(LOG_DIR) + @echo "--- Starting Benchmark ---" + @echo "Data: $(BENCH_CSV)" + @echo "Meta: $(BENCH_META)" + + @# 1. 메타 파일(.meta)에 실행 정보 기록 + @echo "Execution Info: Date=$(BENCH_TIMESTAMP) | Host=$(shell hostname)" > $(BENCH_META) + @echo "Reproduce Command: make benchmark START_N=$(START_N) END_N=$(END_N) STEP_N=$(STEP_N) REPEAT=$(REPEAT) M_CALC='$(M_CALC)' SOL_A=$(SOL_A) SOL_B=$(SOL_B) gen=$(gen)" >> $(BENCH_META) + + @# 2. CSV 파일(.csv)에는 순수 헤더만 기록 (엑셀 호환성 확보) + @echo "N,M,Iter,Seed,$(SOL_A)_Time,$(SOL_B)_Time,Gen_Cmd,Sol_A_Cmd,Sol_B_Cmd" > $(BENCH_CSV) + + @bash -c ' \ + export LC_ALL=C; \ + TIMEFORMAT=%R; \ + \ + for (( n=$(START_N); n<=$(END_N); n+=$(STEP_N) )); do \ + m=$$(( $(M_CALC) )); \ + \ + for (( r=1; r<=$(REPEAT); r++ )); do \ + SEED=$$(cat /dev/urandom | tr -cd "a-zA-Z0-9" | head -c 32); \ + \ + TMP_IN="$(BUILD_IN_DIR)/bench_in_$(BENCH_TIMESTAMP)_$$$$.tmp"; \ + TMP_OUT_A="$(BUILD_DIR)/bench_out_a_$(BENCH_TIMESTAMP)_$$$$.tmp"; \ + TMP_OUT_B="$(BUILD_DIR)/bench_out_b_$(BENCH_TIMESTAMP)_$$$$.tmp"; \ + \ + CMD_GEN="./$(GEN_TO_RUN) -N $$n -M $$m $$SEED"; \ + CMD_SOL_A="./$(SOL_A_EXE) < input"; \ + CMD_SOL_B="./$(SOL_B_EXE) < input"; \ + \ + printf "Run: N=%-5d M=%-5d Iter=%-2d ... " $$n $$m $$r; \ + \ + $$CMD_GEN > $$TMP_IN; \ + \ + t_a=$$( { time ./$(SOL_A_EXE) < $$TMP_IN > $$TMP_OUT_A; } 2>&1 ); \ + t_b=$$( { time ./$(SOL_B_EXE) < $$TMP_IN > $$TMP_OUT_B; } 2>&1 ); \ + \ + if ! ./$(CHK_EXE) $$TMP_IN $$TMP_OUT_A $$TMP_OUT_B > /dev/null 2>&1; then \ + printf "\n[FAIL] Checker found mismatch!\n"; \ + echo "Seed: $$SEED"; \ + echo "Check Metafile: $(BENCH_META)"; \ + exit 1; \ + fi; \ + \ + echo "$$n,$$m,$$r,$$SEED,$$t_a,$$t_b,\"$$CMD_GEN\",\"$$CMD_SOL_A\",\"$$CMD_SOL_B\"" >> $(BENCH_CSV); \ + \ + echo "OK. [A: $${t_a}s] [B: $${t_b}s]"; \ + \ + rm -f $$TMP_IN $$TMP_OUT_A $$TMP_OUT_B; \ + done; \ + done \ + ' + @echo "--- Benchmark Complete! ---" + @echo "CSV Data: $(BENCH_CSV)" + @echo "Metadata: $(BENCH_META)" + +# .PHONY: benchmark +# benchmark: all $(CHK_EXE) $(LOG_DIR) +# @echo "--- Starting Benchmark with Checker ---" +# @echo "Range: N=[$(START_N) .. $(END_N)], Step=$(STEP_N)" +# @echo "Formula: M = $(M_CALC)" +# @echo "Checker: $(CHK_EXE)" +# @echo "Output: $(BENCH_CSV)" + +# @echo "N,M,Iter,Seed,$(SOL_A)_Time,$(SOL_B)_Time,Gen_Cmd,Sol_A_Cmd,Sol_B_Cmd" > $(BENCH_CSV) + +# @bash -c ' \ +# export LC_ALL=C; \ +# TIMEFORMAT=%R; \ +# \ +# for (( n=$(START_N); n<=$(END_N); n+=$(STEP_N) )); do \ +# m=$$(( $(M_CALC) )); \ +# \ +# for (( r=1; r<=$(REPEAT); r++ )); do \ +# SEED=$$(cat /dev/urandom | tr -cd "a-zA-Z0-9" | head -c 32); \ +# \ +# # 병렬 실행을 위한 임시 파일명 설정 (Input, OutA, OutB) \ +# TMP_IN="$(BUILD_IN_DIR)/bench_in_$(BENCH_TIMESTAMP)_$$$$.tmp"; \ +# TMP_OUT_A="$(BUILD_DIR)/bench_out_a_$(BENCH_TIMESTAMP)_$$$$.tmp"; \ +# TMP_OUT_B="$(BUILD_DIR)/bench_out_b_$(BENCH_TIMESTAMP)_$$$$.tmp"; \ +# \ +# CMD_GEN="./$(GEN_TO_RUN) -N $$n -M $$m $$SEED"; \ +# CMD_SOL_A="./$(SOL_A_EXE) < input"; \ +# CMD_SOL_B="./$(SOL_B_EXE) < input"; \ +# \ +# printf "Run: N=%-5d M=%-5d Iter=%-2d ... " $$n $$m $$r; \ +# \ +# # 1. Generator 실행 \ +# $$CMD_GEN > $$TMP_IN; \ +# \ +# # 2. Sol A 실행 (시간 측정 + 출력 저장) \ +# t_a=$$( { time ./$(SOL_A_EXE) < $$TMP_IN > $$TMP_OUT_A; } 2>&1 ); \ +# \ +# # 3. Sol B 실행 (시간 측정 + 출력 저장) \ +# t_b=$$( { time ./$(SOL_B_EXE) < $$TMP_IN > $$TMP_OUT_B; } 2>&1 ); \ +# \ +# # 4. Checker 실행 (비교) \ +# if ! ./$(CHK_EXE) $$TMP_IN $$TMP_OUT_A $$TMP_OUT_B > /dev/null 2>&1; then \ +# printf "\n[FAIL] Checker found mismatch!\n"; \ +# echo "Seed: $$SEED"; \ +# echo "Input saved to: $$TMP_IN"; \ +# echo "Out A saved to: $$TMP_OUT_A"; \ +# echo "Out B saved to: $$TMP_OUT_B"; \ +# exit 1; \ +# fi; \ +# \ +# # 5. 통과 시 CSV 기록 \ +# echo "$$n,$$m,$$r,$$SEED,$$t_a,$$t_b,\"$$CMD_GEN\",\"$$CMD_SOL_A\",\"$$CMD_SOL_B\"" >> $(BENCH_CSV); \ +# \ +# echo "OK. [A: $${t_a}s] [B: $${t_b}s]"; \ +# \ +# # 6. 임시 파일 삭제 \ +# rm -f $$TMP_IN $$TMP_OUT_A $$TMP_OUT_B; \ +# done; \ +# done \ +# ' +# @echo "--- Benchmark Complete! Data saved to $(BENCH_CSV) ---" + +# .PHONY: benchmark +# benchmark: all $(LOG_DIR) +# @echo "--- Starting Advanced Benchmark ---" +# @echo "Range: N=[$(START_N) .. $(END_N)], Step=$(STEP_N)" +# @echo "Formula: M = $(M_CALC)" +# @echo "Repeat: $(REPEAT) times per setting" +# @echo "Output: $(BENCH_CSV)" + +# @echo "N,M,Iter,Seed,$(SOL_A)_Time,$(SOL_B)_Time,Gen_Cmd,Sol_A_Cmd,Sol_B_Cmd" > $(BENCH_CSV) + +# @bash -c ' \ +# export LC_ALL=C; \ +# TIMEFORMAT=%R; \ +# \ +# for (( n=$(START_N); n<=$(END_N); n+=$(STEP_N) )); do \ +# m=$$(( $(M_CALC) )); \ +# \ +# for (( r=1; r<=$(REPEAT); r++ )); do \ +# SEED=$$(cat /dev/urandom | tr -cd "a-zA-Z0-9" | head -c 32); \ +# \ +# # [변경] 파일명에 타임스탬프와 PID($$)를 포함하여 충돌 방지 \ +# TMP_IN="$(BUILD_IN_DIR)/bench_input_$(BENCH_TIMESTAMP)_$$$$.tmp"; \ +# \ +# CMD_GEN="./$(GEN_TO_RUN) -N $$n -M $$m $$SEED"; \ +# CMD_SOL_A="./$(SOL_A_EXE) < input"; \ +# CMD_SOL_B="./$(SOL_B_EXE) < input"; \ +# \ +# printf "Run: N=%-5d M=%-5d Iter=%-2d ... " $$n $$m $$r; \ +# \ +# $$CMD_GEN > $$TMP_IN; \ +# \ +# if ! ./$(VAL_EXE) < $$TMP_IN > /dev/null 2>&1; then \ +# echo " [FAIL] Validator rejected input! Seed: $$SEED"; \ +# rm -f $$TMP_IN; \ +# exit 1; \ +# fi; \ +# \ +# t_a=$$( { time ./$(SOL_A_EXE) < $$TMP_IN > /dev/null; } 2>&1 ); \ +# t_b=$$( { time ./$(SOL_B_EXE) < $$TMP_IN > /dev/null; } 2>&1 ); \ +# \ +# echo "$$n,$$m,$$r,$$SEED,$$t_a,$$t_b,\"$$CMD_GEN\",\"$$CMD_SOL_A\",\"$$CMD_SOL_B\"" >> $(BENCH_CSV); \ +# \ +# echo "OK. [A: $${t_a}s] [B: $${t_b}s]"; \ +# \ +# rm -f $$TMP_IN; \ +# done; \ +# done \ +# ' +# @echo "--- Benchmark Complete! Data saved to $(BENCH_CSV) ---" + +# .PHONY: benchmark +# benchmark: all $(LOG_DIR) +# @echo "--- Starting Advanced Benchmark ---" +# @echo "Range: N=[$(START_N) .. $(END_N)], Step=$(STEP_N)" +# @echo "Formula: M = $(M_CALC)" +# @echo "Repeat: $(REPEAT) times per setting" +# @echo "Validator: $(VAL_EXE)" +# @echo "Output: $(BENCH_CSV)" + +# @# CSV Header 작성 +# @echo "N,M,Iter,Seed,$(SOL_A)_Time,$(SOL_B)_Time,Gen_Cmd,Sol_A_Cmd,Sol_B_Cmd" > $(BENCH_CSV) + +# @bash -c ' \ +# export LC_ALL=C; \ +# TIMEFORMAT=%R; \ +# \ +# # 1. N 루프 \ +# for (( n=$(START_N); n<=$(END_N); n+=$(STEP_N) )); do \ +# m=$$(( $(M_CALC) )); \ +# \ +# # 2. 반복 루프 (Repeat) \ +# for (( r=1; r<=$(REPEAT); r++ )); do \ +# SEED=$$(cat /dev/urandom | tr -cd "a-zA-Z0-9" | head -c 32); \ +# TMP_IN="$(BUILD_IN_DIR)/bench_input.tmp"; \ +# \ +# # 명령어 문자열 구성 (CSV 기록용) \ +# CMD_GEN="./$(GEN_TO_RUN) -N $$n -M $$m $$SEED"; \ +# CMD_SOL_A="./$(SOL_A_EXE) < input"; \ +# CMD_SOL_B="./$(SOL_B_EXE) < input"; \ +# \ +# printf "Run: N=%-5d M=%-5d Iter=%-2d ... " $$n $$m $$r; \ +# \ +# # 3. Generator 실행 \ +# $$CMD_GEN > $$TMP_IN; \ +# \ +# # 4. Validator 실행 (실패 시 즉시 종료) \ +# if ! ./$(VAL_EXE) < $$TMP_IN > /dev/null 2>&1; then \ +# echo " [FAIL] Validator rejected input! Seed: $$SEED"; \ +# exit 1; \ +# fi; \ +# \ +# # 5. 솔루션 실행 및 시간 측정 \ +# t_a=$$( { time ./$(SOL_A_EXE) < $$TMP_IN > /dev/null; } 2>&1 ); \ +# t_b=$$( { time ./$(SOL_B_EXE) < $$TMP_IN > /dev/null; } 2>&1 ); \ +# \ +# # 6. CSV 저장 (명령어에 콤마가 포함될 수 있으므로 따옴표로 감싸는 것이 안전하나, 여기선 단순화) \ +# echo "$$n,$$m,$$r,$$SEED,$$t_a,$$t_b,\"$$CMD_GEN\",\"$$CMD_SOL_A\",\"$$CMD_SOL_B\"" >> $(BENCH_CSV); \ +# \ +# echo "OK. [A: $${t_a}s] [B: $${t_b}s]"; \ +# \ +# rm -f $$TMP_IN; \ +# done; \ +# done \ +# ' +# @echo "--- Benchmark Complete! Data saved to $(BENCH_CSV) ---" \ No newline at end of file