---
title: "regression 재현성: 같은 seed인데 입력이 달라졌다면"
author: "브레이브브라우저쓰자"
published_at: "2026-10-19T03:32:01.724Z"
updated_at: "2026-10-06T01:02:37.935Z"
tags: ["regression 재현성"]
canonical_url: "https://ariser.io/@writer_a9817d5a8de0/regression-%EC%9E%AC%ED%98%84%EC%84%B1-%EA%B0%99%EC%9D%80-seed%EC%9D%B8%EB%8D%B0-%EC%9E%85%EB%A0%A5%EC%9D%B4-%EB%8B%AC%EB%9D%BC%EC%A1%8C%EB%8B%A4%EB%A9%B4"
ai_assisted: true
---

# regression 재현성: 같은 seed인데 입력이 달라졌다면

이 글은 AI가 활용됐습니다.

[원문](https://ariser.io/@writer_a9817d5a8de0/regression-%EC%9E%AC%ED%98%84%EC%84%B1-%EA%B0%99%EC%9D%80-seed%EC%9D%B8%EB%8D%B0-%EC%9E%85%EB%A0%A5%EC%9D%B4-%EB%8B%AC%EB%9D%BC%EC%A1%8C%EB%8B%A4%EB%A9%B4)

실패한 테스트의 seed를 보관했다. 며칠 뒤 같은 seed로 다시 실행했지만 실패가 사라졌다. RTL이 고쳐졌을 수도 있지만, 자극 생성 코드나 설정이 바뀌어 다른 입력을 보냈을 수도 있다. 재현의 기준은 seed 문자열이 같다는 사실보다 실제로 같은 시험을 실행했다는 사실이다.

regression 재현성은 입력을 만드는 조건, 회로를 빌드하는 조건, 결과를 판정하는 조건을 함께 보존하는 문제다.

## seed는 난수 발생기의 시작점을 정한다

설명용으로 0~15의 상태를 가지는 작은 발생기를 만들자. 시작 상태가 3이고 다음 상태를 `(5×상태+1) mod 16`으로 계산한다. 이 발생기는 실제 검증 도구의 알고리즘이 아니며, 호출 순서의 효과만 보기 위한 예다.

처음 네 값은 0, 1, 6, 15다. 요청 하나의 두 입력에 처음 두 값을 사용하면 `(0,1)`이 된다. 여기에 로그용 표식을 고르려고 같은 발생기를 한 번 호출하는 코드를 앞에 넣으면, 표식이 0을 가져가고 요청 입력은 `(1,6)`이 된다.

| 실행 경로 | 첫 난수의 용도 | 첫 요청 입력 |
|---|---|---|
| 원래 코드 | 입력 a | (0,1) |
| 추가 호출이 있는 코드 | 로그 표식 | (1,6) |

시작 상태는 같아도 호출 경로가 달라지면 입력이 달라진다. 실제 환경에는 객체별·process별 난수 상태와 random stability 규칙이 있으므로 이 단일 발생기 예제를 모든 도구의 동작으로 일반화하지 않는다. 다만 seed만 비교해서 자극의 동일성을 결론 낼 수 없다는 점은 남는다.

lowRISC의 DV 가이드는 random stability와 맞지 않는 난수 함수를 피하라고 권한다. 난수 사용 방식을 일관되게 관리하는 것은 재현 가능한 자극의 일부다. [lowRISC DV Coding Style, Forbidden System Tasks and Functions](https://github.com/lowRISC/style-guides/blob/master/DVCodingStyle.md)

## 실제 적용한 seed와 옵션을 기록한다

도구의 seed 옵션도 정확히 읽어야 한다. Verilator의 현재 런타임 문서는 `+verilator+seed+<value>`의 적용 범위와 기본값을 설명한다. 0을 지정하면 시작 시 비영의 seed를 선택하며, 선택한 값을 `$get_initial_random_seed`로 얻어 다시 실행할 수 있다. 명령행에 0이 있었다는 기록만으로 같은 시작값을 지정한 것은 아니다. [Verilator, Simulation Runtime Arguments](https://verilator.org/guide/latest/exe_sim.html)

이는 그 옵션 하나가 모든 외부 라이브러리나 별도 난수 스트림까지 통제한다는 뜻은 아니다. 자극을 만드는 Python이나 DPI 코드, 외부 프로그램에 독자적인 seed가 있다면 함께 기록해야 한다. 자동 선택한 실제 값과 사용자가 요청한 옵션도 구별해 두면 좋다.

초기화 옵션도 시험을 바꿀 수 있다. 같은 Verilator 문서는 지원하는 설정에서 초기값을 0, 1 또는 랜덤으로 선택하는 옵션을 설명한다. seed가 같아도 그 설정이 다르면 reset 전후에 만나는 상태가 달라질 수 있다. 선언한 seed와 실제 빌드·런타임 설정이 함께 있어야 한다.

## 실패를 만든 입력 묶음을 남긴다

재현 기록에는 적어도 다음의 서로 다른 정보를 포함한다.

- DUT와 testbench의 정확한 소스 revision, 수정 중인 변경분, 생성된 설정 파일.
- simulator와 관련 compiler·solver·참조 라이브러리 버전, 빌드 옵션과 실행 명령.
- 테스트 이름과 설정, 실제 seed들, timeout, 입력 파일의 내용 또는 hash.
- reset과 자극의 관측 기록, 첫 mismatch의 요청·예상값·실제값, 종료 이유.

revision만 기록하고 아직 commit하지 않은 변경분을 버리면 같은 소스를 복원하지 못한다. 경로만 저장한 입력 파일은 나중에 내용이 바뀔 수 있으므로 내용이나 immutable한 식별이 필요하다. 실패 당시 timeout도 중요하다. 더 긴 timeout으로 응답을 기다려 통과했다면 원래의 시간 요구를 바꾼 것인지 따져야 한다.

작은 test case 파일을 보존하는 방법도 있다. TestFloat 문서는 같은 실행 프로그램에 같은 인자를 주면 같은 입력 순서를 생성한다고 설명하고, 출력 test case를 파일로 저장하는 사용을 지원한다. 이 조건에는 seed뿐 아니라 연산과 관련 옵션도 포함된다. [Berkeley TestFloat, testfloat_gen의 -seed 및 Output Format](https://www.jhauser.us/arithmetic/TestFloat-3/doc/testfloat_gen.html)

## 먼저 입력의 첫 차이를 찾는다

실패가 재현되지 않으면 마지막 PASS와 FAIL만 비교하지 말고 실제 수락 요청의 앞부분부터 대조한다. 100번째 요청의 입력이나 reset 시점이 달라졌다면 그 뒤 결과가 달라지는 것은 자연스럽다. 먼저 자극의 차이를 설명한 뒤 DUT의 결과를 비교해야 한다.

병렬 실행도 무조건 재현을 깨는 것은 아니다. 독립적인 난수 스트림과 결정적인 스케줄링을 가진 환경은 같은 입력을 유지할 수 있다. 반면 공유 상태를 서로 다른 호출 순서로 소비하거나 동일 시각의 race에 의존하면 실행 순서가 결과에 영향을 줄 수 있다. thread 수를 기록하고 관련 환경 조건을 고정하는 이유는 이 의존성을 추적하기 위해서다.

재현 패키지에는 저장 비용이 든다. 모든 파형을 무조건 남기기보다 소스와 설정, 필요한 입력 및 실패 문맥을 먼저 보존하고 상세 파형은 그 조건에서 다시 생성하는 방법이 가능하다. 다만 외부 서비스나 비결정적 입력을 나중에 그대로 복구할 수 없다면 그 입력 자체를 확보해야 한다.

같은 seed는 같은 시험을 복원하기 위한 단서다. 그 단서를 실제 입력과 빌드 조건에 연결해야 실패가 사라진 이유를 판단할 수 있다. 소스와 설정을 고정하고 첫 입력 차이부터 비교하면, 수정으로 해결된 버그와 시험이 바뀌어 숨은 버그를 구별할 수 있다.

