실시간 폐루프 API 설계 핵심 (시간정확성, 트랜잭션, 지터)

 

실시간 폐루프 API 설계 핵심 (시간정확성, 트랜잭션, 지터)
실시간 폐루프 API 설계 핵심 (시간정확성, 트랜잭션, 지터)

이 논문은 CL API라는 “실시간(서브-밀리초) 폐루프 BNN 상호작용”을 위해, 하드웨어 스펙이 아니라 API 계약(Contract) 차원에서 시간·순서·동기화·실패 의미론을 고정하자는 제안을 합니다. CL1(리눅스 커널 드라이버+FPGA+유저스페이스 라이브러리)로 그 계약이 실제로 지켜질 수 있음을 수치와 예시로 보여준 점이 핵심입니다.

시간적정확성: 평균 지연이 아니라 “API가 보장하는 시간 의미론”입니다

사용자 비평에서 가장 칭찬할 만한 지점은, 논문이 ‘실시간’을 단순히 “평균 지연이 낮다”로 정의하지 않고 **temporal correctness(시간적 정확성)**라는 더 엄격한 기준으로 못 박았다는 점입니다. 논문은 CL API Contract의 핵심을 Table 1에 “보장(Guarantees)·가정(Assumptions)·제약(Constraints)” 형태로 정리하며, 사용자에게 무엇을 기대하게 할지(그리고 무엇을 기대하면 안 되는지)를 API 레벨에서 규정합니다. 여기서 중요한 메시지는 “하드웨어가 빨라도 의미론이 불명확하면 재현성이 흔들린다”는 문제의식이며, CL API는 그 의미론을 먼저 고정합니다.

특히 Table 1의 5.1~5.3은 ‘시간적정확성’이 어떤 구조로 제공되는지 잘 보여줍니다. (1) 실시간 폐루프 실행은 “정의된 temporal semantics”이며, (2) 사용자는 선언형(declarative)으로 “무엇을, 어떤 순서로”만 쓰고, (3) 시스템이 deterministic ordering과 synchronization, start-time semantics를 보장한다는 틀입니다. 여기서 “절대 일어나지 않는 것”을 명시한 문장이 매우 중요합니다. 즉, 실행은 prior commitments 때문에 지연될 수 있지만, 요청한 것보다 더 이르게(never earlier than requested) 실행되지는 않는다는 규칙이 API 계약의 안전장치입니다. 실험에서 더 무서운 것은 ‘늦음’보다 ‘앞당겨짐’인데, 앞당겨짐이 발생하면 입력의 시간 구조 자체가 깨져 실험 해석이 불가능해지기 때문입니다.

논문이 제시하는 CL1 구현 디테일은 “시간적정확성”을 이해하는 데 실무적으로 유용합니다. 예를 들어 CL1에서 하드웨어 timing engine은 40µs 프레임(ADC 샘플 프레임) 단위로 동작하며, 사용자는 최대 25kHz 루프(=40µs 틱)로 폐루프를 돌릴 수 있다고 설명합니다. 또한 루프 바디의 유효 예산이 약 33~34µs이며, 이를 초과하면 시스템이 지터 누적을 숨기지 않고 TimeoutError를 발생시킨다는 점을 코드 예시로 보여줍니다. “평균적으로 빠르다”가 아니라 “프레임 예산을 넘기면 실패로 처리한다”는 철학이 바로 시간적정확성의 구현 방식입니다.

다만 비평에서 지적했듯, ‘계약’이 강점인 만큼 계약의 해석 여지를 더 줄이는 방식이 필요합니다. 지금은 표와 문장으로 보장을 설명하는 비중이 큰데, 동시성/순서/동기화에서 문장만으로는 구현이 바뀔 때 경계가 흔들릴 수 있습니다. 이 논문이 한 장만 더 보강한다면, “형식 모델(상태기계, happens-before 규칙)”이 가장 효율적입니다. 예를 들어 다음 3가지만 상태도로 고정해도 계약이 훨씬 단단해집니다.

Visibility frame 규칙: 사용자 호출이 언제 timing engine에 “가시화(visible)”되는지(논문은 40µs 프레임마다 가시화된다고 설명).

Per-channel queue 규칙: 같은 채널은 deterministic order로 실행되며, 서로 다른 채널은 sync 없이 병렬 진행한다는 규칙.

Never earlier than requested: 지연은 가능하지만 앞당김은 불가라는 안전 규칙.

아래 표는 논문 Table 1을 “사용자 관점(실험 재현성)”으로 다시 번역한 요약입니다. 이 표처럼 “무엇을 믿고 코드를 짤 수 있는지”를 독자가 바로 이해하게 만드는 것이 CL API의 진짜 장점입니다.

