평활화된 구간 전환

FxDart 승

요구사항

온도 센서가 하루에 원시 판독값 12개를 보고합니다. 이를 3-판독 이동 평균으로 평활화하고, 평활화된 값을 구간으로 분류한 뒤(cool < 20° ≤ ok < 25° ≤ hot), 모든 구간 전환을 보고하세요 — 어느 구간에서 나와 어느 구간으로 들어갔는지, 그리고 양쪽의 평활값을 함께 출력합니다. 전환이 없는 날은 아무것도 출력하지 않는 대신 stable 한 줄을 출력합니다. 7월 이틀치 데이터는 코드에 있으며, 두 버전 모두 예상 출력 아래에 표시된 줄들을 출력해야 합니다.

예상 출력
2026-07-14 (3-reading moving average):
  cool → ok (avg 18.6 → 21.2)
  ok → hot (avg 21.2 → 25.6)
  hot → ok (avg 25.6 → 24.9)
2026-07-15 (3-reading moving average):
  stable — no zone changes

나란히 보기

네이티브 Dart

FxDart

차이가 나는 이유

이 과제의 모든 단계는 이웃한 요소를 봐야 하는데, 바로 거기서 순수 Dart의 어휘가 바닥납니다: 슬라이딩 윈도우도, 인접 중복 제거도, 후속 값 짝짓기도 표준 라이브러리와 package:collection 어디에도 없습니다 (slices는 겹침 없이 타일링만 합니다). 그래서 네이티브 버전은 각자 가변 상태를 든 인덱스 루프 세 개가 됩니다: 윈도우 합계, 자기 꼬리와 비교하는 runStarts 리스트, 전환 줄을 위한 i - 1 되돌아보기 — 거기에 stable 날을 위한 마지막 isEmpty 땜질까지.

FxDart 체인은 다섯 단계를 데이터가 흐르는 순서대로 서술합니다: windowed(3) → 윈도우별 average, uniqAdjacentBy(zone)가 각 구간 런의 첫 평활값만 남기고, pairwise가 런 시작점들을 (from, to) 전환으로 바꾸고, ifEmpty가 stable 날의 줄을 파이프라인 바깥의 if 검사 대신 파이프라인 안에서 공급합니다. 각 조각은 독립적으로 테스트할 수 있고, 윈도우 경계를 다시 구현하는 코드는 어디에도 없습니다. 이 네 연산자는 Rx 윈도잉 계열의 풀(pull) 모델 이식입니다.

벤치마크

Apple M1 Max, RAM 32 GB · Dart 3.12.2 (AOT 컴파일) · 2026-08-24

N = 100

시간 무승부

네이티브 Dart 2.3 µs
FxDart 4.3 µs

최대 메모리 무승부

네이티브 Dart 16.4 MB
FxDart 16.3 MB

N = 10,000

시간 무승부

네이티브 Dart 194 µs
FxDart 410 µs

최대 메모리 무승부

네이티브 Dart 22.5 MB
FxDart 22.7 MB

N = 1,000,000

시간 FxDart 승

네이티브 Dart 39.4 ms
FxDart 37.4 ms

최대 메모리 FxDart 승

네이티브 Dart 237.3 MB
FxDart 81.8 MB

막대는 사이드별로 새 프로세스에서 반복 측정한 중앙값입니다(작은 N은 타이머 해상도를 위해 배치 처리). 두 사이드가 서로 5% 이내이거나 — 사람이 지각할 수 없는 차이인 0.6ms 이내이면 — 무승부로 칩니다. 상대 차이가 근소한 경우는 최대 5회까지 다시 측정합니다. 앱에서는 어느 막대가 짧든 몇 밀리초 이하의 차이는 사용자에게 보이지 않습니다. 메모리는 프로세스 최대 RSS입니다. Dart VM과 데이터셋은 양쪽이 동일하므로, 두 막대의 차이가 곧 파이프라인 자체가 붙들고 있는 양입니다.