계약 항목 실험/개발자가 얻는 확정 의미
시간적정확성(temporal semantics) 평균 지연이 아니라 프레임 예산·타임스탬프 의미·완료 규칙이 API에 고정됩니다
결정적 순서·동기화 채널별 deterministic order, sync 이후 다채널 동시 시작, “요청보다 빨리 실행”은 금지입니다
실패 의미론(가시적 실패) 지터/자원초과는 숨기지 않고 예외로 노출해, 잘못된 데이터가 조용히 쌓이는 것을 막습니다

결국 이 논문이 말하는 ‘실시간’은 하드웨어의 자랑이 아니라, “시간을 코드가 이해할 수 있는 규칙으로 만든 것”입니다. 이 방향은 폐루프 실험에서 특히 중요하며, 추후 다른 구현체가 나오더라도 ‘계약’이 같으면 코드와 결과 비교가 가능하다는 점에서 인프라 논문으로서 설득력이 있습니다.

원자트랜잭션: 부분 실행을 없애야 재현성이 생깁니다

비평에서 강조한 **원자적 트랜잭션(atomic admission/rollback)**은, 이 논문의 “재현성” 주장에 가장 직접적으로 연결되는 장치입니다. 논문은 Table 1의 5.4에서, 자극(stimulation)과 동기화(synchronisation) 요청이 **원자적 트랜잭션**으로 취급되며 “전부 승인되거나 전부 거절”된다고 명시합니다. 특히 자원 제약(예: 채널별 stimulation queue capacity, synchronization bookkeeping capacity) 때문에 트랜잭션을 수용할 수 없으면, API가 **TransactionRejected**를 올리고 영향을 받는 채널에 대해 **complete rollback**을 수행한다고 적습니다. 그리고 이 admission은 “동기적(synchronous)”이라서 호출이 반환될 때 이미 승인/거절이 결정되어 있습니다.

이 설계가 왜 강력한지, 폐루프 실험의 ‘망하는 순간’을 떠올리면 쉽게 이해됩니다. 예를 들어 다채널 자극에서 8개 채널 중 5개만 큐에 들어가고 3개는 실패했다면, 시스템은 겉보기엔 “일부는 실행”되었고 기록에도 남습니다. 하지만 실험자는 이를 한 번에 알아채기 어렵고, 결과는 이미 오염됩니다. CL API는 이런 “부분 실행(partial admission)”을 원천적으로 금지합니다. 즉, 실패는 실패로 드러나고(예외), 성공은 완전한 성공으로만 남습니다. 재현성은 성능이 아니라 실패를 다루는 태도에서 만들어진다는 점을 이 논문이 정확히 짚은 것입니다.

또한 논문은 트랜잭션의 범위를 명확히 구분합니다. Neurons 객체에서 직접 호출하는 stim/sync는 각 호출이 “자체 트랜잭션”이고, StimPlan에 누적한 작업들은 StimPlan.run() 시점에 “단일 복합 트랜잭션”으로 제출된다고 정의합니다. 이 구분은 선언형 모델의 핵심과도 맞물립니다. StimPlan은 “모아두었다가 한 번에 가시화(visible)되는” 방식이므로, 다채널 정렬과 동시 시작을 더 안정적으로 만들 수 있습니다. 논문은 연속된 Neurons.stim 호출은 3µs 수준이라도, 하드웨어 timing engine에 가시화되는 프레임이 달라지면 “정확히 같은 시각에 시작”을 장담하기 어렵다고 설명하며, StimPlan을 쓰면 동시 가시화·원자적 제출을 통해 이 문제를 해결한다고 말합니다.

다만 비평 관점에서 아쉬운 부분은, ‘원자성’이 강점일수록 “어떤 실패가 얼마나 자주 일어나는가”가 더 중요해진다는 점입니다. 논문은 자원 제약을 구현 디테일로 취급하며 “limits are treated as implementation details”라고도 말합니다. 이 철학은 API 이식성 측면에선 이해되지만, 사용자는 결국 “내 실험이 TransactionRejected를 얼마나 만나나”를 알아야 합니다. 따라서 논문 완성도를 높이려면, 다음 형태의 간단한 지표가 있으면 좋습니다.

트랜잭션 크기(채널 수, 큐 길이, sync 포함 여부)에 따른 거절 확률 또는 거절 임계치.

“시뮬레이터에서는 통과했는데 하드웨어에서는 거절”되는 대표 패턴의 예시(아래 3번째 섹션의 체크리스트와도 연결).

또 하나, 원자성은 “정확성”을 높이지만 “사용자 경험”에서는 양날이 될 수 있습니다. 연구자 입장에서는 가끔 ‘완전 실패’ 대신 ‘부분 성공이라도 데이터로 남겨서 후처리’하고 싶은 욕구가 생깁니다. CL API는 이 길을 선택하지 않았고, 그 대신 계약으로 재현성을 우선했습니다. 이 선택을 더 설득력 있게 만들려면, 논문이 주장하는 재현성 위기의 사례(부분 실행이 왜 치명적인지)를 한두 개만 더 구체적으로 연결하면 좋습니다. 지금도 “black box를 계약으로 대체한다”는 결론 메시지는 강하지만, 독자의 직관을 더 강하게 붙잡을 ‘실패 사례’가 있으면 논문이 더 단단해집니다.

요약하면, 원자트랜잭션은 CL API의 ‘인프라 논문다운’ 설계입니다. 성능 수치가 약간 흔들려도, 이 원칙은 실험 결과의 신뢰도를 지키는 마지막 방어선이 됩니다. 그리고 이 방어선을 API 계약에 포함시킨 것이 이 논문의 가장 실용적인 기여 중 하나입니다.

지터복구: “엄격한 실패”를 선택했다면 안전한 사용법까지 제공해야 합니다

비평이 정확히 짚은 것처럼, CL API의 지터 처리 철학은 “조금 늦더라도 계속”이 아니라 **“데드라인을 넘기면 실패로 드러낸다”**입니다. 논문은 25kHz 루프에서 틱 간격이 40µs이며, 시스템 오버헤드 제외 사용자 루프 바디 예산이 약 33~34µs라고 명시합니다. 그리고 루프가 프레임을 따라잡지 못하면 **TimeoutError**를 발생시켜 “silent jitter accumulation”을 막습니다. 이 방식은 실험 오염을 최소화하지만, 실제 연구자가 겪는 현실(기록 I/O, 스파이크 폭주, 분석 루틴이 순간적으로 무거워지는 상황)을 생각하면 ‘실험이 자주 깨지는’ 경험으로 이어질 수도 있습니다.

논문이 좋은 점은, 이런 긴장을 인정하고 recover_from_jitter라는 명시적 메커니즘을 제공한다는 점입니다. Loop.recover_from_jitter(...)를 호출하면 루프가 “복구(recovery) 페이즈”로 들어가고, 표준 루프 바디를 잠시 우회한 채 논리 tick 인덱스를 빠르게 전진시켜 프레임 클록과 다시 동기화합니다. 또한 스킵되는 tick의 데이터를 선택적으로 콜백(handle_recovery_tick)으로 전달해 상태 일관성을 유지할 수 있게 합니다. 복구에는 제약도 분명히 붙습니다. 콜백이 무거우면 복구에 실패할 수 있고, timeout_seconds로 무한 복구를 막으며, retrospective data window가 약 5초라 그 안에서만 복구가 성공할 수 있다고 설명합니다. 즉, 복구조차도 “아무 때나 되는 마법”이 아니라 계약의 일부로 다뤄집니다.

하지만 비평대로 “제공”과 “안전한 사용법 제시”는 다릅니다. recover_from_jitter를 잘못 쓰면, 사용자가 의도치 않게 중요한 구간을 스킵하거나(분석이 필요한 tick이 누락), 상태 동기화를 놓치거나(자극/기록의 기준점이 흔들림), 결과적으로 재현성을 해칠 수 있습니다. 논문이 빠르게 완성도를 올리려면, 아래와 같은 안전 레시피를 ‘짧은 가이드’로 넣는 것이 효과적입니다(원칙은 이미 본문에 있으니, 사용 패턴만 정리하면 됩니다).

“끊고 새로 시작”이 더 안전한 경우를 명시하는 방식입니다.

루프 바디에서 외부 서비스 호출, 대용량 파일 처리, 모델 추론 같은 작업이 필요하다면, recover_from_jitter로 버티기보다 Neurons.loop를 종료하고 비실시간 작업을 수행한 뒤 새 루프를 시작하는 편이 안전하다는 가이드를 줄 수 있습니다. 논문도 “항상 loop를 종료하고 later starting a new one으로 구현할 수 있다”고 언급합니다.

recover_from_jitter가 적합한 ‘짧은 지연’의 정의를 제시하는 방식입니다.

콜백(handle_recovery_tick)은 “nominal tick interval에 가까워지면 실패”한다고 적혀 있으므로, 콜백에서는 절대 무거운 연산을 하지 말고 “스킵된 tick 수 카운트, 최소 상태 업데이트”만 하라는 규칙을 명시하는 것이 좋습니다. 이는 실무자가 가장 많이 실수하는 부분입니다.

“분석 구간 분리”를 계약 기반으로 안내하는 방식입니다.

recover_from_jitter로 스킵된 구간은 데이터 해석에서 별도 플래그로 분리해야 합니다. CL API가 auditability를 강조하므로, recording(HDF5)과 data stream에 “복구 발생 구간”을 이벤트로 남기게 하는 예시를 제공하면, 실험자가 사후 분석에서 안전하게 제외하거나 별도 처리할 수 있습니다. 논문은 data stream이 명시적 timestamp를 갖고, 기록에 함께 들어간다고 설명하므로 이 흐름과 잘 맞습니다.

두 번째로 비평에서 중요한 지적은 “성능 수치의 조건 제한”입니다. 논문은 Neurons.stim 호출이 3µs까지 가능하고, 채널이 idle이면 실제 자극 시작이 80–120µs 후에 발생한다고 말합니다. 또한 기본 lead time이 80µs이며, 더 긴 lead time을 요청할 수 있다고 설명합니다. 숫자 자체는 매력적이지만, 연구자는 “부하가 걸렸을 때 분포가 어떻게 되나”를 묻습니다. 다채널 동시 자극, 기록(HDF5) 동작, 스파이크 폭주, Python GIL/GC 영향, OS 상태(커널 스케줄링) 같은 스트레스 조건에서 p50/p95/p99/worst가 제시되면, ‘가능함’이 아니라 ‘믿고 쓸 수 있음’으로 넘어갑니다. 지금 논문은 feasibility를 보여주는데 성공했지만, 재현성 위기를 해결한다는 주장까지 밀어붙이려면 ‘증거 포맷’이 평균 중심에서 분포 중심으로 이동하는 편이 설득력 있습니다.

마지막으로 “시뮬레이터의 의미론 동일” 주장과 “용량 제약은 다를 수 있음”이라는 단서의 충돌 가능성은 실제 사용자 경험에서 큰 이슈가 됩니다. 논문은 simulation이 API 의미론에 대해 deterministic이며 authoritative라고 규정하면서도, 동시에 simulation은 hardware-specific capacity constraints를 모두 강제하지 않을 수 있어 “시뮬에서 승인된 트랜잭션이 하드웨어에서 거절될 수 있다”고 명시합니다. 이 문장은 정직하지만, 사용자는 “시뮬에서 돌아가면 장비에서도 돌아간다”는 기대를 하기 쉽습니다. 따라서 논문이 제안한 개선처럼, “시뮬에서 통과해도 하드웨어에서 실패할 수 있는 조건”을 체크리스트로 명시하면 메시지가 더 안전해집니다. 예를 들면 (1) per-channel queue가 꽉 찬 상태, (2) sync bookkeeping capacity 초과, (3) 동시 StimPlan 규모가 큰 경우, (4) Spike.samples lazy loading이 블로킹을 유발해 루프 예산을 넘기는 경우 같은 항목을 제시할 수 있습니다(Spike.samples는 최대 2ms까지 블로킹 가능하다고 설명합니다).

정리하면, CL API는 “지터를 숨기지 않는다”는 점에서 매우 성실한 인프라입니다. 다만 엄격한 실패를 선택한 만큼, 사용자가 실제로 덜 다치게(실험이 덜 깨지게) 만드는 운영 레시피와 분포 기반 측정이 따라오면, 이 논문의 ‘접근성+재현성’ 메시지가 훨씬 넓은 사용자층에서 힘을 얻을 것입니다.


CL API는 ‘실시간’을 시간적정확성 계약으로 정의하고, 원자트랜잭션과 지터복구로 실패를 투명하게 드러내 재현성을 끌어올립니다. 여기에 형식 명세, 분포 기반 성능, 안전 레시피가 보강되면 실배치 설득력이 크게 강화됩니다.

자주 묻는 질문 (FAQ)

Q. CL API가 말하는 “실시간”은 왜 평균 지연이 아니라 시간적정확성인가요? A. 평균 지연이 낮아도 타임스탬프 의미, 순서, 동기화가 흔들리면 입력의 시간 구조가 깨져 실험이 오염될 수 있습니다. CL API는 프레임(40µs) 예산, deterministic ordering, “요청보다 빨리 실행 금지” 같은 규칙을 API 계약으로 고정해 재현성을 확보합니다.

Q. TransactionRejected가 나면 실험이 완전히 망가지는 것 아닌가요?
A. 오히려 그 반대입니다. 부분 실행이 조용히 발생하면 기록은 남지만 의미가 깨질 수 있습니다. CL API는 트랜잭션을 전부 승인하거나 전부 거절하고, 거절 시 rollback과 예외로 실패를 드러내 “오염된 데이터가 정상처럼 남는 상황”을 막습니다.

Q. recover_from_jitter는 언제 쓰는 게 안전한가요?
A. 짧은 일시 지연을 “계획된 이벤트”로 처리하고, 스킵된 tick을 최소한의 상태 업데이트로만 정리할 때 유용합니다. 콜백이 무거우면 복구가 실패할 수 있으므로, 큰 연산이 필요하면 루프를 종료하고 비실시간 작업 후 새 루프를 시작하는 패턴이 더 안전합니다.

[출처]
https://arxiv.org/html/2602.11632v1

댓글 쓰기

0 댓글

이 블로그 검색

신고하기

프로필